From 7603ec8224e71f3d31e556740580cec813609558 Mon Sep 17 00:00:00 2001 From: "Documenter.jl" Date: Sun, 3 Mar 2024 11:57:14 +0000 Subject: [PATCH] build based on c8f7aaa --- stable | 2 +- v1 | 2 +- v1.19 | 2 +- v1.19.1/.documenter-siteinfo.json | 1 + v1.19.1/allindex/index.html | 2 + v1.19.1/assets/citations.css | 17 + v1.19.1/assets/documenter.js | 1050 +++++++++++++++++ v1.19.1/assets/themes/documenter-dark.css | 7 + v1.19.1/assets/themes/documenter-light.css | 9 + v1.19.1/assets/themeswap.js | 84 ++ v1.19.1/assets/warner.js | 52 + v1.19.1/changes/index.html | 11 + v1.19.1/citations.bib | 187 +++ v1.19.1/devel/index.html | 2 + v1.19.1/grid/index.html | 2 + v1.19.1/impedance-derivation.tex | 378 ++++++ v1.19.1/index.html | 2 + v1.19.1/internal/index.html | 48 + v1.19.1/method/index.html | 21 + v1.19.1/misc/index.html | 7 + v1.19.1/module_examples/Example001_Solvers.jl | 149 +++ .../Example001_Solvers/index.html | 145 +++ .../Example002_EdgeReaction.jl | 204 ++++ .../Example002_EdgeReaction/index.html | 172 +++ .../module_examples/Example101_Laplace1D.jl | 128 ++ .../Example101_Laplace1D/index.html | 59 + ...mple102_StationaryConvectionDiffusion1D.jl | 133 +++ .../index.html | 99 ++ .../Example103_ConvectionDiffusion1D.jl | 86 ++ .../index.html | 73 ++ .../Example105_NonlinearPoisson1D.jl | 98 ++ .../Example105_NonlinearPoisson1D/index.html | 80 ++ .../Example106_NonlinearDiffusion1D.jl | 110 ++ .../index.html | 92 ++ .../Example107_NonlinearStorage1D.jl | 108 ++ .../Example107_NonlinearStorage1D/index.html | 94 ++ .../Example108_OrdinaryDiffEq1D.jl | 105 ++ .../Example108_OrdinaryDiffEq1D/index.html | 88 ++ ...ample110_ReactionDiffusion1D_TwoSpecies.jl | 90 ++ .../index.html | 74 ++ .../Example115_HeterogeneousCatalysis1D.jl | 183 +++ .../index.html | 144 +++ .../Example120_ThreeRegions1D.jl | 148 +++ .../Example120_ThreeRegions1D/index.html | 146 +++ .../Example121_PoissonPointCharge1D.jl | 108 ++ .../index.html | 96 ++ .../Example125_TestFunctions1D.jl | 75 ++ .../Example125_TestFunctions1D/index.html | 68 ++ .../module_examples/Example150_Impedance1D.jl | 164 +++ .../Example150_Impedance1D/index.html | 90 ++ .../module_examples/Example151_Impedance1D.jl | 171 +++ .../Example151_Impedance1D/index.html | 95 ++ .../Example160_UnipolarDriftDiffusion1D.jl | 267 +++++ .../index.html | 244 ++++ .../module_examples/Example201_Laplace2D.jl | 49 + .../Example201_Laplace2D/index.html | 43 + .../Example203_CoordinateSystems.jl | 255 ++++ .../Example203_CoordinateSystems/index.html | 243 ++++ .../Example204_HagenPoiseuille.jl | 79 ++ .../Example204_HagenPoiseuille/index.html | 66 ++ .../Example205_StagnationPoint.jl | 109 ++ .../Example205_StagnationPoint/index.html | 89 ++ .../module_examples/Example206_JouleHeat.jl | 107 ++ .../Example206_JouleHeat/index.html | 97 ++ .../Example207_NonlinearPoisson2D.jl | 83 ++ .../Example207_NonlinearPoisson2D/index.html | 79 ++ .../Example210_NonlinearPoisson2D_Reaction.jl | 86 ++ .../index.html | 84 ++ ...215_NonlinearPoisson2D_BoundaryReaction.jl | 85 ++ .../index.html | 83 ++ ...e220_NonlinearPoisson2D_BoundarySpecies.jl | 93 ++ .../index.html | 91 ++ .../Example221_EquationBlockPrecon.jl | 131 ++ .../Example221_EquationBlockPrecon/index.html | 125 ++ .../Example225_TestFunctions2D.jl | 250 ++++ .../Example225_TestFunctions2D/index.html | 167 +++ .../Example226_BoundaryIntegral.jl | 80 ++ .../Example226_BoundaryIntegral/index.html | 74 ++ .../Example230_BoundaryFlux.jl | 187 +++ .../Example230_BoundaryFlux/index.html | 148 +++ .../module_examples/Example301_Laplace3D.jl | 48 + .../Example301_Laplace3D/index.html | 42 + ...ample311_HeatEquation_BoundaryDiffusion.jl | 114 ++ .../index.html | 105 ++ .../Example405_GenericOperator.jl | 81 ++ .../Example405_GenericOperator/index.html | 73 ++ .../Example406_WeirdReaction.jl | 197 ++++ .../Example406_WeirdReaction/index.html | 158 +++ .../module_examples/Example410_ManySpecies.jl | 45 + .../Example410_ManySpecies/index.html | 38 + .../Example420_DiscontinuousQuantities.jl | 135 +++ .../index.html | 119 ++ ...ple421_AbstractQuantities_TestFunctions.jl | 173 +++ .../index.html | 147 +++ .../Example422_InterfaceQuantities.jl | 247 ++++ .../Example422_InterfaceQuantities/index.html | 206 ++++ .../Example424_AbstractQuantitiesInit.jl | 78 ++ .../index.html | 73 ++ ...ample430_ParameterDerivativesStationary.jl | 237 ++++ .../index.html | 220 ++++ v1.19.1/module_examples/Example510_Mixture.jl | 246 ++++ .../Example510_Mixture/index.html | 196 +++ v1.19.1/notebooks/index.html | 2 + v1.19.1/objects.inv | Bin 0 -> 9188 bytes v1.19.1/physics/index.html | 20 + .../api-update/index.html | 454 +++++++ .../flux-reconstruction/index.html | 355 ++++++ .../interfaces1d/index.html | 487 ++++++++ .../nonlinear-solvers/index.html | 300 +++++ .../ode-brusselator/index.html | 270 +++++ .../ode-diffusion1d/index.html | 146 +++ .../ode-nlstorage1d/index.html | 295 +++++ .../ode-wave1d/index.html | 290 +++++ .../outflow/index.html | 278 +++++ .../problemcase/index.html | 436 +++++++ v1.19.1/post/index.html | 96 ++ v1.19.1/quantities/index.html | 4 + v1.19.1/runexamples/index.html | 18 + v1.19.1/search_index.js | 3 + v1.19.1/siteinfo.js | 1 + v1.19.1/solutions/index.html | 20 + v1.19.1/solver/index.html | 18 + v1.19.1/system/index.html | 38 + v1.19.1/trivoro.png | Bin 0 -> 114659 bytes v1.19.1/vor.png | Bin 0 -> 24601 bytes versions.js | 2 +- 126 files changed, 15562 insertions(+), 4 deletions(-) create mode 100644 v1.19.1/.documenter-siteinfo.json create mode 100644 v1.19.1/allindex/index.html create mode 100644 v1.19.1/assets/citations.css create mode 100644 v1.19.1/assets/documenter.js create mode 100644 v1.19.1/assets/themes/documenter-dark.css create mode 100644 v1.19.1/assets/themes/documenter-light.css create mode 100644 v1.19.1/assets/themeswap.js create mode 100644 v1.19.1/assets/warner.js create mode 100644 v1.19.1/changes/index.html create mode 100644 v1.19.1/citations.bib create mode 100644 v1.19.1/devel/index.html create mode 100644 v1.19.1/grid/index.html create mode 100644 v1.19.1/impedance-derivation.tex create mode 100644 v1.19.1/index.html create mode 100644 v1.19.1/internal/index.html create mode 100644 v1.19.1/method/index.html create mode 100644 v1.19.1/misc/index.html create mode 100644 v1.19.1/module_examples/Example001_Solvers.jl create mode 100644 v1.19.1/module_examples/Example001_Solvers/index.html create mode 100644 v1.19.1/module_examples/Example002_EdgeReaction.jl create mode 100644 v1.19.1/module_examples/Example002_EdgeReaction/index.html create mode 100644 v1.19.1/module_examples/Example101_Laplace1D.jl create mode 100644 v1.19.1/module_examples/Example101_Laplace1D/index.html create mode 100644 v1.19.1/module_examples/Example102_StationaryConvectionDiffusion1D.jl create mode 100644 v1.19.1/module_examples/Example102_StationaryConvectionDiffusion1D/index.html create mode 100644 v1.19.1/module_examples/Example103_ConvectionDiffusion1D.jl create mode 100644 v1.19.1/module_examples/Example103_ConvectionDiffusion1D/index.html create mode 100644 v1.19.1/module_examples/Example105_NonlinearPoisson1D.jl create mode 100644 v1.19.1/module_examples/Example105_NonlinearPoisson1D/index.html create mode 100644 v1.19.1/module_examples/Example106_NonlinearDiffusion1D.jl create mode 100644 v1.19.1/module_examples/Example106_NonlinearDiffusion1D/index.html create mode 100644 v1.19.1/module_examples/Example107_NonlinearStorage1D.jl create mode 100644 v1.19.1/module_examples/Example107_NonlinearStorage1D/index.html create mode 100644 v1.19.1/module_examples/Example108_OrdinaryDiffEq1D.jl create mode 100644 v1.19.1/module_examples/Example108_OrdinaryDiffEq1D/index.html create mode 100644 v1.19.1/module_examples/Example110_ReactionDiffusion1D_TwoSpecies.jl create mode 100644 v1.19.1/module_examples/Example110_ReactionDiffusion1D_TwoSpecies/index.html create mode 100644 v1.19.1/module_examples/Example115_HeterogeneousCatalysis1D.jl create mode 100644 v1.19.1/module_examples/Example115_HeterogeneousCatalysis1D/index.html create mode 100644 v1.19.1/module_examples/Example120_ThreeRegions1D.jl create mode 100644 v1.19.1/module_examples/Example120_ThreeRegions1D/index.html create mode 100644 v1.19.1/module_examples/Example121_PoissonPointCharge1D.jl create mode 100644 v1.19.1/module_examples/Example121_PoissonPointCharge1D/index.html create mode 100644 v1.19.1/module_examples/Example125_TestFunctions1D.jl create mode 100644 v1.19.1/module_examples/Example125_TestFunctions1D/index.html create mode 100644 v1.19.1/module_examples/Example150_Impedance1D.jl create mode 100644 v1.19.1/module_examples/Example150_Impedance1D/index.html create mode 100644 v1.19.1/module_examples/Example151_Impedance1D.jl create mode 100644 v1.19.1/module_examples/Example151_Impedance1D/index.html create mode 100644 v1.19.1/module_examples/Example160_UnipolarDriftDiffusion1D.jl create mode 100644 v1.19.1/module_examples/Example160_UnipolarDriftDiffusion1D/index.html create mode 100644 v1.19.1/module_examples/Example201_Laplace2D.jl create mode 100644 v1.19.1/module_examples/Example201_Laplace2D/index.html create mode 100644 v1.19.1/module_examples/Example203_CoordinateSystems.jl create mode 100644 v1.19.1/module_examples/Example203_CoordinateSystems/index.html create mode 100644 v1.19.1/module_examples/Example204_HagenPoiseuille.jl create mode 100644 v1.19.1/module_examples/Example204_HagenPoiseuille/index.html create mode 100644 v1.19.1/module_examples/Example205_StagnationPoint.jl create mode 100644 v1.19.1/module_examples/Example205_StagnationPoint/index.html create mode 100644 v1.19.1/module_examples/Example206_JouleHeat.jl create mode 100644 v1.19.1/module_examples/Example206_JouleHeat/index.html create mode 100644 v1.19.1/module_examples/Example207_NonlinearPoisson2D.jl create mode 100644 v1.19.1/module_examples/Example207_NonlinearPoisson2D/index.html create mode 100644 v1.19.1/module_examples/Example210_NonlinearPoisson2D_Reaction.jl create mode 100644 v1.19.1/module_examples/Example210_NonlinearPoisson2D_Reaction/index.html create mode 100644 v1.19.1/module_examples/Example215_NonlinearPoisson2D_BoundaryReaction.jl create mode 100644 v1.19.1/module_examples/Example215_NonlinearPoisson2D_BoundaryReaction/index.html create mode 100644 v1.19.1/module_examples/Example220_NonlinearPoisson2D_BoundarySpecies.jl create mode 100644 v1.19.1/module_examples/Example220_NonlinearPoisson2D_BoundarySpecies/index.html create mode 100644 v1.19.1/module_examples/Example221_EquationBlockPrecon.jl create mode 100644 v1.19.1/module_examples/Example221_EquationBlockPrecon/index.html create mode 100644 v1.19.1/module_examples/Example225_TestFunctions2D.jl create mode 100644 v1.19.1/module_examples/Example225_TestFunctions2D/index.html create mode 100644 v1.19.1/module_examples/Example226_BoundaryIntegral.jl create mode 100644 v1.19.1/module_examples/Example226_BoundaryIntegral/index.html create mode 100644 v1.19.1/module_examples/Example230_BoundaryFlux.jl create mode 100644 v1.19.1/module_examples/Example230_BoundaryFlux/index.html create mode 100644 v1.19.1/module_examples/Example301_Laplace3D.jl create mode 100644 v1.19.1/module_examples/Example301_Laplace3D/index.html create mode 100644 v1.19.1/module_examples/Example311_HeatEquation_BoundaryDiffusion.jl create mode 100644 v1.19.1/module_examples/Example311_HeatEquation_BoundaryDiffusion/index.html create mode 100644 v1.19.1/module_examples/Example405_GenericOperator.jl create mode 100644 v1.19.1/module_examples/Example405_GenericOperator/index.html create mode 100644 v1.19.1/module_examples/Example406_WeirdReaction.jl create mode 100644 v1.19.1/module_examples/Example406_WeirdReaction/index.html create mode 100644 v1.19.1/module_examples/Example410_ManySpecies.jl create mode 100644 v1.19.1/module_examples/Example410_ManySpecies/index.html create mode 100644 v1.19.1/module_examples/Example420_DiscontinuousQuantities.jl create mode 100644 v1.19.1/module_examples/Example420_DiscontinuousQuantities/index.html create mode 100644 v1.19.1/module_examples/Example421_AbstractQuantities_TestFunctions.jl create mode 100644 v1.19.1/module_examples/Example421_AbstractQuantities_TestFunctions/index.html create mode 100644 v1.19.1/module_examples/Example422_InterfaceQuantities.jl create mode 100644 v1.19.1/module_examples/Example422_InterfaceQuantities/index.html create mode 100644 v1.19.1/module_examples/Example424_AbstractQuantitiesInit.jl create mode 100644 v1.19.1/module_examples/Example424_AbstractQuantitiesInit/index.html create mode 100644 v1.19.1/module_examples/Example430_ParameterDerivativesStationary.jl create mode 100644 v1.19.1/module_examples/Example430_ParameterDerivativesStationary/index.html create mode 100644 v1.19.1/module_examples/Example510_Mixture.jl create mode 100644 v1.19.1/module_examples/Example510_Mixture/index.html create mode 100644 v1.19.1/notebooks/index.html create mode 100644 v1.19.1/objects.inv create mode 100644 v1.19.1/physics/index.html create mode 100644 v1.19.1/plutostatichtml_examples/api-update/index.html create mode 100644 v1.19.1/plutostatichtml_examples/flux-reconstruction/index.html create mode 100644 v1.19.1/plutostatichtml_examples/interfaces1d/index.html create mode 100644 v1.19.1/plutostatichtml_examples/nonlinear-solvers/index.html create mode 100644 v1.19.1/plutostatichtml_examples/ode-brusselator/index.html create mode 100644 v1.19.1/plutostatichtml_examples/ode-diffusion1d/index.html create mode 100644 v1.19.1/plutostatichtml_examples/ode-nlstorage1d/index.html create mode 100644 v1.19.1/plutostatichtml_examples/ode-wave1d/index.html create mode 100644 v1.19.1/plutostatichtml_examples/outflow/index.html create mode 100644 v1.19.1/plutostatichtml_examples/problemcase/index.html create mode 100644 v1.19.1/post/index.html create mode 100644 v1.19.1/quantities/index.html create mode 100644 v1.19.1/runexamples/index.html create mode 100644 v1.19.1/search_index.js create mode 100644 v1.19.1/siteinfo.js create mode 100644 v1.19.1/solutions/index.html create mode 100644 v1.19.1/solver/index.html create mode 100644 v1.19.1/system/index.html create mode 100644 v1.19.1/trivoro.png create mode 100644 v1.19.1/vor.png diff --git a/stable b/stable index 388ceb4fb..8e63bed04 120000 --- a/stable +++ b/stable @@ -1 +1 @@ -v1.19.0 \ No newline at end of file +v1.19.1 \ No newline at end of file diff --git a/v1 b/v1 index 388ceb4fb..8e63bed04 120000 --- a/v1 +++ b/v1 @@ -1 +1 @@ -v1.19.0 \ No newline at end of file +v1.19.1 \ No newline at end of file diff --git a/v1.19 b/v1.19 index 388ceb4fb..8e63bed04 120000 --- a/v1.19 +++ b/v1.19 @@ -1 +1 @@ -v1.19.0 \ No newline at end of file +v1.19.1 \ No newline at end of file diff --git a/v1.19.1/.documenter-siteinfo.json b/v1.19.1/.documenter-siteinfo.json new file mode 100644 index 000000000..4d9371876 --- /dev/null +++ b/v1.19.1/.documenter-siteinfo.json @@ -0,0 +1 @@ +{"documenter":{"julia_version":"1.10.2","generation_timestamp":"2024-03-03T11:56:46","documenter_version":"1.3.0"}} \ No newline at end of file diff --git a/v1.19.1/allindex/index.html b/v1.19.1/allindex/index.html new file mode 100644 index 000000000..89f401fa2 --- /dev/null +++ b/v1.19.1/allindex/index.html @@ -0,0 +1,2 @@ + +Index · VoronoiFVM.jl

Index

Exported

VoronoiFVMModule
source

Types and Constructors

Constants

    Methods

    diff --git a/v1.19.1/assets/citations.css b/v1.19.1/assets/citations.css new file mode 100644 index 000000000..76140e873 --- /dev/null +++ b/v1.19.1/assets/citations.css @@ -0,0 +1,17 @@ +.citation dl { + display: grid; + grid-template-columns: max-content auto; } +.citation dt { + grid-column-start: 1; } +.citation dd { + grid-column-start: 2; + margin-bottom: 0.75em; } +.citation ul { + padding: 0 0 2.25em 0; + margin: 0; + list-style: none;} +.citation ul li { + text-indent: -2.25em; + margin: 0.33em 0.5em 0.5em 2.25em;} +.citation ol li { + padding-left:0.75em;} diff --git a/v1.19.1/assets/documenter.js b/v1.19.1/assets/documenter.js new file mode 100644 index 000000000..8a829d865 --- /dev/null +++ b/v1.19.1/assets/documenter.js @@ -0,0 +1,1050 @@ +// Generated by Documenter.jl +requirejs.config({ + paths: { + 'highlight-julia': 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/julia.min', + 'headroom': 'https://cdnjs.cloudflare.com/ajax/libs/headroom/0.12.0/headroom.min', + 'jqueryui': 'https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.13.2/jquery-ui.min', + 'jquery': 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.0/jquery.min', + 'headroom-jquery': 'https://cdnjs.cloudflare.com/ajax/libs/headroom/0.12.0/jQuery.headroom.min', + 'highlight': 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min', + 'highlight-julia-repl': 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/julia-repl.min', + }, + shim: { + "highlight-julia": { + "deps": [ + "highlight" + ] + }, + "headroom-jquery": { + "deps": [ + "jquery", + "headroom" + ] + }, + "highlight-julia-repl": { + "deps": [ + "highlight" + ] + } +} +}); +//////////////////////////////////////////////////////////////////////////////// +require([], function() { +window.MathJax = { + "tex": { + "packages": [ + "base", + "ams", + "autoload" + ], + "inlineMath": [ + [ + "$", + "$" + ], + [ + "\\(", + "\\)" + ] + ], + "tags": "ams" + }, + "options": { + "ignoreHtmlClass": "tex2jax_ignore", + "processHtmlClass": "tex2jax_process" + } +} +; + +(function () { + var script = document.createElement('script'); + script.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-svg.js'; + script.async = true; + document.head.appendChild(script); +})(); + +}) +//////////////////////////////////////////////////////////////////////////////// +require(['jquery', 'highlight', 'highlight-julia', 'highlight-julia-repl'], function($) { +$(document).ready(function() { + hljs.highlightAll(); +}) + +}) +//////////////////////////////////////////////////////////////////////////////// +require(['jquery'], function($) { + +let timer = 0; +var isExpanded = true; + +$(document).on("click", ".docstring header", function () { + let articleToggleTitle = "Expand docstring"; + + debounce(() => { + if ($(this).siblings("section").is(":visible")) { + $(this) + .find(".docstring-article-toggle-button") + .removeClass("fa-chevron-down") + .addClass("fa-chevron-right"); + } else { + $(this) + .find(".docstring-article-toggle-button") + .removeClass("fa-chevron-right") + .addClass("fa-chevron-down"); + + articleToggleTitle = "Collapse docstring"; + } + + $(this) + .find(".docstring-article-toggle-button") + .prop("title", articleToggleTitle); + $(this).siblings("section").slideToggle(); + }); +}); + +$(document).on("click", ".docs-article-toggle-button", function (event) { + let articleToggleTitle = "Expand docstring"; + let navArticleToggleTitle = "Expand all docstrings"; + let animationSpeed = event.noToggleAnimation ? 0 : 400; + + debounce(() => { + if (isExpanded) { + $(this).removeClass("fa-chevron-up").addClass("fa-chevron-down"); + $(".docstring-article-toggle-button") + .removeClass("fa-chevron-down") + .addClass("fa-chevron-right"); + + isExpanded = false; + + $(".docstring section").slideUp(animationSpeed); + } else { + $(this).removeClass("fa-chevron-down").addClass("fa-chevron-up"); + $(".docstring-article-toggle-button") + .removeClass("fa-chevron-right") + .addClass("fa-chevron-down"); + + isExpanded = true; + articleToggleTitle = "Collapse docstring"; + navArticleToggleTitle = "Collapse all docstrings"; + + $(".docstring section").slideDown(animationSpeed); + } + + $(this).prop("title", navArticleToggleTitle); + $(".docstring-article-toggle-button").prop("title", articleToggleTitle); + }); +}); + +function debounce(callback, timeout = 300) { + if (Date.now() - timer > timeout) { + callback(); + } + + clearTimeout(timer); + + timer = Date.now(); +} + +}) +//////////////////////////////////////////////////////////////////////////////// +require([], function() { +function addCopyButtonCallbacks() { + for (const el of document.getElementsByTagName("pre")) { + const button = document.createElement("button"); + button.classList.add("copy-button", "fa-solid", "fa-copy"); + button.setAttribute("aria-label", "Copy this code block"); + button.setAttribute("title", "Copy"); + + el.appendChild(button); + + const success = function () { + button.classList.add("success", "fa-check"); + button.classList.remove("fa-copy"); + }; + + const failure = function () { + button.classList.add("error", "fa-xmark"); + button.classList.remove("fa-copy"); + }; + + button.addEventListener("click", function () { + copyToClipboard(el.innerText).then(success, failure); + + setTimeout(function () { + button.classList.add("fa-copy"); + button.classList.remove("success", "fa-check", "fa-xmark"); + }, 5000); + }); + } +} + +function copyToClipboard(text) { + // clipboard API is only available in secure contexts + if (window.navigator && window.navigator.clipboard) { + return window.navigator.clipboard.writeText(text); + } else { + return new Promise(function (resolve, reject) { + try { + const el = document.createElement("textarea"); + el.textContent = text; + el.style.position = "fixed"; + el.style.opacity = 0; + document.body.appendChild(el); + el.select(); + document.execCommand("copy"); + + resolve(); + } catch (err) { + reject(err); + } finally { + document.body.removeChild(el); + } + }); + } +} + +if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", addCopyButtonCallbacks); +} else { + addCopyButtonCallbacks(); +} + +}) +//////////////////////////////////////////////////////////////////////////////// +require(['jquery', 'headroom', 'headroom-jquery'], function($, Headroom) { + +// Manages the top navigation bar (hides it when the user starts scrolling down on the +// mobile). +window.Headroom = Headroom; // work around buggy module loading? +$(document).ready(function () { + $("#documenter .docs-navbar").headroom({ + tolerance: { up: 10, down: 10 }, + }); +}); + +}) +//////////////////////////////////////////////////////////////////////////////// +require(['jquery'], function($) { + +$(document).ready(function () { + let meta = $("div[data-docstringscollapsed]").data(); + + if (meta?.docstringscollapsed) { + $("#documenter-article-toggle-button").trigger({ + type: "click", + noToggleAnimation: true, + }); + } +}); + +}) +//////////////////////////////////////////////////////////////////////////////// +require(['jquery'], function($) { + +/* +To get an in-depth about the thought process you can refer: https://hetarth02.hashnode.dev/series/gsoc + +PSEUDOCODE: + +Searching happens automatically as the user types or adjusts the selected filters. +To preserve responsiveness, as much as possible of the slow parts of the search are done +in a web worker. Searching and result generation are done in the worker, and filtering and +DOM updates are done in the main thread. The filters are in the main thread as they should +be very quick to apply. This lets filters be changed without re-searching with minisearch +(which is possible even if filtering is on the worker thread) and also lets filters be +changed _while_ the worker is searching and without message passing (neither of which are +possible if filtering is on the worker thread) + +SEARCH WORKER: + +Import minisearch + +Build index + +On message from main thread + run search + find the first 200 unique results from each category, and compute their divs for display + note that this is necessary and sufficient information for the main thread to find the + first 200 unique results from any given filter set + post results to main thread + +MAIN: + +Launch worker + +Declare nonconstant globals (worker_is_running, last_search_text, unfiltered_results) + +On text update + if worker is not running, launch_search() + +launch_search + set worker_is_running to true, set last_search_text to the search text + post the search query to worker + +on message from worker + if last_search_text is not the same as the text in the search field, + the latest search result is not reflective of the latest search query, so update again + launch_search() + otherwise + set worker_is_running to false + + regardless, display the new search results to the user + save the unfiltered_results as a global + update_search() + +on filter click + adjust the filter selection + update_search() + +update_search + apply search filters by looping through the unfiltered_results and finding the first 200 + unique results that match the filters + + Update the DOM +*/ + +/////// SEARCH WORKER /////// + +function worker_function(documenterSearchIndex, documenterBaseURL, filters) { + importScripts( + "https://cdn.jsdelivr.net/npm/minisearch@6.1.0/dist/umd/index.min.js" + ); + + let data = documenterSearchIndex.map((x, key) => { + x["id"] = key; // minisearch requires a unique for each object + return x; + }); + + // list below is the lunr 2.1.3 list minus the intersect with names(Base) + // (all, any, get, in, is, only, which) and (do, else, for, let, where, while, with) + // ideally we'd just filter the original list but it's not available as a variable + const stopWords = new Set([ + "a", + "able", + "about", + "across", + "after", + "almost", + "also", + "am", + "among", + "an", + "and", + "are", + "as", + "at", + "be", + "because", + "been", + "but", + "by", + "can", + "cannot", + "could", + "dear", + "did", + "does", + "either", + "ever", + "every", + "from", + "got", + "had", + "has", + "have", + "he", + "her", + "hers", + "him", + "his", + "how", + "however", + "i", + "if", + "into", + "it", + "its", + "just", + "least", + "like", + "likely", + "may", + "me", + "might", + "most", + "must", + "my", + "neither", + "no", + "nor", + "not", + "of", + "off", + "often", + "on", + "or", + "other", + "our", + "own", + "rather", + "said", + "say", + "says", + "she", + "should", + "since", + "so", + "some", + "than", + "that", + "the", + "their", + "them", + "then", + "there", + "these", + "they", + "this", + "tis", + "to", + "too", + "twas", + "us", + "wants", + "was", + "we", + "were", + "what", + "when", + "who", + "whom", + "why", + "will", + "would", + "yet", + "you", + "your", + ]); + + let index = new MiniSearch({ + fields: ["title", "text"], // fields to index for full-text search + storeFields: ["location", "title", "text", "category", "page"], // fields to return with results + processTerm: (term) => { + let word = stopWords.has(term) ? null : term; + if (word) { + // custom trimmer that doesn't strip @ and !, which are used in julia macro and function names + word = word + .replace(/^[^a-zA-Z0-9@!]+/, "") + .replace(/[^a-zA-Z0-9@!]+$/, ""); + + word = word.toLowerCase(); + } + + return word ?? null; + }, + // add . as a separator, because otherwise "title": "Documenter.Anchors.add!", would not + // find anything if searching for "add!", only for the entire qualification + tokenize: (string) => string.split(/[\s\-\.]+/), + // options which will be applied during the search + searchOptions: { + prefix: true, + boost: { title: 100 }, + fuzzy: 2, + }, + }); + + index.addAll(data); + + /** + * Used to map characters to HTML entities. + * Refer: https://github.com/lodash/lodash/blob/main/src/escape.ts + */ + const htmlEscapes = { + "&": "&", + "<": "<", + ">": ">", + '"': """, + "'": "'", + }; + + /** + * Used to match HTML entities and HTML characters. + * Refer: https://github.com/lodash/lodash/blob/main/src/escape.ts + */ + const reUnescapedHtml = /[&<>"']/g; + const reHasUnescapedHtml = RegExp(reUnescapedHtml.source); + + /** + * Escape function from lodash + * Refer: https://github.com/lodash/lodash/blob/main/src/escape.ts + */ + function escape(string) { + return string && reHasUnescapedHtml.test(string) + ? string.replace(reUnescapedHtml, (chr) => htmlEscapes[chr]) + : string || ""; + } + + /** + * Make the result component given a minisearch result data object and the value + * of the search input as queryString. To view the result object structure, refer: + * https://lucaong.github.io/minisearch/modules/_minisearch_.html#searchresult + * + * @param {object} result + * @param {string} querystring + * @returns string + */ + function make_search_result(result, querystring) { + let search_divider = `
    `; + let display_link = + result.location.slice(Math.max(0), Math.min(50, result.location.length)) + + (result.location.length > 30 ? "..." : ""); // To cut-off the link because it messes with the overflow of the whole div + + if (result.page !== "") { + display_link += ` (${result.page})`; + } + + let textindex = new RegExp(`${querystring}`, "i").exec(result.text); + let text = + textindex !== null + ? result.text.slice( + Math.max(textindex.index - 100, 0), + Math.min( + textindex.index + querystring.length + 100, + result.text.length + ) + ) + : ""; // cut-off text before and after from the match + + text = text.length ? escape(text) : ""; + + let display_result = text.length + ? "..." + + text.replace( + new RegExp(`${escape(querystring)}`, "i"), // For first occurrence + '$&' + ) + + "..." + : ""; // highlights the match + + let in_code = false; + if (!["page", "section"].includes(result.category.toLowerCase())) { + in_code = true; + } + + // We encode the full url to escape some special characters which can lead to broken links + let result_div = ` + +
    +
    ${escape(result.title)}
    +
    ${result.category}
    +
    +

    + ${display_result} +

    +
    + ${display_link} +
    +
    + ${search_divider} + `; + + return result_div; + } + + self.onmessage = function (e) { + let query = e.data; + let results = index.search(query, { + filter: (result) => { + // Only return relevant results + return result.score >= 1; + }, + }); + + // Pre-filter to deduplicate and limit to 200 per category to the extent + // possible without knowing what the filters are. + let filtered_results = []; + let counts = {}; + for (let filter of filters) { + counts[filter] = 0; + } + let present = {}; + + for (let result of results) { + cat = result.category; + cnt = counts[cat]; + if (cnt < 200) { + id = cat + "---" + result.location; + if (present[id]) { + continue; + } + present[id] = true; + filtered_results.push({ + location: result.location, + category: cat, + div: make_search_result(result, query), + }); + } + } + + postMessage(filtered_results); + }; +} + +// `worker = Threads.@spawn worker_function(documenterSearchIndex)`, but in JavaScript! +const filters = [ + ...new Set(documenterSearchIndex["docs"].map((x) => x.category)), +]; +const worker_str = + "(" + + worker_function.toString() + + ")(" + + JSON.stringify(documenterSearchIndex["docs"]) + + "," + + JSON.stringify(documenterBaseURL) + + "," + + JSON.stringify(filters) + + ")"; +const worker_blob = new Blob([worker_str], { type: "text/javascript" }); +const worker = new Worker(URL.createObjectURL(worker_blob)); + +/////// SEARCH MAIN /////// + +// Whether the worker is currently handling a search. This is a boolean +// as the worker only ever handles 1 or 0 searches at a time. +var worker_is_running = false; + +// The last search text that was sent to the worker. This is used to determine +// if the worker should be launched again when it reports back results. +var last_search_text = ""; + +// The results of the last search. This, in combination with the state of the filters +// in the DOM, is used compute the results to display on calls to update_search. +var unfiltered_results = []; + +// Which filter is currently selected +var selected_filter = ""; + +$(document).on("input", ".documenter-search-input", function (event) { + if (!worker_is_running) { + launch_search(); + } +}); + +function launch_search() { + worker_is_running = true; + last_search_text = $(".documenter-search-input").val(); + worker.postMessage(last_search_text); +} + +worker.onmessage = function (e) { + if (last_search_text !== $(".documenter-search-input").val()) { + launch_search(); + } else { + worker_is_running = false; + } + + unfiltered_results = e.data; + update_search(); +}; + +$(document).on("click", ".search-filter", function () { + if ($(this).hasClass("search-filter-selected")) { + selected_filter = ""; + } else { + selected_filter = $(this).text().toLowerCase(); + } + + // This updates search results and toggles classes for UI: + update_search(); +}); + +/** + * Make/Update the search component + */ +function update_search() { + let querystring = $(".documenter-search-input").val(); + + if (querystring.trim()) { + if (selected_filter == "") { + results = unfiltered_results; + } else { + results = unfiltered_results.filter((result) => { + return selected_filter == result.category.toLowerCase(); + }); + } + + let search_result_container = ``; + let modal_filters = make_modal_body_filters(); + let search_divider = `
    `; + + if (results.length) { + let links = []; + let count = 0; + let search_results = ""; + + for (var i = 0, n = results.length; i < n && count < 200; ++i) { + let result = results[i]; + if (result.location && !links.includes(result.location)) { + search_results += result.div; + count++; + links.push(result.location); + } + } + + if (count == 1) { + count_str = "1 result"; + } else if (count == 200) { + count_str = "200+ results"; + } else { + count_str = count + " results"; + } + let result_count = `
    ${count_str}
    `; + + search_result_container = ` +
    + ${modal_filters} + ${search_divider} + ${result_count} +
    + ${search_results} +
    +
    + `; + } else { + search_result_container = ` +
    + ${modal_filters} + ${search_divider} +
    0 result(s)
    +
    +
    No result found!
    + `; + } + + if ($(".search-modal-card-body").hasClass("is-justify-content-center")) { + $(".search-modal-card-body").removeClass("is-justify-content-center"); + } + + $(".search-modal-card-body").html(search_result_container); + } else { + if (!$(".search-modal-card-body").hasClass("is-justify-content-center")) { + $(".search-modal-card-body").addClass("is-justify-content-center"); + } + + $(".search-modal-card-body").html(` +
    Type something to get started!
    + `); + } +} + +/** + * Make the modal filter html + * + * @returns string + */ +function make_modal_body_filters() { + let str = filters + .map((val) => { + if (selected_filter == val.toLowerCase()) { + return `${val}`; + } else { + return `${val}`; + } + }) + .join(""); + + return ` +
    + Filters: + ${str} +
    `; +} + +}) +//////////////////////////////////////////////////////////////////////////////// +require(['jquery'], function($) { + +// Modal settings dialog +$(document).ready(function () { + var settings = $("#documenter-settings"); + $("#documenter-settings-button").click(function () { + settings.toggleClass("is-active"); + }); + // Close the dialog if X is clicked + $("#documenter-settings button.delete").click(function () { + settings.removeClass("is-active"); + }); + // Close dialog if ESC is pressed + $(document).keyup(function (e) { + if (e.keyCode == 27) settings.removeClass("is-active"); + }); +}); + +}) +//////////////////////////////////////////////////////////////////////////////// +require(['jquery'], function($) { + +$(document).ready(function () { + let search_modal_header = ` + + `; + + let initial_search_body = ` +
    Type something to get started!
    + `; + + let search_modal_footer = ` + + `; + + $(document.body).append( + ` + + ` + ); + + document.querySelector(".docs-search-query").addEventListener("click", () => { + openModal(); + }); + + document + .querySelector(".close-search-modal") + .addEventListener("click", () => { + closeModal(); + }); + + $(document).on("click", ".search-result-link", function () { + closeModal(); + }); + + document.addEventListener("keydown", (event) => { + if ((event.ctrlKey || event.metaKey) && event.key === "/") { + openModal(); + } else if (event.key === "Escape") { + closeModal(); + } + + return false; + }); + + // Functions to open and close a modal + function openModal() { + let searchModal = document.querySelector("#search-modal"); + + searchModal.classList.add("is-active"); + document.querySelector(".documenter-search-input").focus(); + } + + function closeModal() { + let searchModal = document.querySelector("#search-modal"); + let initial_search_body = ` +
    Type something to get started!
    + `; + + searchModal.classList.remove("is-active"); + document.querySelector(".documenter-search-input").blur(); + + if (!$(".search-modal-card-body").hasClass("is-justify-content-center")) { + $(".search-modal-card-body").addClass("is-justify-content-center"); + } + + $(".documenter-search-input").val(""); + $(".search-modal-card-body").html(initial_search_body); + } + + document + .querySelector("#search-modal .modal-background") + .addEventListener("click", () => { + closeModal(); + }); +}); + +}) +//////////////////////////////////////////////////////////////////////////////// +require(['jquery'], function($) { + +// Manages the showing and hiding of the sidebar. +$(document).ready(function () { + var sidebar = $("#documenter > .docs-sidebar"); + var sidebar_button = $("#documenter-sidebar-button"); + sidebar_button.click(function (ev) { + ev.preventDefault(); + sidebar.toggleClass("visible"); + if (sidebar.hasClass("visible")) { + // Makes sure that the current menu item is visible in the sidebar. + $("#documenter .docs-menu a.is-active").focus(); + } + }); + $("#documenter > .docs-main").bind("click", function (ev) { + if ($(ev.target).is(sidebar_button)) { + return; + } + if (sidebar.hasClass("visible")) { + sidebar.removeClass("visible"); + } + }); +}); + +// Resizes the package name / sitename in the sidebar if it is too wide. +// Inspired by: https://github.com/davatron5000/FitText.js +$(document).ready(function () { + e = $("#documenter .docs-autofit"); + function resize() { + var L = parseInt(e.css("max-width"), 10); + var L0 = e.width(); + if (L0 > L) { + var h0 = parseInt(e.css("font-size"), 10); + e.css("font-size", (L * h0) / L0); + // TODO: make sure it survives resizes? + } + } + // call once and then register events + resize(); + $(window).resize(resize); + $(window).on("orientationchange", resize); +}); + +// Scroll the navigation bar to the currently selected menu item +$(document).ready(function () { + var sidebar = $("#documenter .docs-menu").get(0); + var active = $("#documenter .docs-menu .is-active").get(0); + if (typeof active !== "undefined") { + sidebar.scrollTop = active.offsetTop - sidebar.offsetTop - 15; + } +}); + +}) +//////////////////////////////////////////////////////////////////////////////// +require(['jquery'], function($) { + +// Theme picker setup +$(document).ready(function () { + // onchange callback + $("#documenter-themepicker").change(function themepick_callback(ev) { + var themename = $("#documenter-themepicker option:selected").attr("value"); + if (themename === "auto") { + // set_theme(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'); + window.localStorage.removeItem("documenter-theme"); + } else { + // set_theme(themename); + window.localStorage.setItem("documenter-theme", themename); + } + // We re-use the global function from themeswap.js to actually do the swapping. + set_theme_from_local_storage(); + }); + + // Make sure that the themepicker displays the correct theme when the theme is retrieved + // from localStorage + if (typeof window.localStorage !== "undefined") { + var theme = window.localStorage.getItem("documenter-theme"); + if (theme !== null) { + $("#documenter-themepicker option").each(function (i, e) { + e.selected = e.value === theme; + }); + } + } +}); + +}) +//////////////////////////////////////////////////////////////////////////////// +require(['jquery'], function($) { + +// update the version selector with info from the siteinfo.js and ../versions.js files +$(document).ready(function () { + // If the version selector is disabled with DOCUMENTER_VERSION_SELECTOR_DISABLED in the + // siteinfo.js file, we just return immediately and not display the version selector. + if ( + typeof DOCUMENTER_VERSION_SELECTOR_DISABLED === "boolean" && + DOCUMENTER_VERSION_SELECTOR_DISABLED + ) { + return; + } + + var version_selector = $("#documenter .docs-version-selector"); + var version_selector_select = $("#documenter .docs-version-selector select"); + + version_selector_select.change(function (x) { + target_href = version_selector_select + .children("option:selected") + .get(0).value; + window.location.href = target_href; + }); + + // add the current version to the selector based on siteinfo.js, but only if the selector is empty + if ( + typeof DOCUMENTER_CURRENT_VERSION !== "undefined" && + $("#version-selector > option").length == 0 + ) { + var option = $( + "" + ); + version_selector_select.append(option); + } + + if (typeof DOC_VERSIONS !== "undefined") { + var existing_versions = version_selector_select.children("option"); + var existing_versions_texts = existing_versions.map(function (i, x) { + return x.text; + }); + DOC_VERSIONS.forEach(function (each) { + var version_url = documenterBaseURL + "/../" + each + "/"; + var existing_id = $.inArray(each, existing_versions_texts); + // if not already in the version selector, add it as a new option, + // otherwise update the old option with the URL and enable it + if (existing_id == -1) { + var option = $( + "" + ); + version_selector_select.append(option); + } else { + var option = existing_versions[existing_id]; + option.value = version_url; + option.disabled = false; + } + }); + } + + // only show the version selector if the selector has been populated + if (version_selector_select.children("option").length > 0) { + version_selector.toggleClass("visible"); + } +}); + +}) diff --git a/v1.19.1/assets/themes/documenter-dark.css b/v1.19.1/assets/themes/documenter-dark.css new file mode 100644 index 000000000..53889fb99 --- /dev/null +++ b/v1.19.1/assets/themes/documenter-dark.css @@ -0,0 +1,7 @@ +html.theme--documenter-dark .pagination-previous,html.theme--documenter-dark .pagination-next,html.theme--documenter-dark .pagination-link,html.theme--documenter-dark .pagination-ellipsis,html.theme--documenter-dark .file-cta,html.theme--documenter-dark .file-name,html.theme--documenter-dark .select select,html.theme--documenter-dark .textarea,html.theme--documenter-dark .input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input,html.theme--documenter-dark .button{-moz-appearance:none;-webkit-appearance:none;align-items:center;border:1px solid transparent;border-radius:.4em;box-shadow:none;display:inline-flex;font-size:1rem;height:2.5em;justify-content:flex-start;line-height:1.5;padding-bottom:calc(0.5em - 1px);padding-left:calc(0.75em - 1px);padding-right:calc(0.75em - 1px);padding-top:calc(0.5em - 1px);position:relative;vertical-align:top}html.theme--documenter-dark .pagination-previous:focus,html.theme--documenter-dark .pagination-next:focus,html.theme--documenter-dark .pagination-link:focus,html.theme--documenter-dark .pagination-ellipsis:focus,html.theme--documenter-dark .file-cta:focus,html.theme--documenter-dark .file-name:focus,html.theme--documenter-dark .select select:focus,html.theme--documenter-dark .textarea:focus,html.theme--documenter-dark .input:focus,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input:focus,html.theme--documenter-dark .button:focus,html.theme--documenter-dark .is-focused.pagination-previous,html.theme--documenter-dark .is-focused.pagination-next,html.theme--documenter-dark .is-focused.pagination-link,html.theme--documenter-dark .is-focused.pagination-ellipsis,html.theme--documenter-dark .is-focused.file-cta,html.theme--documenter-dark .is-focused.file-name,html.theme--documenter-dark .select select.is-focused,html.theme--documenter-dark .is-focused.textarea,html.theme--documenter-dark .is-focused.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-focused,html.theme--documenter-dark .is-focused.button,html.theme--documenter-dark .pagination-previous:active,html.theme--documenter-dark .pagination-next:active,html.theme--documenter-dark .pagination-link:active,html.theme--documenter-dark .pagination-ellipsis:active,html.theme--documenter-dark .file-cta:active,html.theme--documenter-dark .file-name:active,html.theme--documenter-dark .select select:active,html.theme--documenter-dark .textarea:active,html.theme--documenter-dark .input:active,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input:active,html.theme--documenter-dark .button:active,html.theme--documenter-dark .is-active.pagination-previous,html.theme--documenter-dark .is-active.pagination-next,html.theme--documenter-dark .is-active.pagination-link,html.theme--documenter-dark .is-active.pagination-ellipsis,html.theme--documenter-dark .is-active.file-cta,html.theme--documenter-dark .is-active.file-name,html.theme--documenter-dark .select select.is-active,html.theme--documenter-dark .is-active.textarea,html.theme--documenter-dark .is-active.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-active,html.theme--documenter-dark .is-active.button{outline:none}html.theme--documenter-dark .pagination-previous[disabled],html.theme--documenter-dark .pagination-next[disabled],html.theme--documenter-dark .pagination-link[disabled],html.theme--documenter-dark .pagination-ellipsis[disabled],html.theme--documenter-dark .file-cta[disabled],html.theme--documenter-dark .file-name[disabled],html.theme--documenter-dark .select select[disabled],html.theme--documenter-dark .textarea[disabled],html.theme--documenter-dark .input[disabled],html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input[disabled],html.theme--documenter-dark .button[disabled],fieldset[disabled] html.theme--documenter-dark .pagination-previous,html.theme--documenter-dark fieldset[disabled] .pagination-previous,fieldset[disabled] html.theme--documenter-dark .pagination-next,html.theme--documenter-dark fieldset[disabled] .pagination-next,fieldset[disabled] html.theme--documenter-dark .pagination-link,html.theme--documenter-dark fieldset[disabled] .pagination-link,fieldset[disabled] html.theme--documenter-dark .pagination-ellipsis,html.theme--documenter-dark fieldset[disabled] .pagination-ellipsis,fieldset[disabled] html.theme--documenter-dark .file-cta,html.theme--documenter-dark fieldset[disabled] .file-cta,fieldset[disabled] html.theme--documenter-dark .file-name,html.theme--documenter-dark fieldset[disabled] .file-name,fieldset[disabled] html.theme--documenter-dark .select select,fieldset[disabled] html.theme--documenter-dark .textarea,fieldset[disabled] html.theme--documenter-dark .input,fieldset[disabled] html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input,html.theme--documenter-dark fieldset[disabled] .select select,html.theme--documenter-dark .select fieldset[disabled] select,html.theme--documenter-dark fieldset[disabled] .textarea,html.theme--documenter-dark fieldset[disabled] .input,html.theme--documenter-dark fieldset[disabled] #documenter .docs-sidebar form.docs-search>input,html.theme--documenter-dark #documenter .docs-sidebar fieldset[disabled] form.docs-search>input,fieldset[disabled] html.theme--documenter-dark .button,html.theme--documenter-dark fieldset[disabled] .button{cursor:not-allowed}html.theme--documenter-dark .tabs,html.theme--documenter-dark .pagination-previous,html.theme--documenter-dark .pagination-next,html.theme--documenter-dark .pagination-link,html.theme--documenter-dark .pagination-ellipsis,html.theme--documenter-dark .breadcrumb,html.theme--documenter-dark .file,html.theme--documenter-dark .button,.is-unselectable{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}html.theme--documenter-dark .navbar-link:not(.is-arrowless)::after,html.theme--documenter-dark .select:not(.is-multiple):not(.is-loading)::after{border:3px solid rgba(0,0,0,0);border-radius:2px;border-right:0;border-top:0;content:" ";display:block;height:0.625em;margin-top:-0.4375em;pointer-events:none;position:absolute;top:50%;transform:rotate(-45deg);transform-origin:center;width:0.625em}html.theme--documenter-dark .admonition:not(:last-child),html.theme--documenter-dark .tabs:not(:last-child),html.theme--documenter-dark .pagination:not(:last-child),html.theme--documenter-dark .message:not(:last-child),html.theme--documenter-dark .level:not(:last-child),html.theme--documenter-dark .breadcrumb:not(:last-child),html.theme--documenter-dark .block:not(:last-child),html.theme--documenter-dark .title:not(:last-child),html.theme--documenter-dark .subtitle:not(:last-child),html.theme--documenter-dark .table-container:not(:last-child),html.theme--documenter-dark .table:not(:last-child),html.theme--documenter-dark .progress:not(:last-child),html.theme--documenter-dark .notification:not(:last-child),html.theme--documenter-dark .content:not(:last-child),html.theme--documenter-dark .box:not(:last-child){margin-bottom:1.5rem}html.theme--documenter-dark .modal-close,html.theme--documenter-dark .delete{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-moz-appearance:none;-webkit-appearance:none;background-color:rgba(10,10,10,0.2);border:none;border-radius:9999px;cursor:pointer;pointer-events:auto;display:inline-block;flex-grow:0;flex-shrink:0;font-size:0;height:20px;max-height:20px;max-width:20px;min-height:20px;min-width:20px;outline:none;position:relative;vertical-align:top;width:20px}html.theme--documenter-dark .modal-close::before,html.theme--documenter-dark .delete::before,html.theme--documenter-dark .modal-close::after,html.theme--documenter-dark .delete::after{background-color:#fff;content:"";display:block;left:50%;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%) rotate(45deg);transform-origin:center center}html.theme--documenter-dark .modal-close::before,html.theme--documenter-dark .delete::before{height:2px;width:50%}html.theme--documenter-dark .modal-close::after,html.theme--documenter-dark .delete::after{height:50%;width:2px}html.theme--documenter-dark .modal-close:hover,html.theme--documenter-dark .delete:hover,html.theme--documenter-dark .modal-close:focus,html.theme--documenter-dark .delete:focus{background-color:rgba(10,10,10,0.3)}html.theme--documenter-dark .modal-close:active,html.theme--documenter-dark .delete:active{background-color:rgba(10,10,10,0.4)}html.theme--documenter-dark .is-small.modal-close,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.modal-close,html.theme--documenter-dark .is-small.delete,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.delete{height:16px;max-height:16px;max-width:16px;min-height:16px;min-width:16px;width:16px}html.theme--documenter-dark .is-medium.modal-close,html.theme--documenter-dark .is-medium.delete{height:24px;max-height:24px;max-width:24px;min-height:24px;min-width:24px;width:24px}html.theme--documenter-dark .is-large.modal-close,html.theme--documenter-dark .is-large.delete{height:32px;max-height:32px;max-width:32px;min-height:32px;min-width:32px;width:32px}html.theme--documenter-dark .control.is-loading::after,html.theme--documenter-dark .select.is-loading::after,html.theme--documenter-dark .loader,html.theme--documenter-dark .button.is-loading::after{animation:spinAround 500ms infinite linear;border:2px solid #dbdee0;border-radius:9999px;border-right-color:transparent;border-top-color:transparent;content:"";display:block;height:1em;position:relative;width:1em}html.theme--documenter-dark .hero-video,html.theme--documenter-dark .modal-background,html.theme--documenter-dark .modal,html.theme--documenter-dark .image.is-square img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-square img,html.theme--documenter-dark .image.is-square .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-square .has-ratio,html.theme--documenter-dark .image.is-1by1 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-1by1 img,html.theme--documenter-dark .image.is-1by1 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-1by1 .has-ratio,html.theme--documenter-dark .image.is-5by4 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-5by4 img,html.theme--documenter-dark .image.is-5by4 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-5by4 .has-ratio,html.theme--documenter-dark .image.is-4by3 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-4by3 img,html.theme--documenter-dark .image.is-4by3 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-4by3 .has-ratio,html.theme--documenter-dark .image.is-3by2 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-3by2 img,html.theme--documenter-dark .image.is-3by2 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-3by2 .has-ratio,html.theme--documenter-dark .image.is-5by3 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-5by3 img,html.theme--documenter-dark .image.is-5by3 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-5by3 .has-ratio,html.theme--documenter-dark .image.is-16by9 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-16by9 img,html.theme--documenter-dark .image.is-16by9 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-16by9 .has-ratio,html.theme--documenter-dark .image.is-2by1 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-2by1 img,html.theme--documenter-dark .image.is-2by1 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-2by1 .has-ratio,html.theme--documenter-dark .image.is-3by1 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-3by1 img,html.theme--documenter-dark .image.is-3by1 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-3by1 .has-ratio,html.theme--documenter-dark .image.is-4by5 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-4by5 img,html.theme--documenter-dark .image.is-4by5 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-4by5 .has-ratio,html.theme--documenter-dark .image.is-3by4 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-3by4 img,html.theme--documenter-dark .image.is-3by4 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-3by4 .has-ratio,html.theme--documenter-dark .image.is-2by3 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-2by3 img,html.theme--documenter-dark .image.is-2by3 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-2by3 .has-ratio,html.theme--documenter-dark .image.is-3by5 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-3by5 img,html.theme--documenter-dark .image.is-3by5 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-3by5 .has-ratio,html.theme--documenter-dark .image.is-9by16 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-9by16 img,html.theme--documenter-dark .image.is-9by16 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-9by16 .has-ratio,html.theme--documenter-dark .image.is-1by2 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-1by2 img,html.theme--documenter-dark .image.is-1by2 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-1by2 .has-ratio,html.theme--documenter-dark .image.is-1by3 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-1by3 img,html.theme--documenter-dark .image.is-1by3 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-1by3 .has-ratio,.is-overlay{bottom:0;left:0;position:absolute;right:0;top:0}html.theme--documenter-dark .navbar-burger{-moz-appearance:none;-webkit-appearance:none;appearance:none;background:none;border:none;color:currentColor;font-family:inherit;font-size:1em;margin:0;padding:0}/*! minireset.css v0.0.6 | MIT License | github.com/jgthms/minireset.css */html,body,p,ol,ul,li,dl,dt,dd,blockquote,figure,fieldset,legend,textarea,pre,iframe,hr,h1,h2,h3,h4,h5,h6{margin:0;padding:0}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal}ul{list-style:none}button,input,select,textarea{margin:0}html{box-sizing:border-box}*,*::before,*::after{box-sizing:inherit}img,video{height:auto;max-width:100%}iframe{border:0}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}td:not([align]),th:not([align]){text-align:inherit}.has-text-white{color:#fff !important}a.has-text-white:hover,a.has-text-white:focus{color:#e6e6e6 !important}.has-background-white{background-color:#fff !important}.has-text-black{color:#0a0a0a !important}a.has-text-black:hover,a.has-text-black:focus{color:#000 !important}.has-background-black{background-color:#0a0a0a !important}.has-text-light{color:#ecf0f1 !important}a.has-text-light:hover,a.has-text-light:focus{color:#cfd9db !important}.has-background-light{background-color:#ecf0f1 !important}.has-text-dark{color:#282f2f !important}a.has-text-dark:hover,a.has-text-dark:focus{color:#111414 !important}.has-background-dark{background-color:#282f2f !important}.has-text-primary{color:#375a7f !important}a.has-text-primary:hover,a.has-text-primary:focus{color:#28415b !important}.has-background-primary{background-color:#375a7f !important}.has-text-primary-light{color:#f1f5f9 !important}a.has-text-primary-light:hover,a.has-text-primary-light:focus{color:#cddbe9 !important}.has-background-primary-light{background-color:#f1f5f9 !important}.has-text-primary-dark{color:#4d7eb2 !important}a.has-text-primary-dark:hover,a.has-text-primary-dark:focus{color:#7198c1 !important}.has-background-primary-dark{background-color:#4d7eb2 !important}.has-text-link{color:#1abc9c !important}a.has-text-link:hover,a.has-text-link:focus{color:#148f77 !important}.has-background-link{background-color:#1abc9c !important}.has-text-link-light{color:#edfdf9 !important}a.has-text-link-light:hover,a.has-text-link-light:focus{color:#c0f6ec !important}.has-background-link-light{background-color:#edfdf9 !important}.has-text-link-dark{color:#15987e !important}a.has-text-link-dark:hover,a.has-text-link-dark:focus{color:#1bc5a4 !important}.has-background-link-dark{background-color:#15987e !important}.has-text-info{color:#024c7d !important}a.has-text-info:hover,a.has-text-info:focus{color:#012d4b !important}.has-background-info{background-color:#024c7d !important}.has-text-info-light{color:#ebf7ff !important}a.has-text-info-light:hover,a.has-text-info-light:focus{color:#b9e2fe !important}.has-background-info-light{background-color:#ebf7ff !important}.has-text-info-dark{color:#0e9dfb !important}a.has-text-info-dark:hover,a.has-text-info-dark:focus{color:#40b1fc !important}.has-background-info-dark{background-color:#0e9dfb !important}.has-text-success{color:#008438 !important}a.has-text-success:hover,a.has-text-success:focus{color:#005122 !important}.has-background-success{background-color:#008438 !important}.has-text-success-light{color:#ebfff3 !important}a.has-text-success-light:hover,a.has-text-success-light:focus{color:#b8ffd6 !important}.has-background-success-light{background-color:#ebfff3 !important}.has-text-success-dark{color:#00eb64 !important}a.has-text-success-dark:hover,a.has-text-success-dark:focus{color:#1fff7e !important}.has-background-success-dark{background-color:#00eb64 !important}.has-text-warning{color:#ad8100 !important}a.has-text-warning:hover,a.has-text-warning:focus{color:#7a5b00 !important}.has-background-warning{background-color:#ad8100 !important}.has-text-warning-light{color:#fffaeb !important}a.has-text-warning-light:hover,a.has-text-warning-light:focus{color:#ffedb8 !important}.has-background-warning-light{background-color:#fffaeb !important}.has-text-warning-dark{color:#d19c00 !important}a.has-text-warning-dark:hover,a.has-text-warning-dark:focus{color:#ffbf05 !important}.has-background-warning-dark{background-color:#d19c00 !important}.has-text-danger{color:#9e1b0d !important}a.has-text-danger:hover,a.has-text-danger:focus{color:#6f1309 !important}.has-background-danger{background-color:#9e1b0d !important}.has-text-danger-light{color:#fdeeec !important}a.has-text-danger-light:hover,a.has-text-danger-light:focus{color:#fac3bd !important}.has-background-danger-light{background-color:#fdeeec !important}.has-text-danger-dark{color:#ec311d !important}a.has-text-danger-dark:hover,a.has-text-danger-dark:focus{color:#f05c4c !important}.has-background-danger-dark{background-color:#ec311d !important}.has-text-black-bis{color:#121212 !important}.has-background-black-bis{background-color:#121212 !important}.has-text-black-ter{color:#242424 !important}.has-background-black-ter{background-color:#242424 !important}.has-text-grey-darker{color:#282f2f !important}.has-background-grey-darker{background-color:#282f2f !important}.has-text-grey-dark{color:#343c3d !important}.has-background-grey-dark{background-color:#343c3d !important}.has-text-grey{color:#5e6d6f !important}.has-background-grey{background-color:#5e6d6f !important}.has-text-grey-light{color:#8c9b9d !important}.has-background-grey-light{background-color:#8c9b9d !important}.has-text-grey-lighter{color:#dbdee0 !important}.has-background-grey-lighter{background-color:#dbdee0 !important}.has-text-white-ter{color:#ecf0f1 !important}.has-background-white-ter{background-color:#ecf0f1 !important}.has-text-white-bis{color:#fafafa !important}.has-background-white-bis{background-color:#fafafa !important}.is-flex-direction-row{flex-direction:row !important}.is-flex-direction-row-reverse{flex-direction:row-reverse !important}.is-flex-direction-column{flex-direction:column !important}.is-flex-direction-column-reverse{flex-direction:column-reverse !important}.is-flex-wrap-nowrap{flex-wrap:nowrap !important}.is-flex-wrap-wrap{flex-wrap:wrap !important}.is-flex-wrap-wrap-reverse{flex-wrap:wrap-reverse !important}.is-justify-content-flex-start{justify-content:flex-start !important}.is-justify-content-flex-end{justify-content:flex-end !important}.is-justify-content-center{justify-content:center !important}.is-justify-content-space-between{justify-content:space-between !important}.is-justify-content-space-around{justify-content:space-around !important}.is-justify-content-space-evenly{justify-content:space-evenly !important}.is-justify-content-start{justify-content:start !important}.is-justify-content-end{justify-content:end !important}.is-justify-content-left{justify-content:left !important}.is-justify-content-right{justify-content:right !important}.is-align-content-flex-start{align-content:flex-start !important}.is-align-content-flex-end{align-content:flex-end !important}.is-align-content-center{align-content:center !important}.is-align-content-space-between{align-content:space-between !important}.is-align-content-space-around{align-content:space-around !important}.is-align-content-space-evenly{align-content:space-evenly !important}.is-align-content-stretch{align-content:stretch !important}.is-align-content-start{align-content:start !important}.is-align-content-end{align-content:end !important}.is-align-content-baseline{align-content:baseline !important}.is-align-items-stretch{align-items:stretch !important}.is-align-items-flex-start{align-items:flex-start !important}.is-align-items-flex-end{align-items:flex-end !important}.is-align-items-center{align-items:center !important}.is-align-items-baseline{align-items:baseline !important}.is-align-items-start{align-items:start !important}.is-align-items-end{align-items:end !important}.is-align-items-self-start{align-items:self-start !important}.is-align-items-self-end{align-items:self-end !important}.is-align-self-auto{align-self:auto !important}.is-align-self-flex-start{align-self:flex-start !important}.is-align-self-flex-end{align-self:flex-end !important}.is-align-self-center{align-self:center !important}.is-align-self-baseline{align-self:baseline !important}.is-align-self-stretch{align-self:stretch !important}.is-flex-grow-0{flex-grow:0 !important}.is-flex-grow-1{flex-grow:1 !important}.is-flex-grow-2{flex-grow:2 !important}.is-flex-grow-3{flex-grow:3 !important}.is-flex-grow-4{flex-grow:4 !important}.is-flex-grow-5{flex-grow:5 !important}.is-flex-shrink-0{flex-shrink:0 !important}.is-flex-shrink-1{flex-shrink:1 !important}.is-flex-shrink-2{flex-shrink:2 !important}.is-flex-shrink-3{flex-shrink:3 !important}.is-flex-shrink-4{flex-shrink:4 !important}.is-flex-shrink-5{flex-shrink:5 !important}.is-clearfix::after{clear:both;content:" ";display:table}.is-pulled-left{float:left !important}.is-pulled-right{float:right !important}.is-radiusless{border-radius:0 !important}.is-shadowless{box-shadow:none !important}.is-clickable{cursor:pointer !important;pointer-events:all !important}.is-clipped{overflow:hidden !important}.is-relative{position:relative !important}.is-marginless{margin:0 !important}.is-paddingless{padding:0 !important}.m-0{margin:0 !important}.mt-0{margin-top:0 !important}.mr-0{margin-right:0 !important}.mb-0{margin-bottom:0 !important}.ml-0{margin-left:0 !important}.mx-0{margin-left:0 !important;margin-right:0 !important}.my-0{margin-top:0 !important;margin-bottom:0 !important}.m-1{margin:.25rem !important}.mt-1{margin-top:.25rem !important}.mr-1{margin-right:.25rem !important}.mb-1{margin-bottom:.25rem !important}.ml-1{margin-left:.25rem !important}.mx-1{margin-left:.25rem !important;margin-right:.25rem !important}.my-1{margin-top:.25rem !important;margin-bottom:.25rem !important}.m-2{margin:.5rem !important}.mt-2{margin-top:.5rem !important}.mr-2{margin-right:.5rem !important}.mb-2{margin-bottom:.5rem !important}.ml-2{margin-left:.5rem !important}.mx-2{margin-left:.5rem !important;margin-right:.5rem !important}.my-2{margin-top:.5rem !important;margin-bottom:.5rem !important}.m-3{margin:.75rem !important}.mt-3{margin-top:.75rem !important}.mr-3{margin-right:.75rem !important}.mb-3{margin-bottom:.75rem !important}.ml-3{margin-left:.75rem !important}.mx-3{margin-left:.75rem !important;margin-right:.75rem !important}.my-3{margin-top:.75rem !important;margin-bottom:.75rem !important}.m-4{margin:1rem !important}.mt-4{margin-top:1rem !important}.mr-4{margin-right:1rem !important}.mb-4{margin-bottom:1rem !important}.ml-4{margin-left:1rem !important}.mx-4{margin-left:1rem !important;margin-right:1rem !important}.my-4{margin-top:1rem !important;margin-bottom:1rem !important}.m-5{margin:1.5rem !important}.mt-5{margin-top:1.5rem !important}.mr-5{margin-right:1.5rem !important}.mb-5{margin-bottom:1.5rem !important}.ml-5{margin-left:1.5rem !important}.mx-5{margin-left:1.5rem !important;margin-right:1.5rem !important}.my-5{margin-top:1.5rem !important;margin-bottom:1.5rem !important}.m-6{margin:3rem !important}.mt-6{margin-top:3rem !important}.mr-6{margin-right:3rem !important}.mb-6{margin-bottom:3rem !important}.ml-6{margin-left:3rem !important}.mx-6{margin-left:3rem !important;margin-right:3rem !important}.my-6{margin-top:3rem !important;margin-bottom:3rem !important}.m-auto{margin:auto !important}.mt-auto{margin-top:auto !important}.mr-auto{margin-right:auto !important}.mb-auto{margin-bottom:auto !important}.ml-auto{margin-left:auto !important}.mx-auto{margin-left:auto !important;margin-right:auto !important}.my-auto{margin-top:auto !important;margin-bottom:auto !important}.p-0{padding:0 !important}.pt-0{padding-top:0 !important}.pr-0{padding-right:0 !important}.pb-0{padding-bottom:0 !important}.pl-0{padding-left:0 !important}.px-0{padding-left:0 !important;padding-right:0 !important}.py-0{padding-top:0 !important;padding-bottom:0 !important}.p-1{padding:.25rem !important}.pt-1{padding-top:.25rem !important}.pr-1{padding-right:.25rem !important}.pb-1{padding-bottom:.25rem !important}.pl-1{padding-left:.25rem !important}.px-1{padding-left:.25rem !important;padding-right:.25rem !important}.py-1{padding-top:.25rem !important;padding-bottom:.25rem !important}.p-2{padding:.5rem !important}.pt-2{padding-top:.5rem !important}.pr-2{padding-right:.5rem !important}.pb-2{padding-bottom:.5rem !important}.pl-2{padding-left:.5rem !important}.px-2{padding-left:.5rem !important;padding-right:.5rem !important}.py-2{padding-top:.5rem !important;padding-bottom:.5rem !important}.p-3{padding:.75rem !important}.pt-3{padding-top:.75rem !important}.pr-3{padding-right:.75rem !important}.pb-3{padding-bottom:.75rem !important}.pl-3{padding-left:.75rem !important}.px-3{padding-left:.75rem !important;padding-right:.75rem !important}.py-3{padding-top:.75rem !important;padding-bottom:.75rem !important}.p-4{padding:1rem !important}.pt-4{padding-top:1rem !important}.pr-4{padding-right:1rem !important}.pb-4{padding-bottom:1rem !important}.pl-4{padding-left:1rem !important}.px-4{padding-left:1rem !important;padding-right:1rem !important}.py-4{padding-top:1rem !important;padding-bottom:1rem !important}.p-5{padding:1.5rem !important}.pt-5{padding-top:1.5rem !important}.pr-5{padding-right:1.5rem !important}.pb-5{padding-bottom:1.5rem !important}.pl-5{padding-left:1.5rem !important}.px-5{padding-left:1.5rem !important;padding-right:1.5rem !important}.py-5{padding-top:1.5rem !important;padding-bottom:1.5rem !important}.p-6{padding:3rem !important}.pt-6{padding-top:3rem !important}.pr-6{padding-right:3rem !important}.pb-6{padding-bottom:3rem !important}.pl-6{padding-left:3rem !important}.px-6{padding-left:3rem !important;padding-right:3rem !important}.py-6{padding-top:3rem !important;padding-bottom:3rem !important}.p-auto{padding:auto !important}.pt-auto{padding-top:auto !important}.pr-auto{padding-right:auto !important}.pb-auto{padding-bottom:auto !important}.pl-auto{padding-left:auto !important}.px-auto{padding-left:auto !important;padding-right:auto !important}.py-auto{padding-top:auto !important;padding-bottom:auto !important}.is-size-1{font-size:3rem !important}.is-size-2{font-size:2.5rem !important}.is-size-3{font-size:2rem !important}.is-size-4{font-size:1.5rem !important}.is-size-5{font-size:1.25rem !important}.is-size-6{font-size:1rem !important}.is-size-7,html.theme--documenter-dark .docstring>section>a.docs-sourcelink{font-size:.75rem !important}@media screen and (max-width: 768px){.is-size-1-mobile{font-size:3rem !important}.is-size-2-mobile{font-size:2.5rem !important}.is-size-3-mobile{font-size:2rem !important}.is-size-4-mobile{font-size:1.5rem !important}.is-size-5-mobile{font-size:1.25rem !important}.is-size-6-mobile{font-size:1rem !important}.is-size-7-mobile{font-size:.75rem !important}}@media screen and (min-width: 769px),print{.is-size-1-tablet{font-size:3rem !important}.is-size-2-tablet{font-size:2.5rem !important}.is-size-3-tablet{font-size:2rem !important}.is-size-4-tablet{font-size:1.5rem !important}.is-size-5-tablet{font-size:1.25rem !important}.is-size-6-tablet{font-size:1rem !important}.is-size-7-tablet{font-size:.75rem !important}}@media screen and (max-width: 1055px){.is-size-1-touch{font-size:3rem !important}.is-size-2-touch{font-size:2.5rem !important}.is-size-3-touch{font-size:2rem !important}.is-size-4-touch{font-size:1.5rem !important}.is-size-5-touch{font-size:1.25rem !important}.is-size-6-touch{font-size:1rem !important}.is-size-7-touch{font-size:.75rem !important}}@media screen and (min-width: 1056px){.is-size-1-desktop{font-size:3rem !important}.is-size-2-desktop{font-size:2.5rem !important}.is-size-3-desktop{font-size:2rem !important}.is-size-4-desktop{font-size:1.5rem !important}.is-size-5-desktop{font-size:1.25rem !important}.is-size-6-desktop{font-size:1rem !important}.is-size-7-desktop{font-size:.75rem !important}}@media screen and (min-width: 1216px){.is-size-1-widescreen{font-size:3rem !important}.is-size-2-widescreen{font-size:2.5rem !important}.is-size-3-widescreen{font-size:2rem !important}.is-size-4-widescreen{font-size:1.5rem !important}.is-size-5-widescreen{font-size:1.25rem !important}.is-size-6-widescreen{font-size:1rem !important}.is-size-7-widescreen{font-size:.75rem !important}}@media screen and (min-width: 1408px){.is-size-1-fullhd{font-size:3rem !important}.is-size-2-fullhd{font-size:2.5rem !important}.is-size-3-fullhd{font-size:2rem !important}.is-size-4-fullhd{font-size:1.5rem !important}.is-size-5-fullhd{font-size:1.25rem !important}.is-size-6-fullhd{font-size:1rem !important}.is-size-7-fullhd{font-size:.75rem !important}}.has-text-centered{text-align:center !important}.has-text-justified{text-align:justify !important}.has-text-left{text-align:left !important}.has-text-right{text-align:right !important}@media screen and (max-width: 768px){.has-text-centered-mobile{text-align:center !important}}@media screen and (min-width: 769px),print{.has-text-centered-tablet{text-align:center !important}}@media screen and (min-width: 769px) and (max-width: 1055px){.has-text-centered-tablet-only{text-align:center !important}}@media screen and (max-width: 1055px){.has-text-centered-touch{text-align:center !important}}@media screen and (min-width: 1056px){.has-text-centered-desktop{text-align:center !important}}@media screen and (min-width: 1056px) and (max-width: 1215px){.has-text-centered-desktop-only{text-align:center !important}}@media screen and (min-width: 1216px){.has-text-centered-widescreen{text-align:center !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.has-text-centered-widescreen-only{text-align:center !important}}@media screen and (min-width: 1408px){.has-text-centered-fullhd{text-align:center !important}}@media screen and (max-width: 768px){.has-text-justified-mobile{text-align:justify !important}}@media screen and (min-width: 769px),print{.has-text-justified-tablet{text-align:justify !important}}@media screen and (min-width: 769px) and (max-width: 1055px){.has-text-justified-tablet-only{text-align:justify !important}}@media screen and (max-width: 1055px){.has-text-justified-touch{text-align:justify !important}}@media screen and (min-width: 1056px){.has-text-justified-desktop{text-align:justify !important}}@media screen and (min-width: 1056px) and (max-width: 1215px){.has-text-justified-desktop-only{text-align:justify !important}}@media screen and (min-width: 1216px){.has-text-justified-widescreen{text-align:justify !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.has-text-justified-widescreen-only{text-align:justify !important}}@media screen and (min-width: 1408px){.has-text-justified-fullhd{text-align:justify !important}}@media screen and (max-width: 768px){.has-text-left-mobile{text-align:left !important}}@media screen and (min-width: 769px),print{.has-text-left-tablet{text-align:left !important}}@media screen and (min-width: 769px) and (max-width: 1055px){.has-text-left-tablet-only{text-align:left !important}}@media screen and (max-width: 1055px){.has-text-left-touch{text-align:left !important}}@media screen and (min-width: 1056px){.has-text-left-desktop{text-align:left !important}}@media screen and (min-width: 1056px) and (max-width: 1215px){.has-text-left-desktop-only{text-align:left !important}}@media screen and (min-width: 1216px){.has-text-left-widescreen{text-align:left !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.has-text-left-widescreen-only{text-align:left !important}}@media screen and (min-width: 1408px){.has-text-left-fullhd{text-align:left !important}}@media screen and (max-width: 768px){.has-text-right-mobile{text-align:right !important}}@media screen and (min-width: 769px),print{.has-text-right-tablet{text-align:right !important}}@media screen and (min-width: 769px) and (max-width: 1055px){.has-text-right-tablet-only{text-align:right !important}}@media screen and (max-width: 1055px){.has-text-right-touch{text-align:right !important}}@media screen and (min-width: 1056px){.has-text-right-desktop{text-align:right !important}}@media screen and (min-width: 1056px) and (max-width: 1215px){.has-text-right-desktop-only{text-align:right !important}}@media screen and (min-width: 1216px){.has-text-right-widescreen{text-align:right !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.has-text-right-widescreen-only{text-align:right !important}}@media screen and (min-width: 1408px){.has-text-right-fullhd{text-align:right !important}}.is-capitalized{text-transform:capitalize !important}.is-lowercase{text-transform:lowercase !important}.is-uppercase{text-transform:uppercase !important}.is-italic{font-style:italic !important}.is-underlined{text-decoration:underline !important}.has-text-weight-light{font-weight:300 !important}.has-text-weight-normal{font-weight:400 !important}.has-text-weight-medium{font-weight:500 !important}.has-text-weight-semibold{font-weight:600 !important}.has-text-weight-bold{font-weight:700 !important}.is-family-primary{font-family:"Lato Medium",-apple-system,BlinkMacSystemFont,"Segoe UI","Helvetica Neue","Helvetica","Arial",sans-serif !important}.is-family-secondary{font-family:"Lato Medium",-apple-system,BlinkMacSystemFont,"Segoe UI","Helvetica Neue","Helvetica","Arial",sans-serif !important}.is-family-sans-serif{font-family:"Lato Medium",-apple-system,BlinkMacSystemFont,"Segoe UI","Helvetica Neue","Helvetica","Arial",sans-serif !important}.is-family-monospace{font-family:"JuliaMono","SFMono-Regular","Menlo","Consolas","Liberation Mono","DejaVu Sans Mono",monospace !important}.is-family-code{font-family:"JuliaMono","SFMono-Regular","Menlo","Consolas","Liberation Mono","DejaVu Sans Mono",monospace !important}.is-block{display:block !important}@media screen and (max-width: 768px){.is-block-mobile{display:block !important}}@media screen and (min-width: 769px),print{.is-block-tablet{display:block !important}}@media screen and (min-width: 769px) and (max-width: 1055px){.is-block-tablet-only{display:block !important}}@media screen and (max-width: 1055px){.is-block-touch{display:block !important}}@media screen and (min-width: 1056px){.is-block-desktop{display:block !important}}@media screen and (min-width: 1056px) and (max-width: 1215px){.is-block-desktop-only{display:block !important}}@media screen and (min-width: 1216px){.is-block-widescreen{display:block !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.is-block-widescreen-only{display:block !important}}@media screen and (min-width: 1408px){.is-block-fullhd{display:block !important}}.is-flex{display:flex !important}@media screen and (max-width: 768px){.is-flex-mobile{display:flex !important}}@media screen and (min-width: 769px),print{.is-flex-tablet{display:flex !important}}@media screen and (min-width: 769px) and (max-width: 1055px){.is-flex-tablet-only{display:flex !important}}@media screen and (max-width: 1055px){.is-flex-touch{display:flex !important}}@media screen and (min-width: 1056px){.is-flex-desktop{display:flex !important}}@media screen and (min-width: 1056px) and (max-width: 1215px){.is-flex-desktop-only{display:flex !important}}@media screen and (min-width: 1216px){.is-flex-widescreen{display:flex !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.is-flex-widescreen-only{display:flex !important}}@media screen and (min-width: 1408px){.is-flex-fullhd{display:flex !important}}.is-inline{display:inline !important}@media screen and (max-width: 768px){.is-inline-mobile{display:inline !important}}@media screen and (min-width: 769px),print{.is-inline-tablet{display:inline !important}}@media screen and (min-width: 769px) and (max-width: 1055px){.is-inline-tablet-only{display:inline !important}}@media screen and (max-width: 1055px){.is-inline-touch{display:inline !important}}@media screen and (min-width: 1056px){.is-inline-desktop{display:inline !important}}@media screen and (min-width: 1056px) and (max-width: 1215px){.is-inline-desktop-only{display:inline !important}}@media screen and (min-width: 1216px){.is-inline-widescreen{display:inline !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.is-inline-widescreen-only{display:inline !important}}@media screen and (min-width: 1408px){.is-inline-fullhd{display:inline !important}}.is-inline-block{display:inline-block !important}@media screen and (max-width: 768px){.is-inline-block-mobile{display:inline-block !important}}@media screen and (min-width: 769px),print{.is-inline-block-tablet{display:inline-block !important}}@media screen and (min-width: 769px) and (max-width: 1055px){.is-inline-block-tablet-only{display:inline-block !important}}@media screen and (max-width: 1055px){.is-inline-block-touch{display:inline-block !important}}@media screen and (min-width: 1056px){.is-inline-block-desktop{display:inline-block !important}}@media screen and (min-width: 1056px) and (max-width: 1215px){.is-inline-block-desktop-only{display:inline-block !important}}@media screen and (min-width: 1216px){.is-inline-block-widescreen{display:inline-block !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.is-inline-block-widescreen-only{display:inline-block !important}}@media screen and (min-width: 1408px){.is-inline-block-fullhd{display:inline-block !important}}.is-inline-flex{display:inline-flex !important}@media screen and (max-width: 768px){.is-inline-flex-mobile{display:inline-flex !important}}@media screen and (min-width: 769px),print{.is-inline-flex-tablet{display:inline-flex !important}}@media screen and (min-width: 769px) and (max-width: 1055px){.is-inline-flex-tablet-only{display:inline-flex !important}}@media screen and (max-width: 1055px){.is-inline-flex-touch{display:inline-flex !important}}@media screen and (min-width: 1056px){.is-inline-flex-desktop{display:inline-flex !important}}@media screen and (min-width: 1056px) and (max-width: 1215px){.is-inline-flex-desktop-only{display:inline-flex !important}}@media screen and (min-width: 1216px){.is-inline-flex-widescreen{display:inline-flex !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.is-inline-flex-widescreen-only{display:inline-flex !important}}@media screen and (min-width: 1408px){.is-inline-flex-fullhd{display:inline-flex !important}}.is-hidden{display:none !important}.is-sr-only{border:none !important;clip:rect(0, 0, 0, 0) !important;height:0.01em !important;overflow:hidden !important;padding:0 !important;position:absolute !important;white-space:nowrap !important;width:0.01em !important}@media screen and (max-width: 768px){.is-hidden-mobile{display:none !important}}@media screen and (min-width: 769px),print{.is-hidden-tablet{display:none !important}}@media screen and (min-width: 769px) and (max-width: 1055px){.is-hidden-tablet-only{display:none !important}}@media screen and (max-width: 1055px){.is-hidden-touch{display:none !important}}@media screen and (min-width: 1056px){.is-hidden-desktop{display:none !important}}@media screen and (min-width: 1056px) and (max-width: 1215px){.is-hidden-desktop-only{display:none !important}}@media screen and (min-width: 1216px){.is-hidden-widescreen{display:none !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.is-hidden-widescreen-only{display:none !important}}@media screen and (min-width: 1408px){.is-hidden-fullhd{display:none !important}}.is-invisible{visibility:hidden !important}@media screen and (max-width: 768px){.is-invisible-mobile{visibility:hidden !important}}@media screen and (min-width: 769px),print{.is-invisible-tablet{visibility:hidden !important}}@media screen and (min-width: 769px) and (max-width: 1055px){.is-invisible-tablet-only{visibility:hidden !important}}@media screen and (max-width: 1055px){.is-invisible-touch{visibility:hidden !important}}@media screen and (min-width: 1056px){.is-invisible-desktop{visibility:hidden !important}}@media screen and (min-width: 1056px) and (max-width: 1215px){.is-invisible-desktop-only{visibility:hidden !important}}@media screen and (min-width: 1216px){.is-invisible-widescreen{visibility:hidden !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.is-invisible-widescreen-only{visibility:hidden !important}}@media screen and (min-width: 1408px){.is-invisible-fullhd{visibility:hidden !important}}html.theme--documenter-dark{/*! + Theme: a11y-dark + Author: @ericwbailey + Maintainer: @ericwbailey + + Based on the Tomorrow Night Eighties theme: https://github.com/isagalaev/highlight.js/blob/master/src/styles/tomorrow-night-eighties.css +*/}html.theme--documenter-dark html{background-color:#1f2424;font-size:16px;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;min-width:300px;overflow-x:auto;overflow-y:scroll;text-rendering:optimizeLegibility;text-size-adjust:100%}html.theme--documenter-dark article,html.theme--documenter-dark aside,html.theme--documenter-dark figure,html.theme--documenter-dark footer,html.theme--documenter-dark header,html.theme--documenter-dark hgroup,html.theme--documenter-dark section{display:block}html.theme--documenter-dark body,html.theme--documenter-dark button,html.theme--documenter-dark input,html.theme--documenter-dark optgroup,html.theme--documenter-dark select,html.theme--documenter-dark textarea{font-family:"Lato Medium",-apple-system,BlinkMacSystemFont,"Segoe UI","Helvetica Neue","Helvetica","Arial",sans-serif}html.theme--documenter-dark code,html.theme--documenter-dark pre{-moz-osx-font-smoothing:auto;-webkit-font-smoothing:auto;font-family:"JuliaMono","SFMono-Regular","Menlo","Consolas","Liberation Mono","DejaVu Sans Mono",monospace}html.theme--documenter-dark body{color:#fff;font-size:1em;font-weight:400;line-height:1.5}html.theme--documenter-dark a{color:#1abc9c;cursor:pointer;text-decoration:none}html.theme--documenter-dark a strong{color:currentColor}html.theme--documenter-dark a:hover{color:#1dd2af}html.theme--documenter-dark code{background-color:rgba(255,255,255,0.05);color:#ececec;font-size:.875em;font-weight:normal;padding:.1em}html.theme--documenter-dark hr{background-color:#282f2f;border:none;display:block;height:2px;margin:1.5rem 0}html.theme--documenter-dark img{height:auto;max-width:100%}html.theme--documenter-dark input[type="checkbox"],html.theme--documenter-dark input[type="radio"]{vertical-align:baseline}html.theme--documenter-dark small{font-size:.875em}html.theme--documenter-dark span{font-style:inherit;font-weight:inherit}html.theme--documenter-dark strong{color:#f2f2f2;font-weight:700}html.theme--documenter-dark fieldset{border:none}html.theme--documenter-dark pre{-webkit-overflow-scrolling:touch;background-color:#282f2f;color:#fff;font-size:.875em;overflow-x:auto;padding:1.25rem 1.5rem;white-space:pre;word-wrap:normal}html.theme--documenter-dark pre code{background-color:transparent;color:currentColor;font-size:1em;padding:0}html.theme--documenter-dark table td,html.theme--documenter-dark table th{vertical-align:top}html.theme--documenter-dark table td:not([align]),html.theme--documenter-dark table th:not([align]){text-align:inherit}html.theme--documenter-dark table th{color:#f2f2f2}html.theme--documenter-dark .box{background-color:#343c3d;border-radius:8px;box-shadow:none;color:#fff;display:block;padding:1.25rem}html.theme--documenter-dark a.box:hover,html.theme--documenter-dark a.box:focus{box-shadow:0 0.5em 1em -0.125em rgba(10,10,10,0.1),0 0 0 1px #1abc9c}html.theme--documenter-dark a.box:active{box-shadow:inset 0 1px 2px rgba(10,10,10,0.2),0 0 0 1px #1abc9c}html.theme--documenter-dark .button{background-color:#282f2f;border-color:#4c5759;border-width:1px;color:#375a7f;cursor:pointer;justify-content:center;padding-bottom:calc(0.5em - 1px);padding-left:1em;padding-right:1em;padding-top:calc(0.5em - 1px);text-align:center;white-space:nowrap}html.theme--documenter-dark .button strong{color:inherit}html.theme--documenter-dark .button .icon,html.theme--documenter-dark .button .icon.is-small,html.theme--documenter-dark .button #documenter .docs-sidebar form.docs-search>input.icon,html.theme--documenter-dark #documenter .docs-sidebar .button form.docs-search>input.icon,html.theme--documenter-dark .button .icon.is-medium,html.theme--documenter-dark .button .icon.is-large{height:1.5em;width:1.5em}html.theme--documenter-dark .button .icon:first-child:not(:last-child){margin-left:calc(-0.5em - 1px);margin-right:.25em}html.theme--documenter-dark .button .icon:last-child:not(:first-child){margin-left:.25em;margin-right:calc(-0.5em - 1px)}html.theme--documenter-dark .button .icon:first-child:last-child{margin-left:calc(-0.5em - 1px);margin-right:calc(-0.5em - 1px)}html.theme--documenter-dark .button:hover,html.theme--documenter-dark .button.is-hovered{border-color:#8c9b9d;color:#f2f2f2}html.theme--documenter-dark .button:focus,html.theme--documenter-dark .button.is-focused{border-color:#8c9b9d;color:#17a689}html.theme--documenter-dark .button:focus:not(:active),html.theme--documenter-dark .button.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(26,188,156,0.25)}html.theme--documenter-dark .button:active,html.theme--documenter-dark .button.is-active{border-color:#343c3d;color:#f2f2f2}html.theme--documenter-dark .button.is-text{background-color:transparent;border-color:transparent;color:#fff;text-decoration:underline}html.theme--documenter-dark .button.is-text:hover,html.theme--documenter-dark .button.is-text.is-hovered,html.theme--documenter-dark .button.is-text:focus,html.theme--documenter-dark .button.is-text.is-focused{background-color:#282f2f;color:#f2f2f2}html.theme--documenter-dark .button.is-text:active,html.theme--documenter-dark .button.is-text.is-active{background-color:#1d2122;color:#f2f2f2}html.theme--documenter-dark .button.is-text[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-text{background-color:transparent;border-color:transparent;box-shadow:none}html.theme--documenter-dark .button.is-ghost{background:none;border-color:rgba(0,0,0,0);color:#1abc9c;text-decoration:none}html.theme--documenter-dark .button.is-ghost:hover,html.theme--documenter-dark .button.is-ghost.is-hovered{color:#1abc9c;text-decoration:underline}html.theme--documenter-dark .button.is-white{background-color:#fff;border-color:transparent;color:#0a0a0a}html.theme--documenter-dark .button.is-white:hover,html.theme--documenter-dark .button.is-white.is-hovered{background-color:#f9f9f9;border-color:transparent;color:#0a0a0a}html.theme--documenter-dark .button.is-white:focus,html.theme--documenter-dark .button.is-white.is-focused{border-color:transparent;color:#0a0a0a}html.theme--documenter-dark .button.is-white:focus:not(:active),html.theme--documenter-dark .button.is-white.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(255,255,255,0.25)}html.theme--documenter-dark .button.is-white:active,html.theme--documenter-dark .button.is-white.is-active{background-color:#f2f2f2;border-color:transparent;color:#0a0a0a}html.theme--documenter-dark .button.is-white[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-white{background-color:#fff;border-color:#fff;box-shadow:none}html.theme--documenter-dark .button.is-white.is-inverted{background-color:#0a0a0a;color:#fff}html.theme--documenter-dark .button.is-white.is-inverted:hover,html.theme--documenter-dark .button.is-white.is-inverted.is-hovered{background-color:#000}html.theme--documenter-dark .button.is-white.is-inverted[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-white.is-inverted{background-color:#0a0a0a;border-color:transparent;box-shadow:none;color:#fff}html.theme--documenter-dark .button.is-white.is-loading::after{border-color:transparent transparent #0a0a0a #0a0a0a !important}html.theme--documenter-dark .button.is-white.is-outlined{background-color:transparent;border-color:#fff;color:#fff}html.theme--documenter-dark .button.is-white.is-outlined:hover,html.theme--documenter-dark .button.is-white.is-outlined.is-hovered,html.theme--documenter-dark .button.is-white.is-outlined:focus,html.theme--documenter-dark .button.is-white.is-outlined.is-focused{background-color:#fff;border-color:#fff;color:#0a0a0a}html.theme--documenter-dark .button.is-white.is-outlined.is-loading::after{border-color:transparent transparent #fff #fff !important}html.theme--documenter-dark .button.is-white.is-outlined.is-loading:hover::after,html.theme--documenter-dark .button.is-white.is-outlined.is-loading.is-hovered::after,html.theme--documenter-dark .button.is-white.is-outlined.is-loading:focus::after,html.theme--documenter-dark .button.is-white.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #0a0a0a #0a0a0a !important}html.theme--documenter-dark .button.is-white.is-outlined[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-white.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}html.theme--documenter-dark .button.is-white.is-inverted.is-outlined{background-color:transparent;border-color:#0a0a0a;color:#0a0a0a}html.theme--documenter-dark .button.is-white.is-inverted.is-outlined:hover,html.theme--documenter-dark .button.is-white.is-inverted.is-outlined.is-hovered,html.theme--documenter-dark .button.is-white.is-inverted.is-outlined:focus,html.theme--documenter-dark .button.is-white.is-inverted.is-outlined.is-focused{background-color:#0a0a0a;color:#fff}html.theme--documenter-dark .button.is-white.is-inverted.is-outlined.is-loading:hover::after,html.theme--documenter-dark .button.is-white.is-inverted.is-outlined.is-loading.is-hovered::after,html.theme--documenter-dark .button.is-white.is-inverted.is-outlined.is-loading:focus::after,html.theme--documenter-dark .button.is-white.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #fff #fff !important}html.theme--documenter-dark .button.is-white.is-inverted.is-outlined[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-white.is-inverted.is-outlined{background-color:transparent;border-color:#0a0a0a;box-shadow:none;color:#0a0a0a}html.theme--documenter-dark .button.is-black{background-color:#0a0a0a;border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-black:hover,html.theme--documenter-dark .button.is-black.is-hovered{background-color:#040404;border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-black:focus,html.theme--documenter-dark .button.is-black.is-focused{border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-black:focus:not(:active),html.theme--documenter-dark .button.is-black.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(10,10,10,0.25)}html.theme--documenter-dark .button.is-black:active,html.theme--documenter-dark .button.is-black.is-active{background-color:#000;border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-black[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-black{background-color:#0a0a0a;border-color:#0a0a0a;box-shadow:none}html.theme--documenter-dark .button.is-black.is-inverted{background-color:#fff;color:#0a0a0a}html.theme--documenter-dark .button.is-black.is-inverted:hover,html.theme--documenter-dark .button.is-black.is-inverted.is-hovered{background-color:#f2f2f2}html.theme--documenter-dark .button.is-black.is-inverted[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-black.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#0a0a0a}html.theme--documenter-dark .button.is-black.is-loading::after{border-color:transparent transparent #fff #fff !important}html.theme--documenter-dark .button.is-black.is-outlined{background-color:transparent;border-color:#0a0a0a;color:#0a0a0a}html.theme--documenter-dark .button.is-black.is-outlined:hover,html.theme--documenter-dark .button.is-black.is-outlined.is-hovered,html.theme--documenter-dark .button.is-black.is-outlined:focus,html.theme--documenter-dark .button.is-black.is-outlined.is-focused{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}html.theme--documenter-dark .button.is-black.is-outlined.is-loading::after{border-color:transparent transparent #0a0a0a #0a0a0a !important}html.theme--documenter-dark .button.is-black.is-outlined.is-loading:hover::after,html.theme--documenter-dark .button.is-black.is-outlined.is-loading.is-hovered::after,html.theme--documenter-dark .button.is-black.is-outlined.is-loading:focus::after,html.theme--documenter-dark .button.is-black.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #fff #fff !important}html.theme--documenter-dark .button.is-black.is-outlined[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-black.is-outlined{background-color:transparent;border-color:#0a0a0a;box-shadow:none;color:#0a0a0a}html.theme--documenter-dark .button.is-black.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}html.theme--documenter-dark .button.is-black.is-inverted.is-outlined:hover,html.theme--documenter-dark .button.is-black.is-inverted.is-outlined.is-hovered,html.theme--documenter-dark .button.is-black.is-inverted.is-outlined:focus,html.theme--documenter-dark .button.is-black.is-inverted.is-outlined.is-focused{background-color:#fff;color:#0a0a0a}html.theme--documenter-dark .button.is-black.is-inverted.is-outlined.is-loading:hover::after,html.theme--documenter-dark .button.is-black.is-inverted.is-outlined.is-loading.is-hovered::after,html.theme--documenter-dark .button.is-black.is-inverted.is-outlined.is-loading:focus::after,html.theme--documenter-dark .button.is-black.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #0a0a0a #0a0a0a !important}html.theme--documenter-dark .button.is-black.is-inverted.is-outlined[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-black.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}html.theme--documenter-dark .button.is-light{background-color:#ecf0f1;border-color:transparent;color:rgba(0,0,0,0.7)}html.theme--documenter-dark .button.is-light:hover,html.theme--documenter-dark .button.is-light.is-hovered{background-color:#e5eaec;border-color:transparent;color:rgba(0,0,0,0.7)}html.theme--documenter-dark .button.is-light:focus,html.theme--documenter-dark .button.is-light.is-focused{border-color:transparent;color:rgba(0,0,0,0.7)}html.theme--documenter-dark .button.is-light:focus:not(:active),html.theme--documenter-dark .button.is-light.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(236,240,241,0.25)}html.theme--documenter-dark .button.is-light:active,html.theme--documenter-dark .button.is-light.is-active{background-color:#dde4e6;border-color:transparent;color:rgba(0,0,0,0.7)}html.theme--documenter-dark .button.is-light[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-light{background-color:#ecf0f1;border-color:#ecf0f1;box-shadow:none}html.theme--documenter-dark .button.is-light.is-inverted{background-color:rgba(0,0,0,0.7);color:#ecf0f1}html.theme--documenter-dark .button.is-light.is-inverted:hover,html.theme--documenter-dark .button.is-light.is-inverted.is-hovered{background-color:rgba(0,0,0,0.7)}html.theme--documenter-dark .button.is-light.is-inverted[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-light.is-inverted{background-color:rgba(0,0,0,0.7);border-color:transparent;box-shadow:none;color:#ecf0f1}html.theme--documenter-dark .button.is-light.is-loading::after{border-color:transparent transparent rgba(0,0,0,0.7) rgba(0,0,0,0.7) !important}html.theme--documenter-dark .button.is-light.is-outlined{background-color:transparent;border-color:#ecf0f1;color:#ecf0f1}html.theme--documenter-dark .button.is-light.is-outlined:hover,html.theme--documenter-dark .button.is-light.is-outlined.is-hovered,html.theme--documenter-dark .button.is-light.is-outlined:focus,html.theme--documenter-dark .button.is-light.is-outlined.is-focused{background-color:#ecf0f1;border-color:#ecf0f1;color:rgba(0,0,0,0.7)}html.theme--documenter-dark .button.is-light.is-outlined.is-loading::after{border-color:transparent transparent #ecf0f1 #ecf0f1 !important}html.theme--documenter-dark .button.is-light.is-outlined.is-loading:hover::after,html.theme--documenter-dark .button.is-light.is-outlined.is-loading.is-hovered::after,html.theme--documenter-dark .button.is-light.is-outlined.is-loading:focus::after,html.theme--documenter-dark .button.is-light.is-outlined.is-loading.is-focused::after{border-color:transparent transparent rgba(0,0,0,0.7) rgba(0,0,0,0.7) !important}html.theme--documenter-dark .button.is-light.is-outlined[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-light.is-outlined{background-color:transparent;border-color:#ecf0f1;box-shadow:none;color:#ecf0f1}html.theme--documenter-dark .button.is-light.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,0.7);color:rgba(0,0,0,0.7)}html.theme--documenter-dark .button.is-light.is-inverted.is-outlined:hover,html.theme--documenter-dark .button.is-light.is-inverted.is-outlined.is-hovered,html.theme--documenter-dark .button.is-light.is-inverted.is-outlined:focus,html.theme--documenter-dark .button.is-light.is-inverted.is-outlined.is-focused{background-color:rgba(0,0,0,0.7);color:#ecf0f1}html.theme--documenter-dark .button.is-light.is-inverted.is-outlined.is-loading:hover::after,html.theme--documenter-dark .button.is-light.is-inverted.is-outlined.is-loading.is-hovered::after,html.theme--documenter-dark .button.is-light.is-inverted.is-outlined.is-loading:focus::after,html.theme--documenter-dark .button.is-light.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #ecf0f1 #ecf0f1 !important}html.theme--documenter-dark .button.is-light.is-inverted.is-outlined[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-light.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,0.7);box-shadow:none;color:rgba(0,0,0,0.7)}html.theme--documenter-dark .button.is-dark,html.theme--documenter-dark .content kbd.button{background-color:#282f2f;border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-dark:hover,html.theme--documenter-dark .content kbd.button:hover,html.theme--documenter-dark .button.is-dark.is-hovered,html.theme--documenter-dark .content kbd.button.is-hovered{background-color:#232829;border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-dark:focus,html.theme--documenter-dark .content kbd.button:focus,html.theme--documenter-dark .button.is-dark.is-focused,html.theme--documenter-dark .content kbd.button.is-focused{border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-dark:focus:not(:active),html.theme--documenter-dark .content kbd.button:focus:not(:active),html.theme--documenter-dark .button.is-dark.is-focused:not(:active),html.theme--documenter-dark .content kbd.button.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(40,47,47,0.25)}html.theme--documenter-dark .button.is-dark:active,html.theme--documenter-dark .content kbd.button:active,html.theme--documenter-dark .button.is-dark.is-active,html.theme--documenter-dark .content kbd.button.is-active{background-color:#1d2122;border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-dark[disabled],html.theme--documenter-dark .content kbd.button[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-dark,fieldset[disabled] html.theme--documenter-dark .content kbd.button{background-color:#282f2f;border-color:#282f2f;box-shadow:none}html.theme--documenter-dark .button.is-dark.is-inverted,html.theme--documenter-dark .content kbd.button.is-inverted{background-color:#fff;color:#282f2f}html.theme--documenter-dark .button.is-dark.is-inverted:hover,html.theme--documenter-dark .content kbd.button.is-inverted:hover,html.theme--documenter-dark .button.is-dark.is-inverted.is-hovered,html.theme--documenter-dark .content kbd.button.is-inverted.is-hovered{background-color:#f2f2f2}html.theme--documenter-dark .button.is-dark.is-inverted[disabled],html.theme--documenter-dark .content kbd.button.is-inverted[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-dark.is-inverted,fieldset[disabled] html.theme--documenter-dark .content kbd.button.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#282f2f}html.theme--documenter-dark .button.is-dark.is-loading::after,html.theme--documenter-dark .content kbd.button.is-loading::after{border-color:transparent transparent #fff #fff !important}html.theme--documenter-dark .button.is-dark.is-outlined,html.theme--documenter-dark .content kbd.button.is-outlined{background-color:transparent;border-color:#282f2f;color:#282f2f}html.theme--documenter-dark .button.is-dark.is-outlined:hover,html.theme--documenter-dark .content kbd.button.is-outlined:hover,html.theme--documenter-dark .button.is-dark.is-outlined.is-hovered,html.theme--documenter-dark .content kbd.button.is-outlined.is-hovered,html.theme--documenter-dark .button.is-dark.is-outlined:focus,html.theme--documenter-dark .content kbd.button.is-outlined:focus,html.theme--documenter-dark .button.is-dark.is-outlined.is-focused,html.theme--documenter-dark .content kbd.button.is-outlined.is-focused{background-color:#282f2f;border-color:#282f2f;color:#fff}html.theme--documenter-dark .button.is-dark.is-outlined.is-loading::after,html.theme--documenter-dark .content kbd.button.is-outlined.is-loading::after{border-color:transparent transparent #282f2f #282f2f !important}html.theme--documenter-dark .button.is-dark.is-outlined.is-loading:hover::after,html.theme--documenter-dark .content kbd.button.is-outlined.is-loading:hover::after,html.theme--documenter-dark .button.is-dark.is-outlined.is-loading.is-hovered::after,html.theme--documenter-dark .content kbd.button.is-outlined.is-loading.is-hovered::after,html.theme--documenter-dark .button.is-dark.is-outlined.is-loading:focus::after,html.theme--documenter-dark .content kbd.button.is-outlined.is-loading:focus::after,html.theme--documenter-dark .button.is-dark.is-outlined.is-loading.is-focused::after,html.theme--documenter-dark .content kbd.button.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #fff #fff !important}html.theme--documenter-dark .button.is-dark.is-outlined[disabled],html.theme--documenter-dark .content kbd.button.is-outlined[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-dark.is-outlined,fieldset[disabled] html.theme--documenter-dark .content kbd.button.is-outlined{background-color:transparent;border-color:#282f2f;box-shadow:none;color:#282f2f}html.theme--documenter-dark .button.is-dark.is-inverted.is-outlined,html.theme--documenter-dark .content kbd.button.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}html.theme--documenter-dark .button.is-dark.is-inverted.is-outlined:hover,html.theme--documenter-dark .content kbd.button.is-inverted.is-outlined:hover,html.theme--documenter-dark .button.is-dark.is-inverted.is-outlined.is-hovered,html.theme--documenter-dark .content kbd.button.is-inverted.is-outlined.is-hovered,html.theme--documenter-dark .button.is-dark.is-inverted.is-outlined:focus,html.theme--documenter-dark .content kbd.button.is-inverted.is-outlined:focus,html.theme--documenter-dark .button.is-dark.is-inverted.is-outlined.is-focused,html.theme--documenter-dark .content kbd.button.is-inverted.is-outlined.is-focused{background-color:#fff;color:#282f2f}html.theme--documenter-dark .button.is-dark.is-inverted.is-outlined.is-loading:hover::after,html.theme--documenter-dark .content kbd.button.is-inverted.is-outlined.is-loading:hover::after,html.theme--documenter-dark .button.is-dark.is-inverted.is-outlined.is-loading.is-hovered::after,html.theme--documenter-dark .content kbd.button.is-inverted.is-outlined.is-loading.is-hovered::after,html.theme--documenter-dark .button.is-dark.is-inverted.is-outlined.is-loading:focus::after,html.theme--documenter-dark .content kbd.button.is-inverted.is-outlined.is-loading:focus::after,html.theme--documenter-dark .button.is-dark.is-inverted.is-outlined.is-loading.is-focused::after,html.theme--documenter-dark .content kbd.button.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #282f2f #282f2f !important}html.theme--documenter-dark .button.is-dark.is-inverted.is-outlined[disabled],html.theme--documenter-dark .content kbd.button.is-inverted.is-outlined[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-dark.is-inverted.is-outlined,fieldset[disabled] html.theme--documenter-dark .content kbd.button.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}html.theme--documenter-dark .button.is-primary,html.theme--documenter-dark .docstring>section>a.button.docs-sourcelink{background-color:#375a7f;border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-primary:hover,html.theme--documenter-dark .docstring>section>a.button.docs-sourcelink:hover,html.theme--documenter-dark .button.is-primary.is-hovered,html.theme--documenter-dark .docstring>section>a.button.is-hovered.docs-sourcelink{background-color:#335476;border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-primary:focus,html.theme--documenter-dark .docstring>section>a.button.docs-sourcelink:focus,html.theme--documenter-dark .button.is-primary.is-focused,html.theme--documenter-dark .docstring>section>a.button.is-focused.docs-sourcelink{border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-primary:focus:not(:active),html.theme--documenter-dark .docstring>section>a.button.docs-sourcelink:focus:not(:active),html.theme--documenter-dark .button.is-primary.is-focused:not(:active),html.theme--documenter-dark .docstring>section>a.button.is-focused.docs-sourcelink:not(:active){box-shadow:0 0 0 0.125em rgba(55,90,127,0.25)}html.theme--documenter-dark .button.is-primary:active,html.theme--documenter-dark .docstring>section>a.button.docs-sourcelink:active,html.theme--documenter-dark .button.is-primary.is-active,html.theme--documenter-dark .docstring>section>a.button.is-active.docs-sourcelink{background-color:#2f4d6d;border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-primary[disabled],html.theme--documenter-dark .docstring>section>a.button.docs-sourcelink[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-primary,fieldset[disabled] html.theme--documenter-dark .docstring>section>a.button.docs-sourcelink{background-color:#375a7f;border-color:#375a7f;box-shadow:none}html.theme--documenter-dark .button.is-primary.is-inverted,html.theme--documenter-dark .docstring>section>a.button.is-inverted.docs-sourcelink{background-color:#fff;color:#375a7f}html.theme--documenter-dark .button.is-primary.is-inverted:hover,html.theme--documenter-dark .docstring>section>a.button.is-inverted.docs-sourcelink:hover,html.theme--documenter-dark .button.is-primary.is-inverted.is-hovered,html.theme--documenter-dark .docstring>section>a.button.is-inverted.is-hovered.docs-sourcelink{background-color:#f2f2f2}html.theme--documenter-dark .button.is-primary.is-inverted[disabled],html.theme--documenter-dark .docstring>section>a.button.is-inverted.docs-sourcelink[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-primary.is-inverted,fieldset[disabled] html.theme--documenter-dark .docstring>section>a.button.is-inverted.docs-sourcelink{background-color:#fff;border-color:transparent;box-shadow:none;color:#375a7f}html.theme--documenter-dark .button.is-primary.is-loading::after,html.theme--documenter-dark .docstring>section>a.button.is-loading.docs-sourcelink::after{border-color:transparent transparent #fff #fff !important}html.theme--documenter-dark .button.is-primary.is-outlined,html.theme--documenter-dark .docstring>section>a.button.is-outlined.docs-sourcelink{background-color:transparent;border-color:#375a7f;color:#375a7f}html.theme--documenter-dark .button.is-primary.is-outlined:hover,html.theme--documenter-dark .docstring>section>a.button.is-outlined.docs-sourcelink:hover,html.theme--documenter-dark .button.is-primary.is-outlined.is-hovered,html.theme--documenter-dark .docstring>section>a.button.is-outlined.is-hovered.docs-sourcelink,html.theme--documenter-dark .button.is-primary.is-outlined:focus,html.theme--documenter-dark .docstring>section>a.button.is-outlined.docs-sourcelink:focus,html.theme--documenter-dark .button.is-primary.is-outlined.is-focused,html.theme--documenter-dark .docstring>section>a.button.is-outlined.is-focused.docs-sourcelink{background-color:#375a7f;border-color:#375a7f;color:#fff}html.theme--documenter-dark .button.is-primary.is-outlined.is-loading::after,html.theme--documenter-dark .docstring>section>a.button.is-outlined.is-loading.docs-sourcelink::after{border-color:transparent transparent #375a7f #375a7f !important}html.theme--documenter-dark .button.is-primary.is-outlined.is-loading:hover::after,html.theme--documenter-dark .docstring>section>a.button.is-outlined.is-loading.docs-sourcelink:hover::after,html.theme--documenter-dark .button.is-primary.is-outlined.is-loading.is-hovered::after,html.theme--documenter-dark .docstring>section>a.button.is-outlined.is-loading.is-hovered.docs-sourcelink::after,html.theme--documenter-dark .button.is-primary.is-outlined.is-loading:focus::after,html.theme--documenter-dark .docstring>section>a.button.is-outlined.is-loading.docs-sourcelink:focus::after,html.theme--documenter-dark .button.is-primary.is-outlined.is-loading.is-focused::after,html.theme--documenter-dark .docstring>section>a.button.is-outlined.is-loading.is-focused.docs-sourcelink::after{border-color:transparent transparent #fff #fff !important}html.theme--documenter-dark .button.is-primary.is-outlined[disabled],html.theme--documenter-dark .docstring>section>a.button.is-outlined.docs-sourcelink[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-primary.is-outlined,fieldset[disabled] html.theme--documenter-dark .docstring>section>a.button.is-outlined.docs-sourcelink{background-color:transparent;border-color:#375a7f;box-shadow:none;color:#375a7f}html.theme--documenter-dark .button.is-primary.is-inverted.is-outlined,html.theme--documenter-dark .docstring>section>a.button.is-inverted.is-outlined.docs-sourcelink{background-color:transparent;border-color:#fff;color:#fff}html.theme--documenter-dark .button.is-primary.is-inverted.is-outlined:hover,html.theme--documenter-dark .docstring>section>a.button.is-inverted.is-outlined.docs-sourcelink:hover,html.theme--documenter-dark .button.is-primary.is-inverted.is-outlined.is-hovered,html.theme--documenter-dark .docstring>section>a.button.is-inverted.is-outlined.is-hovered.docs-sourcelink,html.theme--documenter-dark .button.is-primary.is-inverted.is-outlined:focus,html.theme--documenter-dark .docstring>section>a.button.is-inverted.is-outlined.docs-sourcelink:focus,html.theme--documenter-dark .button.is-primary.is-inverted.is-outlined.is-focused,html.theme--documenter-dark .docstring>section>a.button.is-inverted.is-outlined.is-focused.docs-sourcelink{background-color:#fff;color:#375a7f}html.theme--documenter-dark .button.is-primary.is-inverted.is-outlined.is-loading:hover::after,html.theme--documenter-dark .docstring>section>a.button.is-inverted.is-outlined.is-loading.docs-sourcelink:hover::after,html.theme--documenter-dark .button.is-primary.is-inverted.is-outlined.is-loading.is-hovered::after,html.theme--documenter-dark .docstring>section>a.button.is-inverted.is-outlined.is-loading.is-hovered.docs-sourcelink::after,html.theme--documenter-dark .button.is-primary.is-inverted.is-outlined.is-loading:focus::after,html.theme--documenter-dark .docstring>section>a.button.is-inverted.is-outlined.is-loading.docs-sourcelink:focus::after,html.theme--documenter-dark .button.is-primary.is-inverted.is-outlined.is-loading.is-focused::after,html.theme--documenter-dark .docstring>section>a.button.is-inverted.is-outlined.is-loading.is-focused.docs-sourcelink::after{border-color:transparent transparent #375a7f #375a7f !important}html.theme--documenter-dark .button.is-primary.is-inverted.is-outlined[disabled],html.theme--documenter-dark .docstring>section>a.button.is-inverted.is-outlined.docs-sourcelink[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-primary.is-inverted.is-outlined,fieldset[disabled] html.theme--documenter-dark .docstring>section>a.button.is-inverted.is-outlined.docs-sourcelink{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}html.theme--documenter-dark .button.is-primary.is-light,html.theme--documenter-dark .docstring>section>a.button.is-light.docs-sourcelink{background-color:#f1f5f9;color:#4d7eb2}html.theme--documenter-dark .button.is-primary.is-light:hover,html.theme--documenter-dark .docstring>section>a.button.is-light.docs-sourcelink:hover,html.theme--documenter-dark .button.is-primary.is-light.is-hovered,html.theme--documenter-dark .docstring>section>a.button.is-light.is-hovered.docs-sourcelink{background-color:#e8eef5;border-color:transparent;color:#4d7eb2}html.theme--documenter-dark .button.is-primary.is-light:active,html.theme--documenter-dark .docstring>section>a.button.is-light.docs-sourcelink:active,html.theme--documenter-dark .button.is-primary.is-light.is-active,html.theme--documenter-dark .docstring>section>a.button.is-light.is-active.docs-sourcelink{background-color:#dfe8f1;border-color:transparent;color:#4d7eb2}html.theme--documenter-dark .button.is-link{background-color:#1abc9c;border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-link:hover,html.theme--documenter-dark .button.is-link.is-hovered{background-color:#18b193;border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-link:focus,html.theme--documenter-dark .button.is-link.is-focused{border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-link:focus:not(:active),html.theme--documenter-dark .button.is-link.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(26,188,156,0.25)}html.theme--documenter-dark .button.is-link:active,html.theme--documenter-dark .button.is-link.is-active{background-color:#17a689;border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-link[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-link{background-color:#1abc9c;border-color:#1abc9c;box-shadow:none}html.theme--documenter-dark .button.is-link.is-inverted{background-color:#fff;color:#1abc9c}html.theme--documenter-dark .button.is-link.is-inverted:hover,html.theme--documenter-dark .button.is-link.is-inverted.is-hovered{background-color:#f2f2f2}html.theme--documenter-dark .button.is-link.is-inverted[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-link.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#1abc9c}html.theme--documenter-dark .button.is-link.is-loading::after{border-color:transparent transparent #fff #fff !important}html.theme--documenter-dark .button.is-link.is-outlined{background-color:transparent;border-color:#1abc9c;color:#1abc9c}html.theme--documenter-dark .button.is-link.is-outlined:hover,html.theme--documenter-dark .button.is-link.is-outlined.is-hovered,html.theme--documenter-dark .button.is-link.is-outlined:focus,html.theme--documenter-dark .button.is-link.is-outlined.is-focused{background-color:#1abc9c;border-color:#1abc9c;color:#fff}html.theme--documenter-dark .button.is-link.is-outlined.is-loading::after{border-color:transparent transparent #1abc9c #1abc9c !important}html.theme--documenter-dark .button.is-link.is-outlined.is-loading:hover::after,html.theme--documenter-dark .button.is-link.is-outlined.is-loading.is-hovered::after,html.theme--documenter-dark .button.is-link.is-outlined.is-loading:focus::after,html.theme--documenter-dark .button.is-link.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #fff #fff !important}html.theme--documenter-dark .button.is-link.is-outlined[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-link.is-outlined{background-color:transparent;border-color:#1abc9c;box-shadow:none;color:#1abc9c}html.theme--documenter-dark .button.is-link.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}html.theme--documenter-dark .button.is-link.is-inverted.is-outlined:hover,html.theme--documenter-dark .button.is-link.is-inverted.is-outlined.is-hovered,html.theme--documenter-dark .button.is-link.is-inverted.is-outlined:focus,html.theme--documenter-dark .button.is-link.is-inverted.is-outlined.is-focused{background-color:#fff;color:#1abc9c}html.theme--documenter-dark .button.is-link.is-inverted.is-outlined.is-loading:hover::after,html.theme--documenter-dark .button.is-link.is-inverted.is-outlined.is-loading.is-hovered::after,html.theme--documenter-dark .button.is-link.is-inverted.is-outlined.is-loading:focus::after,html.theme--documenter-dark .button.is-link.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #1abc9c #1abc9c !important}html.theme--documenter-dark .button.is-link.is-inverted.is-outlined[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-link.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}html.theme--documenter-dark .button.is-link.is-light{background-color:#edfdf9;color:#15987e}html.theme--documenter-dark .button.is-link.is-light:hover,html.theme--documenter-dark .button.is-link.is-light.is-hovered{background-color:#e2fbf6;border-color:transparent;color:#15987e}html.theme--documenter-dark .button.is-link.is-light:active,html.theme--documenter-dark .button.is-link.is-light.is-active{background-color:#d7f9f3;border-color:transparent;color:#15987e}html.theme--documenter-dark .button.is-info{background-color:#024c7d;border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-info:hover,html.theme--documenter-dark .button.is-info.is-hovered{background-color:#024470;border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-info:focus,html.theme--documenter-dark .button.is-info.is-focused{border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-info:focus:not(:active),html.theme--documenter-dark .button.is-info.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(2,76,125,0.25)}html.theme--documenter-dark .button.is-info:active,html.theme--documenter-dark .button.is-info.is-active{background-color:#023d64;border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-info[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-info{background-color:#024c7d;border-color:#024c7d;box-shadow:none}html.theme--documenter-dark .button.is-info.is-inverted{background-color:#fff;color:#024c7d}html.theme--documenter-dark .button.is-info.is-inverted:hover,html.theme--documenter-dark .button.is-info.is-inverted.is-hovered{background-color:#f2f2f2}html.theme--documenter-dark .button.is-info.is-inverted[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-info.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#024c7d}html.theme--documenter-dark .button.is-info.is-loading::after{border-color:transparent transparent #fff #fff !important}html.theme--documenter-dark .button.is-info.is-outlined{background-color:transparent;border-color:#024c7d;color:#024c7d}html.theme--documenter-dark .button.is-info.is-outlined:hover,html.theme--documenter-dark .button.is-info.is-outlined.is-hovered,html.theme--documenter-dark .button.is-info.is-outlined:focus,html.theme--documenter-dark .button.is-info.is-outlined.is-focused{background-color:#024c7d;border-color:#024c7d;color:#fff}html.theme--documenter-dark .button.is-info.is-outlined.is-loading::after{border-color:transparent transparent #024c7d #024c7d !important}html.theme--documenter-dark .button.is-info.is-outlined.is-loading:hover::after,html.theme--documenter-dark .button.is-info.is-outlined.is-loading.is-hovered::after,html.theme--documenter-dark .button.is-info.is-outlined.is-loading:focus::after,html.theme--documenter-dark .button.is-info.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #fff #fff !important}html.theme--documenter-dark .button.is-info.is-outlined[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-info.is-outlined{background-color:transparent;border-color:#024c7d;box-shadow:none;color:#024c7d}html.theme--documenter-dark .button.is-info.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}html.theme--documenter-dark .button.is-info.is-inverted.is-outlined:hover,html.theme--documenter-dark .button.is-info.is-inverted.is-outlined.is-hovered,html.theme--documenter-dark .button.is-info.is-inverted.is-outlined:focus,html.theme--documenter-dark .button.is-info.is-inverted.is-outlined.is-focused{background-color:#fff;color:#024c7d}html.theme--documenter-dark .button.is-info.is-inverted.is-outlined.is-loading:hover::after,html.theme--documenter-dark .button.is-info.is-inverted.is-outlined.is-loading.is-hovered::after,html.theme--documenter-dark .button.is-info.is-inverted.is-outlined.is-loading:focus::after,html.theme--documenter-dark .button.is-info.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #024c7d #024c7d !important}html.theme--documenter-dark .button.is-info.is-inverted.is-outlined[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-info.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}html.theme--documenter-dark .button.is-info.is-light{background-color:#ebf7ff;color:#0e9dfb}html.theme--documenter-dark .button.is-info.is-light:hover,html.theme--documenter-dark .button.is-info.is-light.is-hovered{background-color:#def2fe;border-color:transparent;color:#0e9dfb}html.theme--documenter-dark .button.is-info.is-light:active,html.theme--documenter-dark .button.is-info.is-light.is-active{background-color:#d2edfe;border-color:transparent;color:#0e9dfb}html.theme--documenter-dark .button.is-success{background-color:#008438;border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-success:hover,html.theme--documenter-dark .button.is-success.is-hovered{background-color:#073;border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-success:focus,html.theme--documenter-dark .button.is-success.is-focused{border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-success:focus:not(:active),html.theme--documenter-dark .button.is-success.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(0,132,56,0.25)}html.theme--documenter-dark .button.is-success:active,html.theme--documenter-dark .button.is-success.is-active{background-color:#006b2d;border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-success[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-success{background-color:#008438;border-color:#008438;box-shadow:none}html.theme--documenter-dark .button.is-success.is-inverted{background-color:#fff;color:#008438}html.theme--documenter-dark .button.is-success.is-inverted:hover,html.theme--documenter-dark .button.is-success.is-inverted.is-hovered{background-color:#f2f2f2}html.theme--documenter-dark .button.is-success.is-inverted[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-success.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#008438}html.theme--documenter-dark .button.is-success.is-loading::after{border-color:transparent transparent #fff #fff !important}html.theme--documenter-dark .button.is-success.is-outlined{background-color:transparent;border-color:#008438;color:#008438}html.theme--documenter-dark .button.is-success.is-outlined:hover,html.theme--documenter-dark .button.is-success.is-outlined.is-hovered,html.theme--documenter-dark .button.is-success.is-outlined:focus,html.theme--documenter-dark .button.is-success.is-outlined.is-focused{background-color:#008438;border-color:#008438;color:#fff}html.theme--documenter-dark .button.is-success.is-outlined.is-loading::after{border-color:transparent transparent #008438 #008438 !important}html.theme--documenter-dark .button.is-success.is-outlined.is-loading:hover::after,html.theme--documenter-dark .button.is-success.is-outlined.is-loading.is-hovered::after,html.theme--documenter-dark .button.is-success.is-outlined.is-loading:focus::after,html.theme--documenter-dark .button.is-success.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #fff #fff !important}html.theme--documenter-dark .button.is-success.is-outlined[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-success.is-outlined{background-color:transparent;border-color:#008438;box-shadow:none;color:#008438}html.theme--documenter-dark .button.is-success.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}html.theme--documenter-dark .button.is-success.is-inverted.is-outlined:hover,html.theme--documenter-dark .button.is-success.is-inverted.is-outlined.is-hovered,html.theme--documenter-dark .button.is-success.is-inverted.is-outlined:focus,html.theme--documenter-dark .button.is-success.is-inverted.is-outlined.is-focused{background-color:#fff;color:#008438}html.theme--documenter-dark .button.is-success.is-inverted.is-outlined.is-loading:hover::after,html.theme--documenter-dark .button.is-success.is-inverted.is-outlined.is-loading.is-hovered::after,html.theme--documenter-dark .button.is-success.is-inverted.is-outlined.is-loading:focus::after,html.theme--documenter-dark .button.is-success.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #008438 #008438 !important}html.theme--documenter-dark .button.is-success.is-inverted.is-outlined[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-success.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}html.theme--documenter-dark .button.is-success.is-light{background-color:#ebfff3;color:#00eb64}html.theme--documenter-dark .button.is-success.is-light:hover,html.theme--documenter-dark .button.is-success.is-light.is-hovered{background-color:#deffec;border-color:transparent;color:#00eb64}html.theme--documenter-dark .button.is-success.is-light:active,html.theme--documenter-dark .button.is-success.is-light.is-active{background-color:#d1ffe5;border-color:transparent;color:#00eb64}html.theme--documenter-dark .button.is-warning{background-color:#ad8100;border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-warning:hover,html.theme--documenter-dark .button.is-warning.is-hovered{background-color:#a07700;border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-warning:focus,html.theme--documenter-dark .button.is-warning.is-focused{border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-warning:focus:not(:active),html.theme--documenter-dark .button.is-warning.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(173,129,0,0.25)}html.theme--documenter-dark .button.is-warning:active,html.theme--documenter-dark .button.is-warning.is-active{background-color:#946e00;border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-warning[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-warning{background-color:#ad8100;border-color:#ad8100;box-shadow:none}html.theme--documenter-dark .button.is-warning.is-inverted{background-color:#fff;color:#ad8100}html.theme--documenter-dark .button.is-warning.is-inverted:hover,html.theme--documenter-dark .button.is-warning.is-inverted.is-hovered{background-color:#f2f2f2}html.theme--documenter-dark .button.is-warning.is-inverted[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-warning.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#ad8100}html.theme--documenter-dark .button.is-warning.is-loading::after{border-color:transparent transparent #fff #fff !important}html.theme--documenter-dark .button.is-warning.is-outlined{background-color:transparent;border-color:#ad8100;color:#ad8100}html.theme--documenter-dark .button.is-warning.is-outlined:hover,html.theme--documenter-dark .button.is-warning.is-outlined.is-hovered,html.theme--documenter-dark .button.is-warning.is-outlined:focus,html.theme--documenter-dark .button.is-warning.is-outlined.is-focused{background-color:#ad8100;border-color:#ad8100;color:#fff}html.theme--documenter-dark .button.is-warning.is-outlined.is-loading::after{border-color:transparent transparent #ad8100 #ad8100 !important}html.theme--documenter-dark .button.is-warning.is-outlined.is-loading:hover::after,html.theme--documenter-dark .button.is-warning.is-outlined.is-loading.is-hovered::after,html.theme--documenter-dark .button.is-warning.is-outlined.is-loading:focus::after,html.theme--documenter-dark .button.is-warning.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #fff #fff !important}html.theme--documenter-dark .button.is-warning.is-outlined[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-warning.is-outlined{background-color:transparent;border-color:#ad8100;box-shadow:none;color:#ad8100}html.theme--documenter-dark .button.is-warning.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}html.theme--documenter-dark .button.is-warning.is-inverted.is-outlined:hover,html.theme--documenter-dark .button.is-warning.is-inverted.is-outlined.is-hovered,html.theme--documenter-dark .button.is-warning.is-inverted.is-outlined:focus,html.theme--documenter-dark .button.is-warning.is-inverted.is-outlined.is-focused{background-color:#fff;color:#ad8100}html.theme--documenter-dark .button.is-warning.is-inverted.is-outlined.is-loading:hover::after,html.theme--documenter-dark .button.is-warning.is-inverted.is-outlined.is-loading.is-hovered::after,html.theme--documenter-dark .button.is-warning.is-inverted.is-outlined.is-loading:focus::after,html.theme--documenter-dark .button.is-warning.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #ad8100 #ad8100 !important}html.theme--documenter-dark .button.is-warning.is-inverted.is-outlined[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-warning.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}html.theme--documenter-dark .button.is-warning.is-light{background-color:#fffaeb;color:#d19c00}html.theme--documenter-dark .button.is-warning.is-light:hover,html.theme--documenter-dark .button.is-warning.is-light.is-hovered{background-color:#fff7de;border-color:transparent;color:#d19c00}html.theme--documenter-dark .button.is-warning.is-light:active,html.theme--documenter-dark .button.is-warning.is-light.is-active{background-color:#fff3d1;border-color:transparent;color:#d19c00}html.theme--documenter-dark .button.is-danger{background-color:#9e1b0d;border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-danger:hover,html.theme--documenter-dark .button.is-danger.is-hovered{background-color:#92190c;border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-danger:focus,html.theme--documenter-dark .button.is-danger.is-focused{border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-danger:focus:not(:active),html.theme--documenter-dark .button.is-danger.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(158,27,13,0.25)}html.theme--documenter-dark .button.is-danger:active,html.theme--documenter-dark .button.is-danger.is-active{background-color:#86170b;border-color:transparent;color:#fff}html.theme--documenter-dark .button.is-danger[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-danger{background-color:#9e1b0d;border-color:#9e1b0d;box-shadow:none}html.theme--documenter-dark .button.is-danger.is-inverted{background-color:#fff;color:#9e1b0d}html.theme--documenter-dark .button.is-danger.is-inverted:hover,html.theme--documenter-dark .button.is-danger.is-inverted.is-hovered{background-color:#f2f2f2}html.theme--documenter-dark .button.is-danger.is-inverted[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-danger.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#9e1b0d}html.theme--documenter-dark .button.is-danger.is-loading::after{border-color:transparent transparent #fff #fff !important}html.theme--documenter-dark .button.is-danger.is-outlined{background-color:transparent;border-color:#9e1b0d;color:#9e1b0d}html.theme--documenter-dark .button.is-danger.is-outlined:hover,html.theme--documenter-dark .button.is-danger.is-outlined.is-hovered,html.theme--documenter-dark .button.is-danger.is-outlined:focus,html.theme--documenter-dark .button.is-danger.is-outlined.is-focused{background-color:#9e1b0d;border-color:#9e1b0d;color:#fff}html.theme--documenter-dark .button.is-danger.is-outlined.is-loading::after{border-color:transparent transparent #9e1b0d #9e1b0d !important}html.theme--documenter-dark .button.is-danger.is-outlined.is-loading:hover::after,html.theme--documenter-dark .button.is-danger.is-outlined.is-loading.is-hovered::after,html.theme--documenter-dark .button.is-danger.is-outlined.is-loading:focus::after,html.theme--documenter-dark .button.is-danger.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #fff #fff !important}html.theme--documenter-dark .button.is-danger.is-outlined[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-danger.is-outlined{background-color:transparent;border-color:#9e1b0d;box-shadow:none;color:#9e1b0d}html.theme--documenter-dark .button.is-danger.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}html.theme--documenter-dark .button.is-danger.is-inverted.is-outlined:hover,html.theme--documenter-dark .button.is-danger.is-inverted.is-outlined.is-hovered,html.theme--documenter-dark .button.is-danger.is-inverted.is-outlined:focus,html.theme--documenter-dark .button.is-danger.is-inverted.is-outlined.is-focused{background-color:#fff;color:#9e1b0d}html.theme--documenter-dark .button.is-danger.is-inverted.is-outlined.is-loading:hover::after,html.theme--documenter-dark .button.is-danger.is-inverted.is-outlined.is-loading.is-hovered::after,html.theme--documenter-dark .button.is-danger.is-inverted.is-outlined.is-loading:focus::after,html.theme--documenter-dark .button.is-danger.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #9e1b0d #9e1b0d !important}html.theme--documenter-dark .button.is-danger.is-inverted.is-outlined[disabled],fieldset[disabled] html.theme--documenter-dark .button.is-danger.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}html.theme--documenter-dark .button.is-danger.is-light{background-color:#fdeeec;color:#ec311d}html.theme--documenter-dark .button.is-danger.is-light:hover,html.theme--documenter-dark .button.is-danger.is-light.is-hovered{background-color:#fce3e0;border-color:transparent;color:#ec311d}html.theme--documenter-dark .button.is-danger.is-light:active,html.theme--documenter-dark .button.is-danger.is-light.is-active{background-color:#fcd8d5;border-color:transparent;color:#ec311d}html.theme--documenter-dark .button.is-small,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.button{font-size:.75rem}html.theme--documenter-dark .button.is-small:not(.is-rounded),html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.button:not(.is-rounded){border-radius:3px}html.theme--documenter-dark .button.is-normal{font-size:1rem}html.theme--documenter-dark .button.is-medium{font-size:1.25rem}html.theme--documenter-dark .button.is-large{font-size:1.5rem}html.theme--documenter-dark .button[disabled],fieldset[disabled] html.theme--documenter-dark .button{background-color:#8c9b9d;border-color:#5e6d6f;box-shadow:none;opacity:.5}html.theme--documenter-dark .button.is-fullwidth{display:flex;width:100%}html.theme--documenter-dark .button.is-loading{color:transparent !important;pointer-events:none}html.theme--documenter-dark .button.is-loading::after{position:absolute;left:calc(50% - (1em * 0.5));top:calc(50% - (1em * 0.5));position:absolute !important}html.theme--documenter-dark .button.is-static{background-color:#282f2f;border-color:#5e6d6f;color:#dbdee0;box-shadow:none;pointer-events:none}html.theme--documenter-dark .button.is-rounded,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.button{border-radius:9999px;padding-left:calc(1em + 0.25em);padding-right:calc(1em + 0.25em)}html.theme--documenter-dark .buttons{align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start}html.theme--documenter-dark .buttons .button{margin-bottom:0.5rem}html.theme--documenter-dark .buttons .button:not(:last-child):not(.is-fullwidth){margin-right:.5rem}html.theme--documenter-dark .buttons:last-child{margin-bottom:-0.5rem}html.theme--documenter-dark .buttons:not(:last-child){margin-bottom:1rem}html.theme--documenter-dark .buttons.are-small .button:not(.is-normal):not(.is-medium):not(.is-large){font-size:.75rem}html.theme--documenter-dark .buttons.are-small .button:not(.is-normal):not(.is-medium):not(.is-large):not(.is-rounded){border-radius:3px}html.theme--documenter-dark .buttons.are-medium .button:not(.is-small):not(.is-normal):not(.is-large){font-size:1.25rem}html.theme--documenter-dark .buttons.are-large .button:not(.is-small):not(.is-normal):not(.is-medium){font-size:1.5rem}html.theme--documenter-dark .buttons.has-addons .button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}html.theme--documenter-dark .buttons.has-addons .button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0;margin-right:-1px}html.theme--documenter-dark .buttons.has-addons .button:last-child{margin-right:0}html.theme--documenter-dark .buttons.has-addons .button:hover,html.theme--documenter-dark .buttons.has-addons .button.is-hovered{z-index:2}html.theme--documenter-dark .buttons.has-addons .button:focus,html.theme--documenter-dark .buttons.has-addons .button.is-focused,html.theme--documenter-dark .buttons.has-addons .button:active,html.theme--documenter-dark .buttons.has-addons .button.is-active,html.theme--documenter-dark .buttons.has-addons .button.is-selected{z-index:3}html.theme--documenter-dark .buttons.has-addons .button:focus:hover,html.theme--documenter-dark .buttons.has-addons .button.is-focused:hover,html.theme--documenter-dark .buttons.has-addons .button:active:hover,html.theme--documenter-dark .buttons.has-addons .button.is-active:hover,html.theme--documenter-dark .buttons.has-addons .button.is-selected:hover{z-index:4}html.theme--documenter-dark .buttons.has-addons .button.is-expanded{flex-grow:1;flex-shrink:1}html.theme--documenter-dark .buttons.is-centered{justify-content:center}html.theme--documenter-dark .buttons.is-centered:not(.has-addons) .button:not(.is-fullwidth){margin-left:0.25rem;margin-right:0.25rem}html.theme--documenter-dark .buttons.is-right{justify-content:flex-end}html.theme--documenter-dark .buttons.is-right:not(.has-addons) .button:not(.is-fullwidth){margin-left:0.25rem;margin-right:0.25rem}@media screen and (max-width: 768px){html.theme--documenter-dark .button.is-responsive.is-small,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-responsive{font-size:.5625rem}html.theme--documenter-dark .button.is-responsive,html.theme--documenter-dark .button.is-responsive.is-normal{font-size:.65625rem}html.theme--documenter-dark .button.is-responsive.is-medium{font-size:.75rem}html.theme--documenter-dark .button.is-responsive.is-large{font-size:1rem}}@media screen and (min-width: 769px) and (max-width: 1055px){html.theme--documenter-dark .button.is-responsive.is-small,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-responsive{font-size:.65625rem}html.theme--documenter-dark .button.is-responsive,html.theme--documenter-dark .button.is-responsive.is-normal{font-size:.75rem}html.theme--documenter-dark .button.is-responsive.is-medium{font-size:1rem}html.theme--documenter-dark .button.is-responsive.is-large{font-size:1.25rem}}html.theme--documenter-dark .container{flex-grow:1;margin:0 auto;position:relative;width:auto}html.theme--documenter-dark .container.is-fluid{max-width:none !important;padding-left:32px;padding-right:32px;width:100%}@media screen and (min-width: 1056px){html.theme--documenter-dark .container{max-width:992px}}@media screen and (max-width: 1215px){html.theme--documenter-dark .container.is-widescreen:not(.is-max-desktop){max-width:1152px}}@media screen and (max-width: 1407px){html.theme--documenter-dark .container.is-fullhd:not(.is-max-desktop):not(.is-max-widescreen){max-width:1344px}}@media screen and (min-width: 1216px){html.theme--documenter-dark .container:not(.is-max-desktop){max-width:1152px}}@media screen and (min-width: 1408px){html.theme--documenter-dark .container:not(.is-max-desktop):not(.is-max-widescreen){max-width:1344px}}html.theme--documenter-dark .content li+li{margin-top:0.25em}html.theme--documenter-dark .content p:not(:last-child),html.theme--documenter-dark .content dl:not(:last-child),html.theme--documenter-dark .content ol:not(:last-child),html.theme--documenter-dark .content ul:not(:last-child),html.theme--documenter-dark .content blockquote:not(:last-child),html.theme--documenter-dark .content pre:not(:last-child),html.theme--documenter-dark .content table:not(:last-child){margin-bottom:1em}html.theme--documenter-dark .content h1,html.theme--documenter-dark .content h2,html.theme--documenter-dark .content h3,html.theme--documenter-dark .content h4,html.theme--documenter-dark .content h5,html.theme--documenter-dark .content h6{color:#f2f2f2;font-weight:600;line-height:1.125}html.theme--documenter-dark .content h1{font-size:2em;margin-bottom:0.5em}html.theme--documenter-dark .content h1:not(:first-child){margin-top:1em}html.theme--documenter-dark .content h2{font-size:1.75em;margin-bottom:0.5714em}html.theme--documenter-dark .content h2:not(:first-child){margin-top:1.1428em}html.theme--documenter-dark .content h3{font-size:1.5em;margin-bottom:0.6666em}html.theme--documenter-dark .content h3:not(:first-child){margin-top:1.3333em}html.theme--documenter-dark .content h4{font-size:1.25em;margin-bottom:0.8em}html.theme--documenter-dark .content h5{font-size:1.125em;margin-bottom:0.8888em}html.theme--documenter-dark .content h6{font-size:1em;margin-bottom:1em}html.theme--documenter-dark .content blockquote{background-color:#282f2f;border-left:5px solid #5e6d6f;padding:1.25em 1.5em}html.theme--documenter-dark .content ol{list-style-position:outside;margin-left:2em;margin-top:1em}html.theme--documenter-dark .content ol:not([type]){list-style-type:decimal}html.theme--documenter-dark .content ol.is-lower-alpha:not([type]){list-style-type:lower-alpha}html.theme--documenter-dark .content ol.is-lower-roman:not([type]){list-style-type:lower-roman}html.theme--documenter-dark .content ol.is-upper-alpha:not([type]){list-style-type:upper-alpha}html.theme--documenter-dark .content ol.is-upper-roman:not([type]){list-style-type:upper-roman}html.theme--documenter-dark .content ul{list-style:disc outside;margin-left:2em;margin-top:1em}html.theme--documenter-dark .content ul ul{list-style-type:circle;margin-top:0.5em}html.theme--documenter-dark .content ul ul ul{list-style-type:square}html.theme--documenter-dark .content dd{margin-left:2em}html.theme--documenter-dark .content figure{margin-left:2em;margin-right:2em;text-align:center}html.theme--documenter-dark .content figure:not(:first-child){margin-top:2em}html.theme--documenter-dark .content figure:not(:last-child){margin-bottom:2em}html.theme--documenter-dark .content figure img{display:inline-block}html.theme--documenter-dark .content figure figcaption{font-style:italic}html.theme--documenter-dark .content pre{-webkit-overflow-scrolling:touch;overflow-x:auto;padding:0;white-space:pre;word-wrap:normal}html.theme--documenter-dark .content sup,html.theme--documenter-dark .content sub{font-size:75%}html.theme--documenter-dark .content table{width:100%}html.theme--documenter-dark .content table td,html.theme--documenter-dark .content table th{border:1px solid #5e6d6f;border-width:0 0 1px;padding:0.5em 0.75em;vertical-align:top}html.theme--documenter-dark .content table th{color:#f2f2f2}html.theme--documenter-dark .content table th:not([align]){text-align:inherit}html.theme--documenter-dark .content table thead td,html.theme--documenter-dark .content table thead th{border-width:0 0 2px;color:#f2f2f2}html.theme--documenter-dark .content table tfoot td,html.theme--documenter-dark .content table tfoot th{border-width:2px 0 0;color:#f2f2f2}html.theme--documenter-dark .content table tbody tr:last-child td,html.theme--documenter-dark .content table tbody tr:last-child th{border-bottom-width:0}html.theme--documenter-dark .content .tabs li+li{margin-top:0}html.theme--documenter-dark .content.is-small,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.content{font-size:.75rem}html.theme--documenter-dark .content.is-normal{font-size:1rem}html.theme--documenter-dark .content.is-medium{font-size:1.25rem}html.theme--documenter-dark .content.is-large{font-size:1.5rem}html.theme--documenter-dark .icon{align-items:center;display:inline-flex;justify-content:center;height:1.5rem;width:1.5rem}html.theme--documenter-dark .icon.is-small,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.icon{height:1rem;width:1rem}html.theme--documenter-dark .icon.is-medium{height:2rem;width:2rem}html.theme--documenter-dark .icon.is-large{height:3rem;width:3rem}html.theme--documenter-dark .icon-text{align-items:flex-start;color:inherit;display:inline-flex;flex-wrap:wrap;line-height:1.5rem;vertical-align:top}html.theme--documenter-dark .icon-text .icon{flex-grow:0;flex-shrink:0}html.theme--documenter-dark .icon-text .icon:not(:last-child){margin-right:.25em}html.theme--documenter-dark .icon-text .icon:not(:first-child){margin-left:.25em}html.theme--documenter-dark div.icon-text{display:flex}html.theme--documenter-dark .image,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img{display:block;position:relative}html.theme--documenter-dark .image img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img img{display:block;height:auto;width:100%}html.theme--documenter-dark .image img.is-rounded,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img img.is-rounded{border-radius:9999px}html.theme--documenter-dark .image.is-fullwidth,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-fullwidth{width:100%}html.theme--documenter-dark .image.is-square img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-square img,html.theme--documenter-dark .image.is-square .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-square .has-ratio,html.theme--documenter-dark .image.is-1by1 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-1by1 img,html.theme--documenter-dark .image.is-1by1 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-1by1 .has-ratio,html.theme--documenter-dark .image.is-5by4 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-5by4 img,html.theme--documenter-dark .image.is-5by4 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-5by4 .has-ratio,html.theme--documenter-dark .image.is-4by3 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-4by3 img,html.theme--documenter-dark .image.is-4by3 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-4by3 .has-ratio,html.theme--documenter-dark .image.is-3by2 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-3by2 img,html.theme--documenter-dark .image.is-3by2 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-3by2 .has-ratio,html.theme--documenter-dark .image.is-5by3 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-5by3 img,html.theme--documenter-dark .image.is-5by3 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-5by3 .has-ratio,html.theme--documenter-dark .image.is-16by9 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-16by9 img,html.theme--documenter-dark .image.is-16by9 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-16by9 .has-ratio,html.theme--documenter-dark .image.is-2by1 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-2by1 img,html.theme--documenter-dark .image.is-2by1 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-2by1 .has-ratio,html.theme--documenter-dark .image.is-3by1 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-3by1 img,html.theme--documenter-dark .image.is-3by1 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-3by1 .has-ratio,html.theme--documenter-dark .image.is-4by5 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-4by5 img,html.theme--documenter-dark .image.is-4by5 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-4by5 .has-ratio,html.theme--documenter-dark .image.is-3by4 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-3by4 img,html.theme--documenter-dark .image.is-3by4 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-3by4 .has-ratio,html.theme--documenter-dark .image.is-2by3 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-2by3 img,html.theme--documenter-dark .image.is-2by3 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-2by3 .has-ratio,html.theme--documenter-dark .image.is-3by5 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-3by5 img,html.theme--documenter-dark .image.is-3by5 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-3by5 .has-ratio,html.theme--documenter-dark .image.is-9by16 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-9by16 img,html.theme--documenter-dark .image.is-9by16 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-9by16 .has-ratio,html.theme--documenter-dark .image.is-1by2 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-1by2 img,html.theme--documenter-dark .image.is-1by2 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-1by2 .has-ratio,html.theme--documenter-dark .image.is-1by3 img,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-1by3 img,html.theme--documenter-dark .image.is-1by3 .has-ratio,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-1by3 .has-ratio{height:100%;width:100%}html.theme--documenter-dark .image.is-square,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-square,html.theme--documenter-dark .image.is-1by1,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-1by1{padding-top:100%}html.theme--documenter-dark .image.is-5by4,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-5by4{padding-top:80%}html.theme--documenter-dark .image.is-4by3,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-4by3{padding-top:75%}html.theme--documenter-dark .image.is-3by2,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-3by2{padding-top:66.6666%}html.theme--documenter-dark .image.is-5by3,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-5by3{padding-top:60%}html.theme--documenter-dark .image.is-16by9,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-16by9{padding-top:56.25%}html.theme--documenter-dark .image.is-2by1,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-2by1{padding-top:50%}html.theme--documenter-dark .image.is-3by1,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-3by1{padding-top:33.3333%}html.theme--documenter-dark .image.is-4by5,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-4by5{padding-top:125%}html.theme--documenter-dark .image.is-3by4,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-3by4{padding-top:133.3333%}html.theme--documenter-dark .image.is-2by3,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-2by3{padding-top:150%}html.theme--documenter-dark .image.is-3by5,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-3by5{padding-top:166.6666%}html.theme--documenter-dark .image.is-9by16,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-9by16{padding-top:177.7777%}html.theme--documenter-dark .image.is-1by2,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-1by2{padding-top:200%}html.theme--documenter-dark .image.is-1by3,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-1by3{padding-top:300%}html.theme--documenter-dark .image.is-16x16,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-16x16{height:16px;width:16px}html.theme--documenter-dark .image.is-24x24,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-24x24{height:24px;width:24px}html.theme--documenter-dark .image.is-32x32,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-32x32{height:32px;width:32px}html.theme--documenter-dark .image.is-48x48,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-48x48{height:48px;width:48px}html.theme--documenter-dark .image.is-64x64,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-64x64{height:64px;width:64px}html.theme--documenter-dark .image.is-96x96,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-96x96{height:96px;width:96px}html.theme--documenter-dark .image.is-128x128,html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img.is-128x128{height:128px;width:128px}html.theme--documenter-dark .notification{background-color:#282f2f;border-radius:.4em;position:relative;padding:1.25rem 2.5rem 1.25rem 1.5rem}html.theme--documenter-dark .notification a:not(.button):not(.dropdown-item){color:currentColor;text-decoration:underline}html.theme--documenter-dark .notification strong{color:currentColor}html.theme--documenter-dark .notification code,html.theme--documenter-dark .notification pre{background:#fff}html.theme--documenter-dark .notification pre code{background:transparent}html.theme--documenter-dark .notification>.delete{right:.5rem;position:absolute;top:0.5rem}html.theme--documenter-dark .notification .title,html.theme--documenter-dark .notification .subtitle,html.theme--documenter-dark .notification .content{color:currentColor}html.theme--documenter-dark .notification.is-white{background-color:#fff;color:#0a0a0a}html.theme--documenter-dark .notification.is-black{background-color:#0a0a0a;color:#fff}html.theme--documenter-dark .notification.is-light{background-color:#ecf0f1;color:rgba(0,0,0,0.7)}html.theme--documenter-dark .notification.is-dark,html.theme--documenter-dark .content kbd.notification{background-color:#282f2f;color:#fff}html.theme--documenter-dark .notification.is-primary,html.theme--documenter-dark .docstring>section>a.notification.docs-sourcelink{background-color:#375a7f;color:#fff}html.theme--documenter-dark .notification.is-primary.is-light,html.theme--documenter-dark .docstring>section>a.notification.is-light.docs-sourcelink{background-color:#f1f5f9;color:#4d7eb2}html.theme--documenter-dark .notification.is-link{background-color:#1abc9c;color:#fff}html.theme--documenter-dark .notification.is-link.is-light{background-color:#edfdf9;color:#15987e}html.theme--documenter-dark .notification.is-info{background-color:#024c7d;color:#fff}html.theme--documenter-dark .notification.is-info.is-light{background-color:#ebf7ff;color:#0e9dfb}html.theme--documenter-dark .notification.is-success{background-color:#008438;color:#fff}html.theme--documenter-dark .notification.is-success.is-light{background-color:#ebfff3;color:#00eb64}html.theme--documenter-dark .notification.is-warning{background-color:#ad8100;color:#fff}html.theme--documenter-dark .notification.is-warning.is-light{background-color:#fffaeb;color:#d19c00}html.theme--documenter-dark .notification.is-danger{background-color:#9e1b0d;color:#fff}html.theme--documenter-dark .notification.is-danger.is-light{background-color:#fdeeec;color:#ec311d}html.theme--documenter-dark .progress{-moz-appearance:none;-webkit-appearance:none;border:none;border-radius:9999px;display:block;height:1rem;overflow:hidden;padding:0;width:100%}html.theme--documenter-dark .progress::-webkit-progress-bar{background-color:#343c3d}html.theme--documenter-dark .progress::-webkit-progress-value{background-color:#dbdee0}html.theme--documenter-dark .progress::-moz-progress-bar{background-color:#dbdee0}html.theme--documenter-dark .progress::-ms-fill{background-color:#dbdee0;border:none}html.theme--documenter-dark .progress.is-white::-webkit-progress-value{background-color:#fff}html.theme--documenter-dark .progress.is-white::-moz-progress-bar{background-color:#fff}html.theme--documenter-dark .progress.is-white::-ms-fill{background-color:#fff}html.theme--documenter-dark .progress.is-white:indeterminate{background-image:linear-gradient(to right, #fff 30%, #343c3d 30%)}html.theme--documenter-dark .progress.is-black::-webkit-progress-value{background-color:#0a0a0a}html.theme--documenter-dark .progress.is-black::-moz-progress-bar{background-color:#0a0a0a}html.theme--documenter-dark .progress.is-black::-ms-fill{background-color:#0a0a0a}html.theme--documenter-dark .progress.is-black:indeterminate{background-image:linear-gradient(to right, #0a0a0a 30%, #343c3d 30%)}html.theme--documenter-dark .progress.is-light::-webkit-progress-value{background-color:#ecf0f1}html.theme--documenter-dark .progress.is-light::-moz-progress-bar{background-color:#ecf0f1}html.theme--documenter-dark .progress.is-light::-ms-fill{background-color:#ecf0f1}html.theme--documenter-dark .progress.is-light:indeterminate{background-image:linear-gradient(to right, #ecf0f1 30%, #343c3d 30%)}html.theme--documenter-dark .progress.is-dark::-webkit-progress-value,html.theme--documenter-dark .content kbd.progress::-webkit-progress-value{background-color:#282f2f}html.theme--documenter-dark .progress.is-dark::-moz-progress-bar,html.theme--documenter-dark .content kbd.progress::-moz-progress-bar{background-color:#282f2f}html.theme--documenter-dark .progress.is-dark::-ms-fill,html.theme--documenter-dark .content kbd.progress::-ms-fill{background-color:#282f2f}html.theme--documenter-dark .progress.is-dark:indeterminate,html.theme--documenter-dark .content kbd.progress:indeterminate{background-image:linear-gradient(to right, #282f2f 30%, #343c3d 30%)}html.theme--documenter-dark .progress.is-primary::-webkit-progress-value,html.theme--documenter-dark .docstring>section>a.progress.docs-sourcelink::-webkit-progress-value{background-color:#375a7f}html.theme--documenter-dark .progress.is-primary::-moz-progress-bar,html.theme--documenter-dark .docstring>section>a.progress.docs-sourcelink::-moz-progress-bar{background-color:#375a7f}html.theme--documenter-dark .progress.is-primary::-ms-fill,html.theme--documenter-dark .docstring>section>a.progress.docs-sourcelink::-ms-fill{background-color:#375a7f}html.theme--documenter-dark .progress.is-primary:indeterminate,html.theme--documenter-dark .docstring>section>a.progress.docs-sourcelink:indeterminate{background-image:linear-gradient(to right, #375a7f 30%, #343c3d 30%)}html.theme--documenter-dark .progress.is-link::-webkit-progress-value{background-color:#1abc9c}html.theme--documenter-dark .progress.is-link::-moz-progress-bar{background-color:#1abc9c}html.theme--documenter-dark .progress.is-link::-ms-fill{background-color:#1abc9c}html.theme--documenter-dark .progress.is-link:indeterminate{background-image:linear-gradient(to right, #1abc9c 30%, #343c3d 30%)}html.theme--documenter-dark .progress.is-info::-webkit-progress-value{background-color:#024c7d}html.theme--documenter-dark .progress.is-info::-moz-progress-bar{background-color:#024c7d}html.theme--documenter-dark .progress.is-info::-ms-fill{background-color:#024c7d}html.theme--documenter-dark .progress.is-info:indeterminate{background-image:linear-gradient(to right, #024c7d 30%, #343c3d 30%)}html.theme--documenter-dark .progress.is-success::-webkit-progress-value{background-color:#008438}html.theme--documenter-dark .progress.is-success::-moz-progress-bar{background-color:#008438}html.theme--documenter-dark .progress.is-success::-ms-fill{background-color:#008438}html.theme--documenter-dark .progress.is-success:indeterminate{background-image:linear-gradient(to right, #008438 30%, #343c3d 30%)}html.theme--documenter-dark .progress.is-warning::-webkit-progress-value{background-color:#ad8100}html.theme--documenter-dark .progress.is-warning::-moz-progress-bar{background-color:#ad8100}html.theme--documenter-dark .progress.is-warning::-ms-fill{background-color:#ad8100}html.theme--documenter-dark .progress.is-warning:indeterminate{background-image:linear-gradient(to right, #ad8100 30%, #343c3d 30%)}html.theme--documenter-dark .progress.is-danger::-webkit-progress-value{background-color:#9e1b0d}html.theme--documenter-dark .progress.is-danger::-moz-progress-bar{background-color:#9e1b0d}html.theme--documenter-dark .progress.is-danger::-ms-fill{background-color:#9e1b0d}html.theme--documenter-dark .progress.is-danger:indeterminate{background-image:linear-gradient(to right, #9e1b0d 30%, #343c3d 30%)}html.theme--documenter-dark .progress:indeterminate{animation-duration:1.5s;animation-iteration-count:infinite;animation-name:moveIndeterminate;animation-timing-function:linear;background-color:#343c3d;background-image:linear-gradient(to right, #fff 30%, #343c3d 30%);background-position:top left;background-repeat:no-repeat;background-size:150% 150%}html.theme--documenter-dark .progress:indeterminate::-webkit-progress-bar{background-color:transparent}html.theme--documenter-dark .progress:indeterminate::-moz-progress-bar{background-color:transparent}html.theme--documenter-dark .progress:indeterminate::-ms-fill{animation-name:none}html.theme--documenter-dark .progress.is-small,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.progress{height:.75rem}html.theme--documenter-dark .progress.is-medium{height:1.25rem}html.theme--documenter-dark .progress.is-large{height:1.5rem}@keyframes moveIndeterminate{from{background-position:200% 0}to{background-position:-200% 0}}html.theme--documenter-dark .table{background-color:#343c3d;color:#fff}html.theme--documenter-dark .table td,html.theme--documenter-dark .table th{border:1px solid #5e6d6f;border-width:0 0 1px;padding:0.5em 0.75em;vertical-align:top}html.theme--documenter-dark .table td.is-white,html.theme--documenter-dark .table th.is-white{background-color:#fff;border-color:#fff;color:#0a0a0a}html.theme--documenter-dark .table td.is-black,html.theme--documenter-dark .table th.is-black{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}html.theme--documenter-dark .table td.is-light,html.theme--documenter-dark .table th.is-light{background-color:#ecf0f1;border-color:#ecf0f1;color:rgba(0,0,0,0.7)}html.theme--documenter-dark .table td.is-dark,html.theme--documenter-dark .table th.is-dark{background-color:#282f2f;border-color:#282f2f;color:#fff}html.theme--documenter-dark .table td.is-primary,html.theme--documenter-dark .table th.is-primary{background-color:#375a7f;border-color:#375a7f;color:#fff}html.theme--documenter-dark .table td.is-link,html.theme--documenter-dark .table th.is-link{background-color:#1abc9c;border-color:#1abc9c;color:#fff}html.theme--documenter-dark .table td.is-info,html.theme--documenter-dark .table th.is-info{background-color:#024c7d;border-color:#024c7d;color:#fff}html.theme--documenter-dark .table td.is-success,html.theme--documenter-dark .table th.is-success{background-color:#008438;border-color:#008438;color:#fff}html.theme--documenter-dark .table td.is-warning,html.theme--documenter-dark .table th.is-warning{background-color:#ad8100;border-color:#ad8100;color:#fff}html.theme--documenter-dark .table td.is-danger,html.theme--documenter-dark .table th.is-danger{background-color:#9e1b0d;border-color:#9e1b0d;color:#fff}html.theme--documenter-dark .table td.is-narrow,html.theme--documenter-dark .table th.is-narrow{white-space:nowrap;width:1%}html.theme--documenter-dark .table td.is-selected,html.theme--documenter-dark .table th.is-selected{background-color:#375a7f;color:#fff}html.theme--documenter-dark .table td.is-selected a,html.theme--documenter-dark .table td.is-selected strong,html.theme--documenter-dark .table th.is-selected a,html.theme--documenter-dark .table th.is-selected strong{color:currentColor}html.theme--documenter-dark .table td.is-vcentered,html.theme--documenter-dark .table th.is-vcentered{vertical-align:middle}html.theme--documenter-dark .table th{color:#f2f2f2}html.theme--documenter-dark .table th:not([align]){text-align:left}html.theme--documenter-dark .table tr.is-selected{background-color:#375a7f;color:#fff}html.theme--documenter-dark .table tr.is-selected a,html.theme--documenter-dark .table tr.is-selected strong{color:currentColor}html.theme--documenter-dark .table tr.is-selected td,html.theme--documenter-dark .table tr.is-selected th{border-color:#fff;color:currentColor}html.theme--documenter-dark .table thead{background-color:rgba(0,0,0,0)}html.theme--documenter-dark .table thead td,html.theme--documenter-dark .table thead th{border-width:0 0 2px;color:#f2f2f2}html.theme--documenter-dark .table tfoot{background-color:rgba(0,0,0,0)}html.theme--documenter-dark .table tfoot td,html.theme--documenter-dark .table tfoot th{border-width:2px 0 0;color:#f2f2f2}html.theme--documenter-dark .table tbody{background-color:rgba(0,0,0,0)}html.theme--documenter-dark .table tbody tr:last-child td,html.theme--documenter-dark .table tbody tr:last-child th{border-bottom-width:0}html.theme--documenter-dark .table.is-bordered td,html.theme--documenter-dark .table.is-bordered th{border-width:1px}html.theme--documenter-dark .table.is-bordered tr:last-child td,html.theme--documenter-dark .table.is-bordered tr:last-child th{border-bottom-width:1px}html.theme--documenter-dark .table.is-fullwidth{width:100%}html.theme--documenter-dark .table.is-hoverable tbody tr:not(.is-selected):hover{background-color:#282f2f}html.theme--documenter-dark .table.is-hoverable.is-striped tbody tr:not(.is-selected):hover{background-color:#282f2f}html.theme--documenter-dark .table.is-hoverable.is-striped tbody tr:not(.is-selected):hover:nth-child(even){background-color:#2d3435}html.theme--documenter-dark .table.is-narrow td,html.theme--documenter-dark .table.is-narrow th{padding:0.25em 0.5em}html.theme--documenter-dark .table.is-striped tbody tr:not(.is-selected):nth-child(even){background-color:#282f2f}html.theme--documenter-dark .table-container{-webkit-overflow-scrolling:touch;overflow:auto;overflow-y:hidden;max-width:100%}html.theme--documenter-dark .tags{align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start}html.theme--documenter-dark .tags .tag,html.theme--documenter-dark .tags .content kbd,html.theme--documenter-dark .content .tags kbd,html.theme--documenter-dark .tags .docstring>section>a.docs-sourcelink{margin-bottom:0.5rem}html.theme--documenter-dark .tags .tag:not(:last-child),html.theme--documenter-dark .tags .content kbd:not(:last-child),html.theme--documenter-dark .content .tags kbd:not(:last-child),html.theme--documenter-dark .tags .docstring>section>a.docs-sourcelink:not(:last-child){margin-right:.5rem}html.theme--documenter-dark .tags:last-child{margin-bottom:-0.5rem}html.theme--documenter-dark .tags:not(:last-child){margin-bottom:1rem}html.theme--documenter-dark .tags.are-medium .tag:not(.is-normal):not(.is-large),html.theme--documenter-dark .tags.are-medium .content kbd:not(.is-normal):not(.is-large),html.theme--documenter-dark .content .tags.are-medium kbd:not(.is-normal):not(.is-large),html.theme--documenter-dark .tags.are-medium .docstring>section>a.docs-sourcelink:not(.is-normal):not(.is-large){font-size:1rem}html.theme--documenter-dark .tags.are-large .tag:not(.is-normal):not(.is-medium),html.theme--documenter-dark .tags.are-large .content kbd:not(.is-normal):not(.is-medium),html.theme--documenter-dark .content .tags.are-large kbd:not(.is-normal):not(.is-medium),html.theme--documenter-dark .tags.are-large .docstring>section>a.docs-sourcelink:not(.is-normal):not(.is-medium){font-size:1.25rem}html.theme--documenter-dark .tags.is-centered{justify-content:center}html.theme--documenter-dark .tags.is-centered .tag,html.theme--documenter-dark .tags.is-centered .content kbd,html.theme--documenter-dark .content .tags.is-centered kbd,html.theme--documenter-dark .tags.is-centered .docstring>section>a.docs-sourcelink{margin-right:0.25rem;margin-left:0.25rem}html.theme--documenter-dark .tags.is-right{justify-content:flex-end}html.theme--documenter-dark .tags.is-right .tag:not(:first-child),html.theme--documenter-dark .tags.is-right .content kbd:not(:first-child),html.theme--documenter-dark .content .tags.is-right kbd:not(:first-child),html.theme--documenter-dark .tags.is-right .docstring>section>a.docs-sourcelink:not(:first-child){margin-left:0.5rem}html.theme--documenter-dark .tags.is-right .tag:not(:last-child),html.theme--documenter-dark .tags.is-right .content kbd:not(:last-child),html.theme--documenter-dark .content .tags.is-right kbd:not(:last-child),html.theme--documenter-dark .tags.is-right .docstring>section>a.docs-sourcelink:not(:last-child){margin-right:0}html.theme--documenter-dark .tags.has-addons .tag,html.theme--documenter-dark .tags.has-addons .content kbd,html.theme--documenter-dark .content .tags.has-addons kbd,html.theme--documenter-dark .tags.has-addons .docstring>section>a.docs-sourcelink{margin-right:0}html.theme--documenter-dark .tags.has-addons .tag:not(:first-child),html.theme--documenter-dark .tags.has-addons .content kbd:not(:first-child),html.theme--documenter-dark .content .tags.has-addons kbd:not(:first-child),html.theme--documenter-dark .tags.has-addons .docstring>section>a.docs-sourcelink:not(:first-child){margin-left:0;border-top-left-radius:0;border-bottom-left-radius:0}html.theme--documenter-dark .tags.has-addons .tag:not(:last-child),html.theme--documenter-dark .tags.has-addons .content kbd:not(:last-child),html.theme--documenter-dark .content .tags.has-addons kbd:not(:last-child),html.theme--documenter-dark .tags.has-addons .docstring>section>a.docs-sourcelink:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}html.theme--documenter-dark .tag:not(body),html.theme--documenter-dark .content kbd:not(body),html.theme--documenter-dark .docstring>section>a.docs-sourcelink:not(body){align-items:center;background-color:#282f2f;border-radius:.4em;color:#fff;display:inline-flex;font-size:.75rem;height:2em;justify-content:center;line-height:1.5;padding-left:0.75em;padding-right:0.75em;white-space:nowrap}html.theme--documenter-dark .tag:not(body) .delete,html.theme--documenter-dark .content kbd:not(body) .delete,html.theme--documenter-dark .docstring>section>a.docs-sourcelink:not(body) .delete{margin-left:.25rem;margin-right:-.375rem}html.theme--documenter-dark .tag.is-white:not(body),html.theme--documenter-dark .content kbd.is-white:not(body),html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-white:not(body){background-color:#fff;color:#0a0a0a}html.theme--documenter-dark .tag.is-black:not(body),html.theme--documenter-dark .content kbd.is-black:not(body),html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-black:not(body){background-color:#0a0a0a;color:#fff}html.theme--documenter-dark .tag.is-light:not(body),html.theme--documenter-dark .content kbd.is-light:not(body),html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-light:not(body){background-color:#ecf0f1;color:rgba(0,0,0,0.7)}html.theme--documenter-dark .tag.is-dark:not(body),html.theme--documenter-dark .content kbd:not(body),html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-dark:not(body),html.theme--documenter-dark .content .docstring>section>kbd:not(body){background-color:#282f2f;color:#fff}html.theme--documenter-dark .tag.is-primary:not(body),html.theme--documenter-dark .content kbd.is-primary:not(body),html.theme--documenter-dark .docstring>section>a.docs-sourcelink:not(body){background-color:#375a7f;color:#fff}html.theme--documenter-dark .tag.is-primary.is-light:not(body),html.theme--documenter-dark .content kbd.is-primary.is-light:not(body),html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-light:not(body){background-color:#f1f5f9;color:#4d7eb2}html.theme--documenter-dark .tag.is-link:not(body),html.theme--documenter-dark .content kbd.is-link:not(body),html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-link:not(body){background-color:#1abc9c;color:#fff}html.theme--documenter-dark .tag.is-link.is-light:not(body),html.theme--documenter-dark .content kbd.is-link.is-light:not(body),html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-link.is-light:not(body){background-color:#edfdf9;color:#15987e}html.theme--documenter-dark .tag.is-info:not(body),html.theme--documenter-dark .content kbd.is-info:not(body),html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-info:not(body){background-color:#024c7d;color:#fff}html.theme--documenter-dark .tag.is-info.is-light:not(body),html.theme--documenter-dark .content kbd.is-info.is-light:not(body),html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-info.is-light:not(body){background-color:#ebf7ff;color:#0e9dfb}html.theme--documenter-dark .tag.is-success:not(body),html.theme--documenter-dark .content kbd.is-success:not(body),html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-success:not(body){background-color:#008438;color:#fff}html.theme--documenter-dark .tag.is-success.is-light:not(body),html.theme--documenter-dark .content kbd.is-success.is-light:not(body),html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-success.is-light:not(body){background-color:#ebfff3;color:#00eb64}html.theme--documenter-dark .tag.is-warning:not(body),html.theme--documenter-dark .content kbd.is-warning:not(body),html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-warning:not(body){background-color:#ad8100;color:#fff}html.theme--documenter-dark .tag.is-warning.is-light:not(body),html.theme--documenter-dark .content kbd.is-warning.is-light:not(body),html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-warning.is-light:not(body){background-color:#fffaeb;color:#d19c00}html.theme--documenter-dark .tag.is-danger:not(body),html.theme--documenter-dark .content kbd.is-danger:not(body),html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-danger:not(body){background-color:#9e1b0d;color:#fff}html.theme--documenter-dark .tag.is-danger.is-light:not(body),html.theme--documenter-dark .content kbd.is-danger.is-light:not(body),html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-danger.is-light:not(body){background-color:#fdeeec;color:#ec311d}html.theme--documenter-dark .tag.is-normal:not(body),html.theme--documenter-dark .content kbd.is-normal:not(body),html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-normal:not(body){font-size:.75rem}html.theme--documenter-dark .tag.is-medium:not(body),html.theme--documenter-dark .content kbd.is-medium:not(body),html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-medium:not(body){font-size:1rem}html.theme--documenter-dark .tag.is-large:not(body),html.theme--documenter-dark .content kbd.is-large:not(body),html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-large:not(body){font-size:1.25rem}html.theme--documenter-dark .tag:not(body) .icon:first-child:not(:last-child),html.theme--documenter-dark .content kbd:not(body) .icon:first-child:not(:last-child),html.theme--documenter-dark .docstring>section>a.docs-sourcelink:not(body) .icon:first-child:not(:last-child){margin-left:-.375em;margin-right:.1875em}html.theme--documenter-dark .tag:not(body) .icon:last-child:not(:first-child),html.theme--documenter-dark .content kbd:not(body) .icon:last-child:not(:first-child),html.theme--documenter-dark .docstring>section>a.docs-sourcelink:not(body) .icon:last-child:not(:first-child){margin-left:.1875em;margin-right:-.375em}html.theme--documenter-dark .tag:not(body) .icon:first-child:last-child,html.theme--documenter-dark .content kbd:not(body) .icon:first-child:last-child,html.theme--documenter-dark .docstring>section>a.docs-sourcelink:not(body) .icon:first-child:last-child{margin-left:-.375em;margin-right:-.375em}html.theme--documenter-dark .tag.is-delete:not(body),html.theme--documenter-dark .content kbd.is-delete:not(body),html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-delete:not(body){margin-left:1px;padding:0;position:relative;width:2em}html.theme--documenter-dark .tag.is-delete:not(body)::before,html.theme--documenter-dark .content kbd.is-delete:not(body)::before,html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-delete:not(body)::before,html.theme--documenter-dark .tag.is-delete:not(body)::after,html.theme--documenter-dark .content kbd.is-delete:not(body)::after,html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-delete:not(body)::after{background-color:currentColor;content:"";display:block;left:50%;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%) rotate(45deg);transform-origin:center center}html.theme--documenter-dark .tag.is-delete:not(body)::before,html.theme--documenter-dark .content kbd.is-delete:not(body)::before,html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-delete:not(body)::before{height:1px;width:50%}html.theme--documenter-dark .tag.is-delete:not(body)::after,html.theme--documenter-dark .content kbd.is-delete:not(body)::after,html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-delete:not(body)::after{height:50%;width:1px}html.theme--documenter-dark .tag.is-delete:not(body):hover,html.theme--documenter-dark .content kbd.is-delete:not(body):hover,html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-delete:not(body):hover,html.theme--documenter-dark .tag.is-delete:not(body):focus,html.theme--documenter-dark .content kbd.is-delete:not(body):focus,html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-delete:not(body):focus{background-color:#1d2122}html.theme--documenter-dark .tag.is-delete:not(body):active,html.theme--documenter-dark .content kbd.is-delete:not(body):active,html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-delete:not(body):active{background-color:#111414}html.theme--documenter-dark .tag.is-rounded:not(body),html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input:not(body),html.theme--documenter-dark .content kbd.is-rounded:not(body),html.theme--documenter-dark #documenter .docs-sidebar .content form.docs-search>input:not(body),html.theme--documenter-dark .docstring>section>a.docs-sourcelink.is-rounded:not(body){border-radius:9999px}html.theme--documenter-dark a.tag:hover,html.theme--documenter-dark .docstring>section>a.docs-sourcelink:hover{text-decoration:underline}html.theme--documenter-dark .title,html.theme--documenter-dark .subtitle{word-break:break-word}html.theme--documenter-dark .title em,html.theme--documenter-dark .title span,html.theme--documenter-dark .subtitle em,html.theme--documenter-dark .subtitle span{font-weight:inherit}html.theme--documenter-dark .title sub,html.theme--documenter-dark .subtitle sub{font-size:.75em}html.theme--documenter-dark .title sup,html.theme--documenter-dark .subtitle sup{font-size:.75em}html.theme--documenter-dark .title .tag,html.theme--documenter-dark .title .content kbd,html.theme--documenter-dark .content .title kbd,html.theme--documenter-dark .title .docstring>section>a.docs-sourcelink,html.theme--documenter-dark .subtitle .tag,html.theme--documenter-dark .subtitle .content kbd,html.theme--documenter-dark .content .subtitle kbd,html.theme--documenter-dark .subtitle .docstring>section>a.docs-sourcelink{vertical-align:middle}html.theme--documenter-dark .title{color:#fff;font-size:2rem;font-weight:500;line-height:1.125}html.theme--documenter-dark .title strong{color:inherit;font-weight:inherit}html.theme--documenter-dark .title:not(.is-spaced)+.subtitle{margin-top:-1.25rem}html.theme--documenter-dark .title.is-1{font-size:3rem}html.theme--documenter-dark .title.is-2{font-size:2.5rem}html.theme--documenter-dark .title.is-3{font-size:2rem}html.theme--documenter-dark .title.is-4{font-size:1.5rem}html.theme--documenter-dark .title.is-5{font-size:1.25rem}html.theme--documenter-dark .title.is-6{font-size:1rem}html.theme--documenter-dark .title.is-7{font-size:.75rem}html.theme--documenter-dark .subtitle{color:#8c9b9d;font-size:1.25rem;font-weight:400;line-height:1.25}html.theme--documenter-dark .subtitle strong{color:#8c9b9d;font-weight:600}html.theme--documenter-dark .subtitle:not(.is-spaced)+.title{margin-top:-1.25rem}html.theme--documenter-dark .subtitle.is-1{font-size:3rem}html.theme--documenter-dark .subtitle.is-2{font-size:2.5rem}html.theme--documenter-dark .subtitle.is-3{font-size:2rem}html.theme--documenter-dark .subtitle.is-4{font-size:1.5rem}html.theme--documenter-dark .subtitle.is-5{font-size:1.25rem}html.theme--documenter-dark .subtitle.is-6{font-size:1rem}html.theme--documenter-dark .subtitle.is-7{font-size:.75rem}html.theme--documenter-dark .heading{display:block;font-size:11px;letter-spacing:1px;margin-bottom:5px;text-transform:uppercase}html.theme--documenter-dark .number{align-items:center;background-color:#282f2f;border-radius:9999px;display:inline-flex;font-size:1.25rem;height:2em;justify-content:center;margin-right:1.5rem;min-width:2.5em;padding:0.25rem 0.5rem;text-align:center;vertical-align:top}html.theme--documenter-dark .select select,html.theme--documenter-dark .textarea,html.theme--documenter-dark .input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input{background-color:#1f2424;border-color:#5e6d6f;border-radius:.4em;color:#dbdee0}html.theme--documenter-dark .select select::-moz-placeholder,html.theme--documenter-dark .textarea::-moz-placeholder,html.theme--documenter-dark .input::-moz-placeholder,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input::-moz-placeholder{color:#868c98}html.theme--documenter-dark .select select::-webkit-input-placeholder,html.theme--documenter-dark .textarea::-webkit-input-placeholder,html.theme--documenter-dark .input::-webkit-input-placeholder,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input::-webkit-input-placeholder{color:#868c98}html.theme--documenter-dark .select select:-moz-placeholder,html.theme--documenter-dark .textarea:-moz-placeholder,html.theme--documenter-dark .input:-moz-placeholder,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input:-moz-placeholder{color:#868c98}html.theme--documenter-dark .select select:-ms-input-placeholder,html.theme--documenter-dark .textarea:-ms-input-placeholder,html.theme--documenter-dark .input:-ms-input-placeholder,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input:-ms-input-placeholder{color:#868c98}html.theme--documenter-dark .select select:hover,html.theme--documenter-dark .textarea:hover,html.theme--documenter-dark .input:hover,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input:hover,html.theme--documenter-dark .select select.is-hovered,html.theme--documenter-dark .is-hovered.textarea,html.theme--documenter-dark .is-hovered.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-hovered{border-color:#8c9b9d}html.theme--documenter-dark .select select:focus,html.theme--documenter-dark .textarea:focus,html.theme--documenter-dark .input:focus,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input:focus,html.theme--documenter-dark .select select.is-focused,html.theme--documenter-dark .is-focused.textarea,html.theme--documenter-dark .is-focused.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-focused,html.theme--documenter-dark .select select:active,html.theme--documenter-dark .textarea:active,html.theme--documenter-dark .input:active,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input:active,html.theme--documenter-dark .select select.is-active,html.theme--documenter-dark .is-active.textarea,html.theme--documenter-dark .is-active.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-active{border-color:#1abc9c;box-shadow:0 0 0 0.125em rgba(26,188,156,0.25)}html.theme--documenter-dark .select select[disabled],html.theme--documenter-dark .textarea[disabled],html.theme--documenter-dark .input[disabled],html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input[disabled],fieldset[disabled] html.theme--documenter-dark .select select,fieldset[disabled] html.theme--documenter-dark .textarea,fieldset[disabled] html.theme--documenter-dark .input,fieldset[disabled] html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input{background-color:#8c9b9d;border-color:#282f2f;box-shadow:none;color:#fff}html.theme--documenter-dark .select select[disabled]::-moz-placeholder,html.theme--documenter-dark .textarea[disabled]::-moz-placeholder,html.theme--documenter-dark .input[disabled]::-moz-placeholder,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input[disabled]::-moz-placeholder,fieldset[disabled] html.theme--documenter-dark .select select::-moz-placeholder,fieldset[disabled] html.theme--documenter-dark .textarea::-moz-placeholder,fieldset[disabled] html.theme--documenter-dark .input::-moz-placeholder,fieldset[disabled] html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input::-moz-placeholder{color:rgba(255,255,255,0.3)}html.theme--documenter-dark .select select[disabled]::-webkit-input-placeholder,html.theme--documenter-dark .textarea[disabled]::-webkit-input-placeholder,html.theme--documenter-dark .input[disabled]::-webkit-input-placeholder,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input[disabled]::-webkit-input-placeholder,fieldset[disabled] html.theme--documenter-dark .select select::-webkit-input-placeholder,fieldset[disabled] html.theme--documenter-dark .textarea::-webkit-input-placeholder,fieldset[disabled] html.theme--documenter-dark .input::-webkit-input-placeholder,fieldset[disabled] html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input::-webkit-input-placeholder{color:rgba(255,255,255,0.3)}html.theme--documenter-dark .select select[disabled]:-moz-placeholder,html.theme--documenter-dark .textarea[disabled]:-moz-placeholder,html.theme--documenter-dark .input[disabled]:-moz-placeholder,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input[disabled]:-moz-placeholder,fieldset[disabled] html.theme--documenter-dark .select select:-moz-placeholder,fieldset[disabled] html.theme--documenter-dark .textarea:-moz-placeholder,fieldset[disabled] html.theme--documenter-dark .input:-moz-placeholder,fieldset[disabled] html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input:-moz-placeholder{color:rgba(255,255,255,0.3)}html.theme--documenter-dark .select select[disabled]:-ms-input-placeholder,html.theme--documenter-dark .textarea[disabled]:-ms-input-placeholder,html.theme--documenter-dark .input[disabled]:-ms-input-placeholder,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input[disabled]:-ms-input-placeholder,fieldset[disabled] html.theme--documenter-dark .select select:-ms-input-placeholder,fieldset[disabled] html.theme--documenter-dark .textarea:-ms-input-placeholder,fieldset[disabled] html.theme--documenter-dark .input:-ms-input-placeholder,fieldset[disabled] html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input:-ms-input-placeholder{color:rgba(255,255,255,0.3)}html.theme--documenter-dark .textarea,html.theme--documenter-dark .input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input{box-shadow:inset 0 0.0625em 0.125em rgba(10,10,10,0.05);max-width:100%;width:100%}html.theme--documenter-dark .textarea[readonly],html.theme--documenter-dark .input[readonly],html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input[readonly]{box-shadow:none}html.theme--documenter-dark .is-white.textarea,html.theme--documenter-dark .is-white.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-white{border-color:#fff}html.theme--documenter-dark .is-white.textarea:focus,html.theme--documenter-dark .is-white.input:focus,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-white:focus,html.theme--documenter-dark .is-white.is-focused.textarea,html.theme--documenter-dark .is-white.is-focused.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-focused,html.theme--documenter-dark .is-white.textarea:active,html.theme--documenter-dark .is-white.input:active,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-white:active,html.theme--documenter-dark .is-white.is-active.textarea,html.theme--documenter-dark .is-white.is-active.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-active{box-shadow:0 0 0 0.125em rgba(255,255,255,0.25)}html.theme--documenter-dark .is-black.textarea,html.theme--documenter-dark .is-black.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-black{border-color:#0a0a0a}html.theme--documenter-dark .is-black.textarea:focus,html.theme--documenter-dark .is-black.input:focus,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-black:focus,html.theme--documenter-dark .is-black.is-focused.textarea,html.theme--documenter-dark .is-black.is-focused.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-focused,html.theme--documenter-dark .is-black.textarea:active,html.theme--documenter-dark .is-black.input:active,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-black:active,html.theme--documenter-dark .is-black.is-active.textarea,html.theme--documenter-dark .is-black.is-active.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-active{box-shadow:0 0 0 0.125em rgba(10,10,10,0.25)}html.theme--documenter-dark .is-light.textarea,html.theme--documenter-dark .is-light.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-light{border-color:#ecf0f1}html.theme--documenter-dark .is-light.textarea:focus,html.theme--documenter-dark .is-light.input:focus,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-light:focus,html.theme--documenter-dark .is-light.is-focused.textarea,html.theme--documenter-dark .is-light.is-focused.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-focused,html.theme--documenter-dark .is-light.textarea:active,html.theme--documenter-dark .is-light.input:active,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-light:active,html.theme--documenter-dark .is-light.is-active.textarea,html.theme--documenter-dark .is-light.is-active.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-active{box-shadow:0 0 0 0.125em rgba(236,240,241,0.25)}html.theme--documenter-dark .is-dark.textarea,html.theme--documenter-dark .content kbd.textarea,html.theme--documenter-dark .is-dark.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-dark,html.theme--documenter-dark .content kbd.input{border-color:#282f2f}html.theme--documenter-dark .is-dark.textarea:focus,html.theme--documenter-dark .content kbd.textarea:focus,html.theme--documenter-dark .is-dark.input:focus,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-dark:focus,html.theme--documenter-dark .content kbd.input:focus,html.theme--documenter-dark .is-dark.is-focused.textarea,html.theme--documenter-dark .content kbd.is-focused.textarea,html.theme--documenter-dark .is-dark.is-focused.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-focused,html.theme--documenter-dark .content kbd.is-focused.input,html.theme--documenter-dark #documenter .docs-sidebar .content form.docs-search>input.is-focused,html.theme--documenter-dark .is-dark.textarea:active,html.theme--documenter-dark .content kbd.textarea:active,html.theme--documenter-dark .is-dark.input:active,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-dark:active,html.theme--documenter-dark .content kbd.input:active,html.theme--documenter-dark .is-dark.is-active.textarea,html.theme--documenter-dark .content kbd.is-active.textarea,html.theme--documenter-dark .is-dark.is-active.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-active,html.theme--documenter-dark .content kbd.is-active.input,html.theme--documenter-dark #documenter .docs-sidebar .content form.docs-search>input.is-active{box-shadow:0 0 0 0.125em rgba(40,47,47,0.25)}html.theme--documenter-dark .is-primary.textarea,html.theme--documenter-dark .docstring>section>a.textarea.docs-sourcelink,html.theme--documenter-dark .is-primary.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-primary,html.theme--documenter-dark .docstring>section>a.input.docs-sourcelink{border-color:#375a7f}html.theme--documenter-dark .is-primary.textarea:focus,html.theme--documenter-dark .docstring>section>a.textarea.docs-sourcelink:focus,html.theme--documenter-dark .is-primary.input:focus,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-primary:focus,html.theme--documenter-dark .docstring>section>a.input.docs-sourcelink:focus,html.theme--documenter-dark .is-primary.is-focused.textarea,html.theme--documenter-dark .docstring>section>a.is-focused.textarea.docs-sourcelink,html.theme--documenter-dark .is-primary.is-focused.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-focused,html.theme--documenter-dark .docstring>section>a.is-focused.input.docs-sourcelink,html.theme--documenter-dark .is-primary.textarea:active,html.theme--documenter-dark .docstring>section>a.textarea.docs-sourcelink:active,html.theme--documenter-dark .is-primary.input:active,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-primary:active,html.theme--documenter-dark .docstring>section>a.input.docs-sourcelink:active,html.theme--documenter-dark .is-primary.is-active.textarea,html.theme--documenter-dark .docstring>section>a.is-active.textarea.docs-sourcelink,html.theme--documenter-dark .is-primary.is-active.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-active,html.theme--documenter-dark .docstring>section>a.is-active.input.docs-sourcelink{box-shadow:0 0 0 0.125em rgba(55,90,127,0.25)}html.theme--documenter-dark .is-link.textarea,html.theme--documenter-dark .is-link.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-link{border-color:#1abc9c}html.theme--documenter-dark .is-link.textarea:focus,html.theme--documenter-dark .is-link.input:focus,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-link:focus,html.theme--documenter-dark .is-link.is-focused.textarea,html.theme--documenter-dark .is-link.is-focused.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-focused,html.theme--documenter-dark .is-link.textarea:active,html.theme--documenter-dark .is-link.input:active,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-link:active,html.theme--documenter-dark .is-link.is-active.textarea,html.theme--documenter-dark .is-link.is-active.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-active{box-shadow:0 0 0 0.125em rgba(26,188,156,0.25)}html.theme--documenter-dark .is-info.textarea,html.theme--documenter-dark .is-info.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-info{border-color:#024c7d}html.theme--documenter-dark .is-info.textarea:focus,html.theme--documenter-dark .is-info.input:focus,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-info:focus,html.theme--documenter-dark .is-info.is-focused.textarea,html.theme--documenter-dark .is-info.is-focused.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-focused,html.theme--documenter-dark .is-info.textarea:active,html.theme--documenter-dark .is-info.input:active,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-info:active,html.theme--documenter-dark .is-info.is-active.textarea,html.theme--documenter-dark .is-info.is-active.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-active{box-shadow:0 0 0 0.125em rgba(2,76,125,0.25)}html.theme--documenter-dark .is-success.textarea,html.theme--documenter-dark .is-success.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-success{border-color:#008438}html.theme--documenter-dark .is-success.textarea:focus,html.theme--documenter-dark .is-success.input:focus,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-success:focus,html.theme--documenter-dark .is-success.is-focused.textarea,html.theme--documenter-dark .is-success.is-focused.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-focused,html.theme--documenter-dark .is-success.textarea:active,html.theme--documenter-dark .is-success.input:active,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-success:active,html.theme--documenter-dark .is-success.is-active.textarea,html.theme--documenter-dark .is-success.is-active.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-active{box-shadow:0 0 0 0.125em rgba(0,132,56,0.25)}html.theme--documenter-dark .is-warning.textarea,html.theme--documenter-dark .is-warning.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-warning{border-color:#ad8100}html.theme--documenter-dark .is-warning.textarea:focus,html.theme--documenter-dark .is-warning.input:focus,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-warning:focus,html.theme--documenter-dark .is-warning.is-focused.textarea,html.theme--documenter-dark .is-warning.is-focused.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-focused,html.theme--documenter-dark .is-warning.textarea:active,html.theme--documenter-dark .is-warning.input:active,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-warning:active,html.theme--documenter-dark .is-warning.is-active.textarea,html.theme--documenter-dark .is-warning.is-active.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-active{box-shadow:0 0 0 0.125em rgba(173,129,0,0.25)}html.theme--documenter-dark .is-danger.textarea,html.theme--documenter-dark .is-danger.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-danger{border-color:#9e1b0d}html.theme--documenter-dark .is-danger.textarea:focus,html.theme--documenter-dark .is-danger.input:focus,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-danger:focus,html.theme--documenter-dark .is-danger.is-focused.textarea,html.theme--documenter-dark .is-danger.is-focused.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-focused,html.theme--documenter-dark .is-danger.textarea:active,html.theme--documenter-dark .is-danger.input:active,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-danger:active,html.theme--documenter-dark .is-danger.is-active.textarea,html.theme--documenter-dark .is-danger.is-active.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-active{box-shadow:0 0 0 0.125em rgba(158,27,13,0.25)}html.theme--documenter-dark .is-small.textarea,html.theme--documenter-dark .is-small.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input{border-radius:3px;font-size:.75rem}html.theme--documenter-dark .is-medium.textarea,html.theme--documenter-dark .is-medium.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-medium{font-size:1.25rem}html.theme--documenter-dark .is-large.textarea,html.theme--documenter-dark .is-large.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-large{font-size:1.5rem}html.theme--documenter-dark .is-fullwidth.textarea,html.theme--documenter-dark .is-fullwidth.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-fullwidth{display:block;width:100%}html.theme--documenter-dark .is-inline.textarea,html.theme--documenter-dark .is-inline.input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-inline{display:inline;width:auto}html.theme--documenter-dark .input.is-rounded,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input{border-radius:9999px;padding-left:calc(calc(0.75em - 1px) + 0.375em);padding-right:calc(calc(0.75em - 1px) + 0.375em)}html.theme--documenter-dark .input.is-static,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-static{background-color:transparent;border-color:transparent;box-shadow:none;padding-left:0;padding-right:0}html.theme--documenter-dark .textarea{display:block;max-width:100%;min-width:100%;padding:calc(0.75em - 1px);resize:vertical}html.theme--documenter-dark .textarea:not([rows]){max-height:40em;min-height:8em}html.theme--documenter-dark .textarea[rows]{height:initial}html.theme--documenter-dark .textarea.has-fixed-size{resize:none}html.theme--documenter-dark .radio,html.theme--documenter-dark .checkbox{cursor:pointer;display:inline-block;line-height:1.25;position:relative}html.theme--documenter-dark .radio input,html.theme--documenter-dark .checkbox input{cursor:pointer}html.theme--documenter-dark .radio:hover,html.theme--documenter-dark .checkbox:hover{color:#8c9b9d}html.theme--documenter-dark .radio[disabled],html.theme--documenter-dark .checkbox[disabled],fieldset[disabled] html.theme--documenter-dark .radio,fieldset[disabled] html.theme--documenter-dark .checkbox,html.theme--documenter-dark .radio input[disabled],html.theme--documenter-dark .checkbox input[disabled]{color:#fff;cursor:not-allowed}html.theme--documenter-dark .radio+.radio{margin-left:.5em}html.theme--documenter-dark .select{display:inline-block;max-width:100%;position:relative;vertical-align:top}html.theme--documenter-dark .select:not(.is-multiple){height:2.5em}html.theme--documenter-dark .select:not(.is-multiple):not(.is-loading)::after{border-color:#1abc9c;right:1.125em;z-index:4}html.theme--documenter-dark .select.is-rounded select,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.select select{border-radius:9999px;padding-left:1em}html.theme--documenter-dark .select select{cursor:pointer;display:block;font-size:1em;max-width:100%;outline:none}html.theme--documenter-dark .select select::-ms-expand{display:none}html.theme--documenter-dark .select select[disabled]:hover,fieldset[disabled] html.theme--documenter-dark .select select:hover{border-color:#282f2f}html.theme--documenter-dark .select select:not([multiple]){padding-right:2.5em}html.theme--documenter-dark .select select[multiple]{height:auto;padding:0}html.theme--documenter-dark .select select[multiple] option{padding:0.5em 1em}html.theme--documenter-dark .select:not(.is-multiple):not(.is-loading):hover::after{border-color:#8c9b9d}html.theme--documenter-dark .select.is-white:not(:hover)::after{border-color:#fff}html.theme--documenter-dark .select.is-white select{border-color:#fff}html.theme--documenter-dark .select.is-white select:hover,html.theme--documenter-dark .select.is-white select.is-hovered{border-color:#f2f2f2}html.theme--documenter-dark .select.is-white select:focus,html.theme--documenter-dark .select.is-white select.is-focused,html.theme--documenter-dark .select.is-white select:active,html.theme--documenter-dark .select.is-white select.is-active{box-shadow:0 0 0 0.125em rgba(255,255,255,0.25)}html.theme--documenter-dark .select.is-black:not(:hover)::after{border-color:#0a0a0a}html.theme--documenter-dark .select.is-black select{border-color:#0a0a0a}html.theme--documenter-dark .select.is-black select:hover,html.theme--documenter-dark .select.is-black select.is-hovered{border-color:#000}html.theme--documenter-dark .select.is-black select:focus,html.theme--documenter-dark .select.is-black select.is-focused,html.theme--documenter-dark .select.is-black select:active,html.theme--documenter-dark .select.is-black select.is-active{box-shadow:0 0 0 0.125em rgba(10,10,10,0.25)}html.theme--documenter-dark .select.is-light:not(:hover)::after{border-color:#ecf0f1}html.theme--documenter-dark .select.is-light select{border-color:#ecf0f1}html.theme--documenter-dark .select.is-light select:hover,html.theme--documenter-dark .select.is-light select.is-hovered{border-color:#dde4e6}html.theme--documenter-dark .select.is-light select:focus,html.theme--documenter-dark .select.is-light select.is-focused,html.theme--documenter-dark .select.is-light select:active,html.theme--documenter-dark .select.is-light select.is-active{box-shadow:0 0 0 0.125em rgba(236,240,241,0.25)}html.theme--documenter-dark .select.is-dark:not(:hover)::after,html.theme--documenter-dark .content kbd.select:not(:hover)::after{border-color:#282f2f}html.theme--documenter-dark .select.is-dark select,html.theme--documenter-dark .content kbd.select select{border-color:#282f2f}html.theme--documenter-dark .select.is-dark select:hover,html.theme--documenter-dark .content kbd.select select:hover,html.theme--documenter-dark .select.is-dark select.is-hovered,html.theme--documenter-dark .content kbd.select select.is-hovered{border-color:#1d2122}html.theme--documenter-dark .select.is-dark select:focus,html.theme--documenter-dark .content kbd.select select:focus,html.theme--documenter-dark .select.is-dark select.is-focused,html.theme--documenter-dark .content kbd.select select.is-focused,html.theme--documenter-dark .select.is-dark select:active,html.theme--documenter-dark .content kbd.select select:active,html.theme--documenter-dark .select.is-dark select.is-active,html.theme--documenter-dark .content kbd.select select.is-active{box-shadow:0 0 0 0.125em rgba(40,47,47,0.25)}html.theme--documenter-dark .select.is-primary:not(:hover)::after,html.theme--documenter-dark .docstring>section>a.select.docs-sourcelink:not(:hover)::after{border-color:#375a7f}html.theme--documenter-dark .select.is-primary select,html.theme--documenter-dark .docstring>section>a.select.docs-sourcelink select{border-color:#375a7f}html.theme--documenter-dark .select.is-primary select:hover,html.theme--documenter-dark .docstring>section>a.select.docs-sourcelink select:hover,html.theme--documenter-dark .select.is-primary select.is-hovered,html.theme--documenter-dark .docstring>section>a.select.docs-sourcelink select.is-hovered{border-color:#2f4d6d}html.theme--documenter-dark .select.is-primary select:focus,html.theme--documenter-dark .docstring>section>a.select.docs-sourcelink select:focus,html.theme--documenter-dark .select.is-primary select.is-focused,html.theme--documenter-dark .docstring>section>a.select.docs-sourcelink select.is-focused,html.theme--documenter-dark .select.is-primary select:active,html.theme--documenter-dark .docstring>section>a.select.docs-sourcelink select:active,html.theme--documenter-dark .select.is-primary select.is-active,html.theme--documenter-dark .docstring>section>a.select.docs-sourcelink select.is-active{box-shadow:0 0 0 0.125em rgba(55,90,127,0.25)}html.theme--documenter-dark .select.is-link:not(:hover)::after{border-color:#1abc9c}html.theme--documenter-dark .select.is-link select{border-color:#1abc9c}html.theme--documenter-dark .select.is-link select:hover,html.theme--documenter-dark .select.is-link select.is-hovered{border-color:#17a689}html.theme--documenter-dark .select.is-link select:focus,html.theme--documenter-dark .select.is-link select.is-focused,html.theme--documenter-dark .select.is-link select:active,html.theme--documenter-dark .select.is-link select.is-active{box-shadow:0 0 0 0.125em rgba(26,188,156,0.25)}html.theme--documenter-dark .select.is-info:not(:hover)::after{border-color:#024c7d}html.theme--documenter-dark .select.is-info select{border-color:#024c7d}html.theme--documenter-dark .select.is-info select:hover,html.theme--documenter-dark .select.is-info select.is-hovered{border-color:#023d64}html.theme--documenter-dark .select.is-info select:focus,html.theme--documenter-dark .select.is-info select.is-focused,html.theme--documenter-dark .select.is-info select:active,html.theme--documenter-dark .select.is-info select.is-active{box-shadow:0 0 0 0.125em rgba(2,76,125,0.25)}html.theme--documenter-dark .select.is-success:not(:hover)::after{border-color:#008438}html.theme--documenter-dark .select.is-success select{border-color:#008438}html.theme--documenter-dark .select.is-success select:hover,html.theme--documenter-dark .select.is-success select.is-hovered{border-color:#006b2d}html.theme--documenter-dark .select.is-success select:focus,html.theme--documenter-dark .select.is-success select.is-focused,html.theme--documenter-dark .select.is-success select:active,html.theme--documenter-dark .select.is-success select.is-active{box-shadow:0 0 0 0.125em rgba(0,132,56,0.25)}html.theme--documenter-dark .select.is-warning:not(:hover)::after{border-color:#ad8100}html.theme--documenter-dark .select.is-warning select{border-color:#ad8100}html.theme--documenter-dark .select.is-warning select:hover,html.theme--documenter-dark .select.is-warning select.is-hovered{border-color:#946e00}html.theme--documenter-dark .select.is-warning select:focus,html.theme--documenter-dark .select.is-warning select.is-focused,html.theme--documenter-dark .select.is-warning select:active,html.theme--documenter-dark .select.is-warning select.is-active{box-shadow:0 0 0 0.125em rgba(173,129,0,0.25)}html.theme--documenter-dark .select.is-danger:not(:hover)::after{border-color:#9e1b0d}html.theme--documenter-dark .select.is-danger select{border-color:#9e1b0d}html.theme--documenter-dark .select.is-danger select:hover,html.theme--documenter-dark .select.is-danger select.is-hovered{border-color:#86170b}html.theme--documenter-dark .select.is-danger select:focus,html.theme--documenter-dark .select.is-danger select.is-focused,html.theme--documenter-dark .select.is-danger select:active,html.theme--documenter-dark .select.is-danger select.is-active{box-shadow:0 0 0 0.125em rgba(158,27,13,0.25)}html.theme--documenter-dark .select.is-small,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.select{border-radius:3px;font-size:.75rem}html.theme--documenter-dark .select.is-medium{font-size:1.25rem}html.theme--documenter-dark .select.is-large{font-size:1.5rem}html.theme--documenter-dark .select.is-disabled::after{border-color:#fff !important;opacity:0.5}html.theme--documenter-dark .select.is-fullwidth{width:100%}html.theme--documenter-dark .select.is-fullwidth select{width:100%}html.theme--documenter-dark .select.is-loading::after{margin-top:0;position:absolute;right:.625em;top:0.625em;transform:none}html.theme--documenter-dark .select.is-loading.is-small:after,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-loading:after{font-size:.75rem}html.theme--documenter-dark .select.is-loading.is-medium:after{font-size:1.25rem}html.theme--documenter-dark .select.is-loading.is-large:after{font-size:1.5rem}html.theme--documenter-dark .file{align-items:stretch;display:flex;justify-content:flex-start;position:relative}html.theme--documenter-dark .file.is-white .file-cta{background-color:#fff;border-color:transparent;color:#0a0a0a}html.theme--documenter-dark .file.is-white:hover .file-cta,html.theme--documenter-dark .file.is-white.is-hovered .file-cta{background-color:#f9f9f9;border-color:transparent;color:#0a0a0a}html.theme--documenter-dark .file.is-white:focus .file-cta,html.theme--documenter-dark .file.is-white.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(255,255,255,0.25);color:#0a0a0a}html.theme--documenter-dark .file.is-white:active .file-cta,html.theme--documenter-dark .file.is-white.is-active .file-cta{background-color:#f2f2f2;border-color:transparent;color:#0a0a0a}html.theme--documenter-dark .file.is-black .file-cta{background-color:#0a0a0a;border-color:transparent;color:#fff}html.theme--documenter-dark .file.is-black:hover .file-cta,html.theme--documenter-dark .file.is-black.is-hovered .file-cta{background-color:#040404;border-color:transparent;color:#fff}html.theme--documenter-dark .file.is-black:focus .file-cta,html.theme--documenter-dark .file.is-black.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(10,10,10,0.25);color:#fff}html.theme--documenter-dark .file.is-black:active .file-cta,html.theme--documenter-dark .file.is-black.is-active .file-cta{background-color:#000;border-color:transparent;color:#fff}html.theme--documenter-dark .file.is-light .file-cta{background-color:#ecf0f1;border-color:transparent;color:rgba(0,0,0,0.7)}html.theme--documenter-dark .file.is-light:hover .file-cta,html.theme--documenter-dark .file.is-light.is-hovered .file-cta{background-color:#e5eaec;border-color:transparent;color:rgba(0,0,0,0.7)}html.theme--documenter-dark .file.is-light:focus .file-cta,html.theme--documenter-dark .file.is-light.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(236,240,241,0.25);color:rgba(0,0,0,0.7)}html.theme--documenter-dark .file.is-light:active .file-cta,html.theme--documenter-dark .file.is-light.is-active .file-cta{background-color:#dde4e6;border-color:transparent;color:rgba(0,0,0,0.7)}html.theme--documenter-dark .file.is-dark .file-cta,html.theme--documenter-dark .content kbd.file .file-cta{background-color:#282f2f;border-color:transparent;color:#fff}html.theme--documenter-dark .file.is-dark:hover .file-cta,html.theme--documenter-dark .content kbd.file:hover .file-cta,html.theme--documenter-dark .file.is-dark.is-hovered .file-cta,html.theme--documenter-dark .content kbd.file.is-hovered .file-cta{background-color:#232829;border-color:transparent;color:#fff}html.theme--documenter-dark .file.is-dark:focus .file-cta,html.theme--documenter-dark .content kbd.file:focus .file-cta,html.theme--documenter-dark .file.is-dark.is-focused .file-cta,html.theme--documenter-dark .content kbd.file.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(40,47,47,0.25);color:#fff}html.theme--documenter-dark .file.is-dark:active .file-cta,html.theme--documenter-dark .content kbd.file:active .file-cta,html.theme--documenter-dark .file.is-dark.is-active .file-cta,html.theme--documenter-dark .content kbd.file.is-active .file-cta{background-color:#1d2122;border-color:transparent;color:#fff}html.theme--documenter-dark .file.is-primary .file-cta,html.theme--documenter-dark .docstring>section>a.file.docs-sourcelink .file-cta{background-color:#375a7f;border-color:transparent;color:#fff}html.theme--documenter-dark .file.is-primary:hover .file-cta,html.theme--documenter-dark .docstring>section>a.file.docs-sourcelink:hover .file-cta,html.theme--documenter-dark .file.is-primary.is-hovered .file-cta,html.theme--documenter-dark .docstring>section>a.file.is-hovered.docs-sourcelink .file-cta{background-color:#335476;border-color:transparent;color:#fff}html.theme--documenter-dark .file.is-primary:focus .file-cta,html.theme--documenter-dark .docstring>section>a.file.docs-sourcelink:focus .file-cta,html.theme--documenter-dark .file.is-primary.is-focused .file-cta,html.theme--documenter-dark .docstring>section>a.file.is-focused.docs-sourcelink .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(55,90,127,0.25);color:#fff}html.theme--documenter-dark .file.is-primary:active .file-cta,html.theme--documenter-dark .docstring>section>a.file.docs-sourcelink:active .file-cta,html.theme--documenter-dark .file.is-primary.is-active .file-cta,html.theme--documenter-dark .docstring>section>a.file.is-active.docs-sourcelink .file-cta{background-color:#2f4d6d;border-color:transparent;color:#fff}html.theme--documenter-dark .file.is-link .file-cta{background-color:#1abc9c;border-color:transparent;color:#fff}html.theme--documenter-dark .file.is-link:hover .file-cta,html.theme--documenter-dark .file.is-link.is-hovered .file-cta{background-color:#18b193;border-color:transparent;color:#fff}html.theme--documenter-dark .file.is-link:focus .file-cta,html.theme--documenter-dark .file.is-link.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(26,188,156,0.25);color:#fff}html.theme--documenter-dark .file.is-link:active .file-cta,html.theme--documenter-dark .file.is-link.is-active .file-cta{background-color:#17a689;border-color:transparent;color:#fff}html.theme--documenter-dark .file.is-info .file-cta{background-color:#024c7d;border-color:transparent;color:#fff}html.theme--documenter-dark .file.is-info:hover .file-cta,html.theme--documenter-dark .file.is-info.is-hovered .file-cta{background-color:#024470;border-color:transparent;color:#fff}html.theme--documenter-dark .file.is-info:focus .file-cta,html.theme--documenter-dark .file.is-info.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(2,76,125,0.25);color:#fff}html.theme--documenter-dark .file.is-info:active .file-cta,html.theme--documenter-dark .file.is-info.is-active .file-cta{background-color:#023d64;border-color:transparent;color:#fff}html.theme--documenter-dark .file.is-success .file-cta{background-color:#008438;border-color:transparent;color:#fff}html.theme--documenter-dark .file.is-success:hover .file-cta,html.theme--documenter-dark .file.is-success.is-hovered .file-cta{background-color:#073;border-color:transparent;color:#fff}html.theme--documenter-dark .file.is-success:focus .file-cta,html.theme--documenter-dark .file.is-success.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(0,132,56,0.25);color:#fff}html.theme--documenter-dark .file.is-success:active .file-cta,html.theme--documenter-dark .file.is-success.is-active .file-cta{background-color:#006b2d;border-color:transparent;color:#fff}html.theme--documenter-dark .file.is-warning .file-cta{background-color:#ad8100;border-color:transparent;color:#fff}html.theme--documenter-dark .file.is-warning:hover .file-cta,html.theme--documenter-dark .file.is-warning.is-hovered .file-cta{background-color:#a07700;border-color:transparent;color:#fff}html.theme--documenter-dark .file.is-warning:focus .file-cta,html.theme--documenter-dark .file.is-warning.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(173,129,0,0.25);color:#fff}html.theme--documenter-dark .file.is-warning:active .file-cta,html.theme--documenter-dark .file.is-warning.is-active .file-cta{background-color:#946e00;border-color:transparent;color:#fff}html.theme--documenter-dark .file.is-danger .file-cta{background-color:#9e1b0d;border-color:transparent;color:#fff}html.theme--documenter-dark .file.is-danger:hover .file-cta,html.theme--documenter-dark .file.is-danger.is-hovered .file-cta{background-color:#92190c;border-color:transparent;color:#fff}html.theme--documenter-dark .file.is-danger:focus .file-cta,html.theme--documenter-dark .file.is-danger.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(158,27,13,0.25);color:#fff}html.theme--documenter-dark .file.is-danger:active .file-cta,html.theme--documenter-dark .file.is-danger.is-active .file-cta{background-color:#86170b;border-color:transparent;color:#fff}html.theme--documenter-dark .file.is-small,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.file{font-size:.75rem}html.theme--documenter-dark .file.is-normal{font-size:1rem}html.theme--documenter-dark .file.is-medium{font-size:1.25rem}html.theme--documenter-dark .file.is-medium .file-icon .fa{font-size:21px}html.theme--documenter-dark .file.is-large{font-size:1.5rem}html.theme--documenter-dark .file.is-large .file-icon .fa{font-size:28px}html.theme--documenter-dark .file.has-name .file-cta{border-bottom-right-radius:0;border-top-right-radius:0}html.theme--documenter-dark .file.has-name .file-name{border-bottom-left-radius:0;border-top-left-radius:0}html.theme--documenter-dark .file.has-name.is-empty .file-cta{border-radius:.4em}html.theme--documenter-dark .file.has-name.is-empty .file-name{display:none}html.theme--documenter-dark .file.is-boxed .file-label{flex-direction:column}html.theme--documenter-dark .file.is-boxed .file-cta{flex-direction:column;height:auto;padding:1em 3em}html.theme--documenter-dark .file.is-boxed .file-name{border-width:0 1px 1px}html.theme--documenter-dark .file.is-boxed .file-icon{height:1.5em;width:1.5em}html.theme--documenter-dark .file.is-boxed .file-icon .fa{font-size:21px}html.theme--documenter-dark .file.is-boxed.is-small .file-icon .fa,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-boxed .file-icon .fa{font-size:14px}html.theme--documenter-dark .file.is-boxed.is-medium .file-icon .fa{font-size:28px}html.theme--documenter-dark .file.is-boxed.is-large .file-icon .fa{font-size:35px}html.theme--documenter-dark .file.is-boxed.has-name .file-cta{border-radius:.4em .4em 0 0}html.theme--documenter-dark .file.is-boxed.has-name .file-name{border-radius:0 0 .4em .4em;border-width:0 1px 1px}html.theme--documenter-dark .file.is-centered{justify-content:center}html.theme--documenter-dark .file.is-fullwidth .file-label{width:100%}html.theme--documenter-dark .file.is-fullwidth .file-name{flex-grow:1;max-width:none}html.theme--documenter-dark .file.is-right{justify-content:flex-end}html.theme--documenter-dark .file.is-right .file-cta{border-radius:0 .4em .4em 0}html.theme--documenter-dark .file.is-right .file-name{border-radius:.4em 0 0 .4em;border-width:1px 0 1px 1px;order:-1}html.theme--documenter-dark .file-label{align-items:stretch;display:flex;cursor:pointer;justify-content:flex-start;overflow:hidden;position:relative}html.theme--documenter-dark .file-label:hover .file-cta{background-color:#232829;color:#f2f2f2}html.theme--documenter-dark .file-label:hover .file-name{border-color:#596668}html.theme--documenter-dark .file-label:active .file-cta{background-color:#1d2122;color:#f2f2f2}html.theme--documenter-dark .file-label:active .file-name{border-color:#535f61}html.theme--documenter-dark .file-input{height:100%;left:0;opacity:0;outline:none;position:absolute;top:0;width:100%}html.theme--documenter-dark .file-cta,html.theme--documenter-dark .file-name{border-color:#5e6d6f;border-radius:.4em;font-size:1em;padding-left:1em;padding-right:1em;white-space:nowrap}html.theme--documenter-dark .file-cta{background-color:#282f2f;color:#fff}html.theme--documenter-dark .file-name{border-color:#5e6d6f;border-style:solid;border-width:1px 1px 1px 0;display:block;max-width:16em;overflow:hidden;text-align:inherit;text-overflow:ellipsis}html.theme--documenter-dark .file-icon{align-items:center;display:flex;height:1em;justify-content:center;margin-right:.5em;width:1em}html.theme--documenter-dark .file-icon .fa{font-size:14px}html.theme--documenter-dark .label{color:#f2f2f2;display:block;font-size:1rem;font-weight:700}html.theme--documenter-dark .label:not(:last-child){margin-bottom:0.5em}html.theme--documenter-dark .label.is-small,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.label{font-size:.75rem}html.theme--documenter-dark .label.is-medium{font-size:1.25rem}html.theme--documenter-dark .label.is-large{font-size:1.5rem}html.theme--documenter-dark .help{display:block;font-size:.75rem;margin-top:0.25rem}html.theme--documenter-dark .help.is-white{color:#fff}html.theme--documenter-dark .help.is-black{color:#0a0a0a}html.theme--documenter-dark .help.is-light{color:#ecf0f1}html.theme--documenter-dark .help.is-dark,html.theme--documenter-dark .content kbd.help{color:#282f2f}html.theme--documenter-dark .help.is-primary,html.theme--documenter-dark .docstring>section>a.help.docs-sourcelink{color:#375a7f}html.theme--documenter-dark .help.is-link{color:#1abc9c}html.theme--documenter-dark .help.is-info{color:#024c7d}html.theme--documenter-dark .help.is-success{color:#008438}html.theme--documenter-dark .help.is-warning{color:#ad8100}html.theme--documenter-dark .help.is-danger{color:#9e1b0d}html.theme--documenter-dark .field:not(:last-child){margin-bottom:0.75rem}html.theme--documenter-dark .field.has-addons{display:flex;justify-content:flex-start}html.theme--documenter-dark .field.has-addons .control:not(:last-child){margin-right:-1px}html.theme--documenter-dark .field.has-addons .control:not(:first-child):not(:last-child) .button,html.theme--documenter-dark .field.has-addons .control:not(:first-child):not(:last-child) .input,html.theme--documenter-dark .field.has-addons .control:not(:first-child):not(:last-child) #documenter .docs-sidebar form.docs-search>input,html.theme--documenter-dark #documenter .docs-sidebar .field.has-addons .control:not(:first-child):not(:last-child) form.docs-search>input,html.theme--documenter-dark .field.has-addons .control:not(:first-child):not(:last-child) .select select{border-radius:0}html.theme--documenter-dark .field.has-addons .control:first-child:not(:only-child) .button,html.theme--documenter-dark .field.has-addons .control:first-child:not(:only-child) .input,html.theme--documenter-dark .field.has-addons .control:first-child:not(:only-child) #documenter .docs-sidebar form.docs-search>input,html.theme--documenter-dark #documenter .docs-sidebar .field.has-addons .control:first-child:not(:only-child) form.docs-search>input,html.theme--documenter-dark .field.has-addons .control:first-child:not(:only-child) .select select{border-bottom-right-radius:0;border-top-right-radius:0}html.theme--documenter-dark .field.has-addons .control:last-child:not(:only-child) .button,html.theme--documenter-dark .field.has-addons .control:last-child:not(:only-child) .input,html.theme--documenter-dark .field.has-addons .control:last-child:not(:only-child) #documenter .docs-sidebar form.docs-search>input,html.theme--documenter-dark #documenter .docs-sidebar .field.has-addons .control:last-child:not(:only-child) form.docs-search>input,html.theme--documenter-dark .field.has-addons .control:last-child:not(:only-child) .select select{border-bottom-left-radius:0;border-top-left-radius:0}html.theme--documenter-dark .field.has-addons .control .button:not([disabled]):hover,html.theme--documenter-dark .field.has-addons .control .button.is-hovered:not([disabled]),html.theme--documenter-dark .field.has-addons .control .input:not([disabled]):hover,html.theme--documenter-dark .field.has-addons .control #documenter .docs-sidebar form.docs-search>input:not([disabled]):hover,html.theme--documenter-dark #documenter .docs-sidebar .field.has-addons .control form.docs-search>input:not([disabled]):hover,html.theme--documenter-dark .field.has-addons .control .input.is-hovered:not([disabled]),html.theme--documenter-dark .field.has-addons .control #documenter .docs-sidebar form.docs-search>input.is-hovered:not([disabled]),html.theme--documenter-dark #documenter .docs-sidebar .field.has-addons .control form.docs-search>input.is-hovered:not([disabled]),html.theme--documenter-dark .field.has-addons .control .select select:not([disabled]):hover,html.theme--documenter-dark .field.has-addons .control .select select.is-hovered:not([disabled]){z-index:2}html.theme--documenter-dark .field.has-addons .control .button:not([disabled]):focus,html.theme--documenter-dark .field.has-addons .control .button.is-focused:not([disabled]),html.theme--documenter-dark .field.has-addons .control .button:not([disabled]):active,html.theme--documenter-dark .field.has-addons .control .button.is-active:not([disabled]),html.theme--documenter-dark .field.has-addons .control .input:not([disabled]):focus,html.theme--documenter-dark .field.has-addons .control #documenter .docs-sidebar form.docs-search>input:not([disabled]):focus,html.theme--documenter-dark #documenter .docs-sidebar .field.has-addons .control form.docs-search>input:not([disabled]):focus,html.theme--documenter-dark .field.has-addons .control .input.is-focused:not([disabled]),html.theme--documenter-dark .field.has-addons .control #documenter .docs-sidebar form.docs-search>input.is-focused:not([disabled]),html.theme--documenter-dark #documenter .docs-sidebar .field.has-addons .control form.docs-search>input.is-focused:not([disabled]),html.theme--documenter-dark .field.has-addons .control .input:not([disabled]):active,html.theme--documenter-dark .field.has-addons .control #documenter .docs-sidebar form.docs-search>input:not([disabled]):active,html.theme--documenter-dark #documenter .docs-sidebar .field.has-addons .control form.docs-search>input:not([disabled]):active,html.theme--documenter-dark .field.has-addons .control .input.is-active:not([disabled]),html.theme--documenter-dark .field.has-addons .control #documenter .docs-sidebar form.docs-search>input.is-active:not([disabled]),html.theme--documenter-dark #documenter .docs-sidebar .field.has-addons .control form.docs-search>input.is-active:not([disabled]),html.theme--documenter-dark .field.has-addons .control .select select:not([disabled]):focus,html.theme--documenter-dark .field.has-addons .control .select select.is-focused:not([disabled]),html.theme--documenter-dark .field.has-addons .control .select select:not([disabled]):active,html.theme--documenter-dark .field.has-addons .control .select select.is-active:not([disabled]){z-index:3}html.theme--documenter-dark .field.has-addons .control .button:not([disabled]):focus:hover,html.theme--documenter-dark .field.has-addons .control .button.is-focused:not([disabled]):hover,html.theme--documenter-dark .field.has-addons .control .button:not([disabled]):active:hover,html.theme--documenter-dark .field.has-addons .control .button.is-active:not([disabled]):hover,html.theme--documenter-dark .field.has-addons .control .input:not([disabled]):focus:hover,html.theme--documenter-dark .field.has-addons .control #documenter .docs-sidebar form.docs-search>input:not([disabled]):focus:hover,html.theme--documenter-dark #documenter .docs-sidebar .field.has-addons .control form.docs-search>input:not([disabled]):focus:hover,html.theme--documenter-dark .field.has-addons .control .input.is-focused:not([disabled]):hover,html.theme--documenter-dark .field.has-addons .control #documenter .docs-sidebar form.docs-search>input.is-focused:not([disabled]):hover,html.theme--documenter-dark #documenter .docs-sidebar .field.has-addons .control form.docs-search>input.is-focused:not([disabled]):hover,html.theme--documenter-dark .field.has-addons .control .input:not([disabled]):active:hover,html.theme--documenter-dark .field.has-addons .control #documenter .docs-sidebar form.docs-search>input:not([disabled]):active:hover,html.theme--documenter-dark #documenter .docs-sidebar .field.has-addons .control form.docs-search>input:not([disabled]):active:hover,html.theme--documenter-dark .field.has-addons .control .input.is-active:not([disabled]):hover,html.theme--documenter-dark .field.has-addons .control #documenter .docs-sidebar form.docs-search>input.is-active:not([disabled]):hover,html.theme--documenter-dark #documenter .docs-sidebar .field.has-addons .control form.docs-search>input.is-active:not([disabled]):hover,html.theme--documenter-dark .field.has-addons .control .select select:not([disabled]):focus:hover,html.theme--documenter-dark .field.has-addons .control .select select.is-focused:not([disabled]):hover,html.theme--documenter-dark .field.has-addons .control .select select:not([disabled]):active:hover,html.theme--documenter-dark .field.has-addons .control .select select.is-active:not([disabled]):hover{z-index:4}html.theme--documenter-dark .field.has-addons .control.is-expanded{flex-grow:1;flex-shrink:1}html.theme--documenter-dark .field.has-addons.has-addons-centered{justify-content:center}html.theme--documenter-dark .field.has-addons.has-addons-right{justify-content:flex-end}html.theme--documenter-dark .field.has-addons.has-addons-fullwidth .control{flex-grow:1;flex-shrink:0}html.theme--documenter-dark .field.is-grouped{display:flex;justify-content:flex-start}html.theme--documenter-dark .field.is-grouped>.control{flex-shrink:0}html.theme--documenter-dark .field.is-grouped>.control:not(:last-child){margin-bottom:0;margin-right:.75rem}html.theme--documenter-dark .field.is-grouped>.control.is-expanded{flex-grow:1;flex-shrink:1}html.theme--documenter-dark .field.is-grouped.is-grouped-centered{justify-content:center}html.theme--documenter-dark .field.is-grouped.is-grouped-right{justify-content:flex-end}html.theme--documenter-dark .field.is-grouped.is-grouped-multiline{flex-wrap:wrap}html.theme--documenter-dark .field.is-grouped.is-grouped-multiline>.control:last-child,html.theme--documenter-dark .field.is-grouped.is-grouped-multiline>.control:not(:last-child){margin-bottom:0.75rem}html.theme--documenter-dark .field.is-grouped.is-grouped-multiline:last-child{margin-bottom:-0.75rem}html.theme--documenter-dark .field.is-grouped.is-grouped-multiline:not(:last-child){margin-bottom:0}@media screen and (min-width: 769px),print{html.theme--documenter-dark .field.is-horizontal{display:flex}}html.theme--documenter-dark .field-label .label{font-size:inherit}@media screen and (max-width: 768px){html.theme--documenter-dark .field-label{margin-bottom:0.5rem}}@media screen and (min-width: 769px),print{html.theme--documenter-dark .field-label{flex-basis:0;flex-grow:1;flex-shrink:0;margin-right:1.5rem;text-align:right}html.theme--documenter-dark .field-label.is-small,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.field-label{font-size:.75rem;padding-top:0.375em}html.theme--documenter-dark .field-label.is-normal{padding-top:0.375em}html.theme--documenter-dark .field-label.is-medium{font-size:1.25rem;padding-top:0.375em}html.theme--documenter-dark .field-label.is-large{font-size:1.5rem;padding-top:0.375em}}html.theme--documenter-dark .field-body .field .field{margin-bottom:0}@media screen and (min-width: 769px),print{html.theme--documenter-dark .field-body{display:flex;flex-basis:0;flex-grow:5;flex-shrink:1}html.theme--documenter-dark .field-body .field{margin-bottom:0}html.theme--documenter-dark .field-body>.field{flex-shrink:1}html.theme--documenter-dark .field-body>.field:not(.is-narrow){flex-grow:1}html.theme--documenter-dark .field-body>.field:not(:last-child){margin-right:.75rem}}html.theme--documenter-dark .control{box-sizing:border-box;clear:both;font-size:1rem;position:relative;text-align:inherit}html.theme--documenter-dark .control.has-icons-left .input:focus~.icon,html.theme--documenter-dark .control.has-icons-left #documenter .docs-sidebar form.docs-search>input:focus~.icon,html.theme--documenter-dark #documenter .docs-sidebar .control.has-icons-left form.docs-search>input:focus~.icon,html.theme--documenter-dark .control.has-icons-left .select:focus~.icon,html.theme--documenter-dark .control.has-icons-right .input:focus~.icon,html.theme--documenter-dark .control.has-icons-right #documenter .docs-sidebar form.docs-search>input:focus~.icon,html.theme--documenter-dark #documenter .docs-sidebar .control.has-icons-right form.docs-search>input:focus~.icon,html.theme--documenter-dark .control.has-icons-right .select:focus~.icon{color:#282f2f}html.theme--documenter-dark .control.has-icons-left .input.is-small~.icon,html.theme--documenter-dark .control.has-icons-left #documenter .docs-sidebar form.docs-search>input~.icon,html.theme--documenter-dark #documenter .docs-sidebar .control.has-icons-left form.docs-search>input~.icon,html.theme--documenter-dark .control.has-icons-left .select.is-small~.icon,html.theme--documenter-dark .control.has-icons-right .input.is-small~.icon,html.theme--documenter-dark .control.has-icons-right #documenter .docs-sidebar form.docs-search>input~.icon,html.theme--documenter-dark #documenter .docs-sidebar .control.has-icons-right form.docs-search>input~.icon,html.theme--documenter-dark .control.has-icons-right .select.is-small~.icon{font-size:.75rem}html.theme--documenter-dark .control.has-icons-left .input.is-medium~.icon,html.theme--documenter-dark .control.has-icons-left #documenter .docs-sidebar form.docs-search>input.is-medium~.icon,html.theme--documenter-dark #documenter .docs-sidebar .control.has-icons-left form.docs-search>input.is-medium~.icon,html.theme--documenter-dark .control.has-icons-left .select.is-medium~.icon,html.theme--documenter-dark .control.has-icons-right .input.is-medium~.icon,html.theme--documenter-dark .control.has-icons-right #documenter .docs-sidebar form.docs-search>input.is-medium~.icon,html.theme--documenter-dark #documenter .docs-sidebar .control.has-icons-right form.docs-search>input.is-medium~.icon,html.theme--documenter-dark .control.has-icons-right .select.is-medium~.icon{font-size:1.25rem}html.theme--documenter-dark .control.has-icons-left .input.is-large~.icon,html.theme--documenter-dark .control.has-icons-left #documenter .docs-sidebar form.docs-search>input.is-large~.icon,html.theme--documenter-dark #documenter .docs-sidebar .control.has-icons-left form.docs-search>input.is-large~.icon,html.theme--documenter-dark .control.has-icons-left .select.is-large~.icon,html.theme--documenter-dark .control.has-icons-right .input.is-large~.icon,html.theme--documenter-dark .control.has-icons-right #documenter .docs-sidebar form.docs-search>input.is-large~.icon,html.theme--documenter-dark #documenter .docs-sidebar .control.has-icons-right form.docs-search>input.is-large~.icon,html.theme--documenter-dark .control.has-icons-right .select.is-large~.icon{font-size:1.5rem}html.theme--documenter-dark .control.has-icons-left .icon,html.theme--documenter-dark .control.has-icons-right .icon{color:#5e6d6f;height:2.5em;pointer-events:none;position:absolute;top:0;width:2.5em;z-index:4}html.theme--documenter-dark .control.has-icons-left .input,html.theme--documenter-dark .control.has-icons-left #documenter .docs-sidebar form.docs-search>input,html.theme--documenter-dark #documenter .docs-sidebar .control.has-icons-left form.docs-search>input,html.theme--documenter-dark .control.has-icons-left .select select{padding-left:2.5em}html.theme--documenter-dark .control.has-icons-left .icon.is-left{left:0}html.theme--documenter-dark .control.has-icons-right .input,html.theme--documenter-dark .control.has-icons-right #documenter .docs-sidebar form.docs-search>input,html.theme--documenter-dark #documenter .docs-sidebar .control.has-icons-right form.docs-search>input,html.theme--documenter-dark .control.has-icons-right .select select{padding-right:2.5em}html.theme--documenter-dark .control.has-icons-right .icon.is-right{right:0}html.theme--documenter-dark .control.is-loading::after{position:absolute !important;right:.625em;top:0.625em;z-index:4}html.theme--documenter-dark .control.is-loading.is-small:after,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.is-loading:after{font-size:.75rem}html.theme--documenter-dark .control.is-loading.is-medium:after{font-size:1.25rem}html.theme--documenter-dark .control.is-loading.is-large:after{font-size:1.5rem}html.theme--documenter-dark .breadcrumb{font-size:1rem;white-space:nowrap}html.theme--documenter-dark .breadcrumb a{align-items:center;color:#1abc9c;display:flex;justify-content:center;padding:0 .75em}html.theme--documenter-dark .breadcrumb a:hover{color:#1dd2af}html.theme--documenter-dark .breadcrumb li{align-items:center;display:flex}html.theme--documenter-dark .breadcrumb li:first-child a{padding-left:0}html.theme--documenter-dark .breadcrumb li.is-active a{color:#f2f2f2;cursor:default;pointer-events:none}html.theme--documenter-dark .breadcrumb li+li::before{color:#8c9b9d;content:"\0002f"}html.theme--documenter-dark .breadcrumb ul,html.theme--documenter-dark .breadcrumb ol{align-items:flex-start;display:flex;flex-wrap:wrap;justify-content:flex-start}html.theme--documenter-dark .breadcrumb .icon:first-child{margin-right:.5em}html.theme--documenter-dark .breadcrumb .icon:last-child{margin-left:.5em}html.theme--documenter-dark .breadcrumb.is-centered ol,html.theme--documenter-dark .breadcrumb.is-centered ul{justify-content:center}html.theme--documenter-dark .breadcrumb.is-right ol,html.theme--documenter-dark .breadcrumb.is-right ul{justify-content:flex-end}html.theme--documenter-dark .breadcrumb.is-small,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.breadcrumb{font-size:.75rem}html.theme--documenter-dark .breadcrumb.is-medium{font-size:1.25rem}html.theme--documenter-dark .breadcrumb.is-large{font-size:1.5rem}html.theme--documenter-dark .breadcrumb.has-arrow-separator li+li::before{content:"\02192"}html.theme--documenter-dark .breadcrumb.has-bullet-separator li+li::before{content:"\02022"}html.theme--documenter-dark .breadcrumb.has-dot-separator li+li::before{content:"\000b7"}html.theme--documenter-dark .breadcrumb.has-succeeds-separator li+li::before{content:"\0227B"}html.theme--documenter-dark .card{background-color:#fff;border-radius:.25rem;box-shadow:#171717;color:#fff;max-width:100%;position:relative}html.theme--documenter-dark .card-footer:first-child,html.theme--documenter-dark .card-content:first-child,html.theme--documenter-dark .card-header:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}html.theme--documenter-dark .card-footer:last-child,html.theme--documenter-dark .card-content:last-child,html.theme--documenter-dark .card-header:last-child{border-bottom-left-radius:.25rem;border-bottom-right-radius:.25rem}html.theme--documenter-dark .card-header{background-color:rgba(0,0,0,0);align-items:stretch;box-shadow:0 0.125em 0.25em rgba(10,10,10,0.1);display:flex}html.theme--documenter-dark .card-header-title{align-items:center;color:#f2f2f2;display:flex;flex-grow:1;font-weight:700;padding:0.75rem 1rem}html.theme--documenter-dark .card-header-title.is-centered{justify-content:center}html.theme--documenter-dark .card-header-icon{-moz-appearance:none;-webkit-appearance:none;appearance:none;background:none;border:none;color:currentColor;font-family:inherit;font-size:1em;margin:0;padding:0;align-items:center;cursor:pointer;display:flex;justify-content:center;padding:0.75rem 1rem}html.theme--documenter-dark .card-image{display:block;position:relative}html.theme--documenter-dark .card-image:first-child img{border-top-left-radius:.25rem;border-top-right-radius:.25rem}html.theme--documenter-dark .card-image:last-child img{border-bottom-left-radius:.25rem;border-bottom-right-radius:.25rem}html.theme--documenter-dark .card-content{background-color:rgba(0,0,0,0);padding:1.5rem}html.theme--documenter-dark .card-footer{background-color:rgba(0,0,0,0);border-top:1px solid #ededed;align-items:stretch;display:flex}html.theme--documenter-dark .card-footer-item{align-items:center;display:flex;flex-basis:0;flex-grow:1;flex-shrink:0;justify-content:center;padding:.75rem}html.theme--documenter-dark .card-footer-item:not(:last-child){border-right:1px solid #ededed}html.theme--documenter-dark .card .media:not(:last-child){margin-bottom:1.5rem}html.theme--documenter-dark .dropdown{display:inline-flex;position:relative;vertical-align:top}html.theme--documenter-dark .dropdown.is-active .dropdown-menu,html.theme--documenter-dark .dropdown.is-hoverable:hover .dropdown-menu{display:block}html.theme--documenter-dark .dropdown.is-right .dropdown-menu{left:auto;right:0}html.theme--documenter-dark .dropdown.is-up .dropdown-menu{bottom:100%;padding-bottom:4px;padding-top:initial;top:auto}html.theme--documenter-dark .dropdown-menu{display:none;left:0;min-width:12rem;padding-top:4px;position:absolute;top:100%;z-index:20}html.theme--documenter-dark .dropdown-content{background-color:#282f2f;border-radius:.4em;box-shadow:#171717;padding-bottom:.5rem;padding-top:.5rem}html.theme--documenter-dark .dropdown-item{color:#fff;display:block;font-size:0.875rem;line-height:1.5;padding:0.375rem 1rem;position:relative}html.theme--documenter-dark a.dropdown-item,html.theme--documenter-dark button.dropdown-item{padding-right:3rem;text-align:inherit;white-space:nowrap;width:100%}html.theme--documenter-dark a.dropdown-item:hover,html.theme--documenter-dark button.dropdown-item:hover{background-color:#282f2f;color:#0a0a0a}html.theme--documenter-dark a.dropdown-item.is-active,html.theme--documenter-dark button.dropdown-item.is-active{background-color:#1abc9c;color:#fff}html.theme--documenter-dark .dropdown-divider{background-color:#ededed;border:none;display:block;height:1px;margin:0.5rem 0}html.theme--documenter-dark .level{align-items:center;justify-content:space-between}html.theme--documenter-dark .level code{border-radius:.4em}html.theme--documenter-dark .level img{display:inline-block;vertical-align:top}html.theme--documenter-dark .level.is-mobile{display:flex}html.theme--documenter-dark .level.is-mobile .level-left,html.theme--documenter-dark .level.is-mobile .level-right{display:flex}html.theme--documenter-dark .level.is-mobile .level-left+.level-right{margin-top:0}html.theme--documenter-dark .level.is-mobile .level-item:not(:last-child){margin-bottom:0;margin-right:.75rem}html.theme--documenter-dark .level.is-mobile .level-item:not(.is-narrow){flex-grow:1}@media screen and (min-width: 769px),print{html.theme--documenter-dark .level{display:flex}html.theme--documenter-dark .level>.level-item:not(.is-narrow){flex-grow:1}}html.theme--documenter-dark .level-item{align-items:center;display:flex;flex-basis:auto;flex-grow:0;flex-shrink:0;justify-content:center}html.theme--documenter-dark .level-item .title,html.theme--documenter-dark .level-item .subtitle{margin-bottom:0}@media screen and (max-width: 768px){html.theme--documenter-dark .level-item:not(:last-child){margin-bottom:.75rem}}html.theme--documenter-dark .level-left,html.theme--documenter-dark .level-right{flex-basis:auto;flex-grow:0;flex-shrink:0}html.theme--documenter-dark .level-left .level-item.is-flexible,html.theme--documenter-dark .level-right .level-item.is-flexible{flex-grow:1}@media screen and (min-width: 769px),print{html.theme--documenter-dark .level-left .level-item:not(:last-child),html.theme--documenter-dark .level-right .level-item:not(:last-child){margin-right:.75rem}}html.theme--documenter-dark .level-left{align-items:center;justify-content:flex-start}@media screen and (max-width: 768px){html.theme--documenter-dark .level-left+.level-right{margin-top:1.5rem}}@media screen and (min-width: 769px),print{html.theme--documenter-dark .level-left{display:flex}}html.theme--documenter-dark .level-right{align-items:center;justify-content:flex-end}@media screen and (min-width: 769px),print{html.theme--documenter-dark .level-right{display:flex}}html.theme--documenter-dark .media{align-items:flex-start;display:flex;text-align:inherit}html.theme--documenter-dark .media .content:not(:last-child){margin-bottom:.75rem}html.theme--documenter-dark .media .media{border-top:1px solid rgba(94,109,111,0.5);display:flex;padding-top:.75rem}html.theme--documenter-dark .media .media .content:not(:last-child),html.theme--documenter-dark .media .media .control:not(:last-child){margin-bottom:.5rem}html.theme--documenter-dark .media .media .media{padding-top:.5rem}html.theme--documenter-dark .media .media .media+.media{margin-top:.5rem}html.theme--documenter-dark .media+.media{border-top:1px solid rgba(94,109,111,0.5);margin-top:1rem;padding-top:1rem}html.theme--documenter-dark .media.is-large+.media{margin-top:1.5rem;padding-top:1.5rem}html.theme--documenter-dark .media-left,html.theme--documenter-dark .media-right{flex-basis:auto;flex-grow:0;flex-shrink:0}html.theme--documenter-dark .media-left{margin-right:1rem}html.theme--documenter-dark .media-right{margin-left:1rem}html.theme--documenter-dark .media-content{flex-basis:auto;flex-grow:1;flex-shrink:1;text-align:inherit}@media screen and (max-width: 768px){html.theme--documenter-dark .media-content{overflow-x:auto}}html.theme--documenter-dark .menu{font-size:1rem}html.theme--documenter-dark .menu.is-small,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.menu{font-size:.75rem}html.theme--documenter-dark .menu.is-medium{font-size:1.25rem}html.theme--documenter-dark .menu.is-large{font-size:1.5rem}html.theme--documenter-dark .menu-list{line-height:1.25}html.theme--documenter-dark .menu-list a{border-radius:3px;color:#fff;display:block;padding:0.5em 0.75em}html.theme--documenter-dark .menu-list a:hover{background-color:#282f2f;color:#f2f2f2}html.theme--documenter-dark .menu-list a.is-active{background-color:#1abc9c;color:#fff}html.theme--documenter-dark .menu-list li ul{border-left:1px solid #5e6d6f;margin:.75em;padding-left:.75em}html.theme--documenter-dark .menu-label{color:#fff;font-size:.75em;letter-spacing:.1em;text-transform:uppercase}html.theme--documenter-dark .menu-label:not(:first-child){margin-top:1em}html.theme--documenter-dark .menu-label:not(:last-child){margin-bottom:1em}html.theme--documenter-dark .message{background-color:#282f2f;border-radius:.4em;font-size:1rem}html.theme--documenter-dark .message strong{color:currentColor}html.theme--documenter-dark .message a:not(.button):not(.tag):not(.dropdown-item){color:currentColor;text-decoration:underline}html.theme--documenter-dark .message.is-small,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.message{font-size:.75rem}html.theme--documenter-dark .message.is-medium{font-size:1.25rem}html.theme--documenter-dark .message.is-large{font-size:1.5rem}html.theme--documenter-dark .message.is-white{background-color:#fff}html.theme--documenter-dark .message.is-white .message-header{background-color:#fff;color:#0a0a0a}html.theme--documenter-dark .message.is-white .message-body{border-color:#fff}html.theme--documenter-dark .message.is-black{background-color:#fafafa}html.theme--documenter-dark .message.is-black .message-header{background-color:#0a0a0a;color:#fff}html.theme--documenter-dark .message.is-black .message-body{border-color:#0a0a0a}html.theme--documenter-dark .message.is-light{background-color:#f9fafb}html.theme--documenter-dark .message.is-light .message-header{background-color:#ecf0f1;color:rgba(0,0,0,0.7)}html.theme--documenter-dark .message.is-light .message-body{border-color:#ecf0f1}html.theme--documenter-dark .message.is-dark,html.theme--documenter-dark .content kbd.message{background-color:#f9fafa}html.theme--documenter-dark .message.is-dark .message-header,html.theme--documenter-dark .content kbd.message .message-header{background-color:#282f2f;color:#fff}html.theme--documenter-dark .message.is-dark .message-body,html.theme--documenter-dark .content kbd.message .message-body{border-color:#282f2f}html.theme--documenter-dark .message.is-primary,html.theme--documenter-dark .docstring>section>a.message.docs-sourcelink{background-color:#f1f5f9}html.theme--documenter-dark .message.is-primary .message-header,html.theme--documenter-dark .docstring>section>a.message.docs-sourcelink .message-header{background-color:#375a7f;color:#fff}html.theme--documenter-dark .message.is-primary .message-body,html.theme--documenter-dark .docstring>section>a.message.docs-sourcelink .message-body{border-color:#375a7f;color:#4d7eb2}html.theme--documenter-dark .message.is-link{background-color:#edfdf9}html.theme--documenter-dark .message.is-link .message-header{background-color:#1abc9c;color:#fff}html.theme--documenter-dark .message.is-link .message-body{border-color:#1abc9c;color:#15987e}html.theme--documenter-dark .message.is-info{background-color:#ebf7ff}html.theme--documenter-dark .message.is-info .message-header{background-color:#024c7d;color:#fff}html.theme--documenter-dark .message.is-info .message-body{border-color:#024c7d;color:#0e9dfb}html.theme--documenter-dark .message.is-success{background-color:#ebfff3}html.theme--documenter-dark .message.is-success .message-header{background-color:#008438;color:#fff}html.theme--documenter-dark .message.is-success .message-body{border-color:#008438;color:#00eb64}html.theme--documenter-dark .message.is-warning{background-color:#fffaeb}html.theme--documenter-dark .message.is-warning .message-header{background-color:#ad8100;color:#fff}html.theme--documenter-dark .message.is-warning .message-body{border-color:#ad8100;color:#d19c00}html.theme--documenter-dark .message.is-danger{background-color:#fdeeec}html.theme--documenter-dark .message.is-danger .message-header{background-color:#9e1b0d;color:#fff}html.theme--documenter-dark .message.is-danger .message-body{border-color:#9e1b0d;color:#ec311d}html.theme--documenter-dark .message-header{align-items:center;background-color:#fff;border-radius:.4em .4em 0 0;color:rgba(0,0,0,0.7);display:flex;font-weight:700;justify-content:space-between;line-height:1.25;padding:0.75em 1em;position:relative}html.theme--documenter-dark .message-header .delete{flex-grow:0;flex-shrink:0;margin-left:.75em}html.theme--documenter-dark .message-header+.message-body{border-width:0;border-top-left-radius:0;border-top-right-radius:0}html.theme--documenter-dark .message-body{border-color:#5e6d6f;border-radius:.4em;border-style:solid;border-width:0 0 0 4px;color:#fff;padding:1.25em 1.5em}html.theme--documenter-dark .message-body code,html.theme--documenter-dark .message-body pre{background-color:#fff}html.theme--documenter-dark .message-body pre code{background-color:rgba(0,0,0,0)}html.theme--documenter-dark .modal{align-items:center;display:none;flex-direction:column;justify-content:center;overflow:hidden;position:fixed;z-index:40}html.theme--documenter-dark .modal.is-active{display:flex}html.theme--documenter-dark .modal-background{background-color:rgba(10,10,10,0.86)}html.theme--documenter-dark .modal-content,html.theme--documenter-dark .modal-card{margin:0 20px;max-height:calc(100vh - 160px);overflow:auto;position:relative;width:100%}@media screen and (min-width: 769px){html.theme--documenter-dark .modal-content,html.theme--documenter-dark .modal-card{margin:0 auto;max-height:calc(100vh - 40px);width:640px}}html.theme--documenter-dark .modal-close{background:none;height:40px;position:fixed;right:20px;top:20px;width:40px}html.theme--documenter-dark .modal-card{display:flex;flex-direction:column;max-height:calc(100vh - 40px);overflow:hidden;-ms-overflow-y:visible}html.theme--documenter-dark .modal-card-head,html.theme--documenter-dark .modal-card-foot{align-items:center;background-color:#282f2f;display:flex;flex-shrink:0;justify-content:flex-start;padding:20px;position:relative}html.theme--documenter-dark .modal-card-head{border-bottom:1px solid #5e6d6f;border-top-left-radius:8px;border-top-right-radius:8px}html.theme--documenter-dark .modal-card-title{color:#f2f2f2;flex-grow:1;flex-shrink:0;font-size:1.5rem;line-height:1}html.theme--documenter-dark .modal-card-foot{border-bottom-left-radius:8px;border-bottom-right-radius:8px;border-top:1px solid #5e6d6f}html.theme--documenter-dark .modal-card-foot .button:not(:last-child){margin-right:.5em}html.theme--documenter-dark .modal-card-body{-webkit-overflow-scrolling:touch;background-color:#fff;flex-grow:1;flex-shrink:1;overflow:auto;padding:20px}html.theme--documenter-dark .navbar{background-color:#375a7f;min-height:4rem;position:relative;z-index:30}html.theme--documenter-dark .navbar.is-white{background-color:#fff;color:#0a0a0a}html.theme--documenter-dark .navbar.is-white .navbar-brand>.navbar-item,html.theme--documenter-dark .navbar.is-white .navbar-brand .navbar-link{color:#0a0a0a}html.theme--documenter-dark .navbar.is-white .navbar-brand>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-white .navbar-brand>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-white .navbar-brand>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-white .navbar-brand .navbar-link:focus,html.theme--documenter-dark .navbar.is-white .navbar-brand .navbar-link:hover,html.theme--documenter-dark .navbar.is-white .navbar-brand .navbar-link.is-active{background-color:#f2f2f2;color:#0a0a0a}html.theme--documenter-dark .navbar.is-white .navbar-brand .navbar-link::after{border-color:#0a0a0a}html.theme--documenter-dark .navbar.is-white .navbar-burger{color:#0a0a0a}@media screen and (min-width: 1056px){html.theme--documenter-dark .navbar.is-white .navbar-start>.navbar-item,html.theme--documenter-dark .navbar.is-white .navbar-start .navbar-link,html.theme--documenter-dark .navbar.is-white .navbar-end>.navbar-item,html.theme--documenter-dark .navbar.is-white .navbar-end .navbar-link{color:#0a0a0a}html.theme--documenter-dark .navbar.is-white .navbar-start>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-white .navbar-start>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-white .navbar-start>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-white .navbar-start .navbar-link:focus,html.theme--documenter-dark .navbar.is-white .navbar-start .navbar-link:hover,html.theme--documenter-dark .navbar.is-white .navbar-start .navbar-link.is-active,html.theme--documenter-dark .navbar.is-white .navbar-end>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-white .navbar-end>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-white .navbar-end>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-white .navbar-end .navbar-link:focus,html.theme--documenter-dark .navbar.is-white .navbar-end .navbar-link:hover,html.theme--documenter-dark .navbar.is-white .navbar-end .navbar-link.is-active{background-color:#f2f2f2;color:#0a0a0a}html.theme--documenter-dark .navbar.is-white .navbar-start .navbar-link::after,html.theme--documenter-dark .navbar.is-white .navbar-end .navbar-link::after{border-color:#0a0a0a}html.theme--documenter-dark .navbar.is-white .navbar-item.has-dropdown:focus .navbar-link,html.theme--documenter-dark .navbar.is-white .navbar-item.has-dropdown:hover .navbar-link,html.theme--documenter-dark .navbar.is-white .navbar-item.has-dropdown.is-active .navbar-link{background-color:#f2f2f2;color:#0a0a0a}html.theme--documenter-dark .navbar.is-white .navbar-dropdown a.navbar-item.is-active{background-color:#fff;color:#0a0a0a}}html.theme--documenter-dark .navbar.is-black{background-color:#0a0a0a;color:#fff}html.theme--documenter-dark .navbar.is-black .navbar-brand>.navbar-item,html.theme--documenter-dark .navbar.is-black .navbar-brand .navbar-link{color:#fff}html.theme--documenter-dark .navbar.is-black .navbar-brand>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-black .navbar-brand>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-black .navbar-brand>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-black .navbar-brand .navbar-link:focus,html.theme--documenter-dark .navbar.is-black .navbar-brand .navbar-link:hover,html.theme--documenter-dark .navbar.is-black .navbar-brand .navbar-link.is-active{background-color:#000;color:#fff}html.theme--documenter-dark .navbar.is-black .navbar-brand .navbar-link::after{border-color:#fff}html.theme--documenter-dark .navbar.is-black .navbar-burger{color:#fff}@media screen and (min-width: 1056px){html.theme--documenter-dark .navbar.is-black .navbar-start>.navbar-item,html.theme--documenter-dark .navbar.is-black .navbar-start .navbar-link,html.theme--documenter-dark .navbar.is-black .navbar-end>.navbar-item,html.theme--documenter-dark .navbar.is-black .navbar-end .navbar-link{color:#fff}html.theme--documenter-dark .navbar.is-black .navbar-start>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-black .navbar-start>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-black .navbar-start>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-black .navbar-start .navbar-link:focus,html.theme--documenter-dark .navbar.is-black .navbar-start .navbar-link:hover,html.theme--documenter-dark .navbar.is-black .navbar-start .navbar-link.is-active,html.theme--documenter-dark .navbar.is-black .navbar-end>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-black .navbar-end>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-black .navbar-end>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-black .navbar-end .navbar-link:focus,html.theme--documenter-dark .navbar.is-black .navbar-end .navbar-link:hover,html.theme--documenter-dark .navbar.is-black .navbar-end .navbar-link.is-active{background-color:#000;color:#fff}html.theme--documenter-dark .navbar.is-black .navbar-start .navbar-link::after,html.theme--documenter-dark .navbar.is-black .navbar-end .navbar-link::after{border-color:#fff}html.theme--documenter-dark .navbar.is-black .navbar-item.has-dropdown:focus .navbar-link,html.theme--documenter-dark .navbar.is-black .navbar-item.has-dropdown:hover .navbar-link,html.theme--documenter-dark .navbar.is-black .navbar-item.has-dropdown.is-active .navbar-link{background-color:#000;color:#fff}html.theme--documenter-dark .navbar.is-black .navbar-dropdown a.navbar-item.is-active{background-color:#0a0a0a;color:#fff}}html.theme--documenter-dark .navbar.is-light{background-color:#ecf0f1;color:rgba(0,0,0,0.7)}html.theme--documenter-dark .navbar.is-light .navbar-brand>.navbar-item,html.theme--documenter-dark .navbar.is-light .navbar-brand .navbar-link{color:rgba(0,0,0,0.7)}html.theme--documenter-dark .navbar.is-light .navbar-brand>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-light .navbar-brand>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-light .navbar-brand>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-light .navbar-brand .navbar-link:focus,html.theme--documenter-dark .navbar.is-light .navbar-brand .navbar-link:hover,html.theme--documenter-dark .navbar.is-light .navbar-brand .navbar-link.is-active{background-color:#dde4e6;color:rgba(0,0,0,0.7)}html.theme--documenter-dark .navbar.is-light .navbar-brand .navbar-link::after{border-color:rgba(0,0,0,0.7)}html.theme--documenter-dark .navbar.is-light .navbar-burger{color:rgba(0,0,0,0.7)}@media screen and (min-width: 1056px){html.theme--documenter-dark .navbar.is-light .navbar-start>.navbar-item,html.theme--documenter-dark .navbar.is-light .navbar-start .navbar-link,html.theme--documenter-dark .navbar.is-light .navbar-end>.navbar-item,html.theme--documenter-dark .navbar.is-light .navbar-end .navbar-link{color:rgba(0,0,0,0.7)}html.theme--documenter-dark .navbar.is-light .navbar-start>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-light .navbar-start>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-light .navbar-start>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-light .navbar-start .navbar-link:focus,html.theme--documenter-dark .navbar.is-light .navbar-start .navbar-link:hover,html.theme--documenter-dark .navbar.is-light .navbar-start .navbar-link.is-active,html.theme--documenter-dark .navbar.is-light .navbar-end>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-light .navbar-end>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-light .navbar-end>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-light .navbar-end .navbar-link:focus,html.theme--documenter-dark .navbar.is-light .navbar-end .navbar-link:hover,html.theme--documenter-dark .navbar.is-light .navbar-end .navbar-link.is-active{background-color:#dde4e6;color:rgba(0,0,0,0.7)}html.theme--documenter-dark .navbar.is-light .navbar-start .navbar-link::after,html.theme--documenter-dark .navbar.is-light .navbar-end .navbar-link::after{border-color:rgba(0,0,0,0.7)}html.theme--documenter-dark .navbar.is-light .navbar-item.has-dropdown:focus .navbar-link,html.theme--documenter-dark .navbar.is-light .navbar-item.has-dropdown:hover .navbar-link,html.theme--documenter-dark .navbar.is-light .navbar-item.has-dropdown.is-active .navbar-link{background-color:#dde4e6;color:rgba(0,0,0,0.7)}html.theme--documenter-dark .navbar.is-light .navbar-dropdown a.navbar-item.is-active{background-color:#ecf0f1;color:rgba(0,0,0,0.7)}}html.theme--documenter-dark .navbar.is-dark,html.theme--documenter-dark .content kbd.navbar{background-color:#282f2f;color:#fff}html.theme--documenter-dark .navbar.is-dark .navbar-brand>.navbar-item,html.theme--documenter-dark .content kbd.navbar .navbar-brand>.navbar-item,html.theme--documenter-dark .navbar.is-dark .navbar-brand .navbar-link,html.theme--documenter-dark .content kbd.navbar .navbar-brand .navbar-link{color:#fff}html.theme--documenter-dark .navbar.is-dark .navbar-brand>a.navbar-item:focus,html.theme--documenter-dark .content kbd.navbar .navbar-brand>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-dark .navbar-brand>a.navbar-item:hover,html.theme--documenter-dark .content kbd.navbar .navbar-brand>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-dark .navbar-brand>a.navbar-item.is-active,html.theme--documenter-dark .content kbd.navbar .navbar-brand>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-dark .navbar-brand .navbar-link:focus,html.theme--documenter-dark .content kbd.navbar .navbar-brand .navbar-link:focus,html.theme--documenter-dark .navbar.is-dark .navbar-brand .navbar-link:hover,html.theme--documenter-dark .content kbd.navbar .navbar-brand .navbar-link:hover,html.theme--documenter-dark .navbar.is-dark .navbar-brand .navbar-link.is-active,html.theme--documenter-dark .content kbd.navbar .navbar-brand .navbar-link.is-active{background-color:#1d2122;color:#fff}html.theme--documenter-dark .navbar.is-dark .navbar-brand .navbar-link::after,html.theme--documenter-dark .content kbd.navbar .navbar-brand .navbar-link::after{border-color:#fff}html.theme--documenter-dark .navbar.is-dark .navbar-burger,html.theme--documenter-dark .content kbd.navbar .navbar-burger{color:#fff}@media screen and (min-width: 1056px){html.theme--documenter-dark .navbar.is-dark .navbar-start>.navbar-item,html.theme--documenter-dark .content kbd.navbar .navbar-start>.navbar-item,html.theme--documenter-dark .navbar.is-dark .navbar-start .navbar-link,html.theme--documenter-dark .content kbd.navbar .navbar-start .navbar-link,html.theme--documenter-dark .navbar.is-dark .navbar-end>.navbar-item,html.theme--documenter-dark .content kbd.navbar .navbar-end>.navbar-item,html.theme--documenter-dark .navbar.is-dark .navbar-end .navbar-link,html.theme--documenter-dark .content kbd.navbar .navbar-end .navbar-link{color:#fff}html.theme--documenter-dark .navbar.is-dark .navbar-start>a.navbar-item:focus,html.theme--documenter-dark .content kbd.navbar .navbar-start>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-dark .navbar-start>a.navbar-item:hover,html.theme--documenter-dark .content kbd.navbar .navbar-start>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-dark .navbar-start>a.navbar-item.is-active,html.theme--documenter-dark .content kbd.navbar .navbar-start>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-dark .navbar-start .navbar-link:focus,html.theme--documenter-dark .content kbd.navbar .navbar-start .navbar-link:focus,html.theme--documenter-dark .navbar.is-dark .navbar-start .navbar-link:hover,html.theme--documenter-dark .content kbd.navbar .navbar-start .navbar-link:hover,html.theme--documenter-dark .navbar.is-dark .navbar-start .navbar-link.is-active,html.theme--documenter-dark .content kbd.navbar .navbar-start .navbar-link.is-active,html.theme--documenter-dark .navbar.is-dark .navbar-end>a.navbar-item:focus,html.theme--documenter-dark .content kbd.navbar .navbar-end>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-dark .navbar-end>a.navbar-item:hover,html.theme--documenter-dark .content kbd.navbar .navbar-end>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-dark .navbar-end>a.navbar-item.is-active,html.theme--documenter-dark .content kbd.navbar .navbar-end>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-dark .navbar-end .navbar-link:focus,html.theme--documenter-dark .content kbd.navbar .navbar-end .navbar-link:focus,html.theme--documenter-dark .navbar.is-dark .navbar-end .navbar-link:hover,html.theme--documenter-dark .content kbd.navbar .navbar-end .navbar-link:hover,html.theme--documenter-dark .navbar.is-dark .navbar-end .navbar-link.is-active,html.theme--documenter-dark .content kbd.navbar .navbar-end .navbar-link.is-active{background-color:#1d2122;color:#fff}html.theme--documenter-dark .navbar.is-dark .navbar-start .navbar-link::after,html.theme--documenter-dark .content kbd.navbar .navbar-start .navbar-link::after,html.theme--documenter-dark .navbar.is-dark .navbar-end .navbar-link::after,html.theme--documenter-dark .content kbd.navbar .navbar-end .navbar-link::after{border-color:#fff}html.theme--documenter-dark .navbar.is-dark .navbar-item.has-dropdown:focus .navbar-link,html.theme--documenter-dark .content kbd.navbar .navbar-item.has-dropdown:focus .navbar-link,html.theme--documenter-dark .navbar.is-dark .navbar-item.has-dropdown:hover .navbar-link,html.theme--documenter-dark .content kbd.navbar .navbar-item.has-dropdown:hover .navbar-link,html.theme--documenter-dark .navbar.is-dark .navbar-item.has-dropdown.is-active .navbar-link,html.theme--documenter-dark .content kbd.navbar .navbar-item.has-dropdown.is-active .navbar-link{background-color:#1d2122;color:#fff}html.theme--documenter-dark .navbar.is-dark .navbar-dropdown a.navbar-item.is-active,html.theme--documenter-dark .content kbd.navbar .navbar-dropdown a.navbar-item.is-active{background-color:#282f2f;color:#fff}}html.theme--documenter-dark .navbar.is-primary,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink{background-color:#375a7f;color:#fff}html.theme--documenter-dark .navbar.is-primary .navbar-brand>.navbar-item,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-brand>.navbar-item,html.theme--documenter-dark .navbar.is-primary .navbar-brand .navbar-link,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-brand .navbar-link{color:#fff}html.theme--documenter-dark .navbar.is-primary .navbar-brand>a.navbar-item:focus,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-brand>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-primary .navbar-brand>a.navbar-item:hover,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-brand>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-primary .navbar-brand>a.navbar-item.is-active,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-brand>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-primary .navbar-brand .navbar-link:focus,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-brand .navbar-link:focus,html.theme--documenter-dark .navbar.is-primary .navbar-brand .navbar-link:hover,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-brand .navbar-link:hover,html.theme--documenter-dark .navbar.is-primary .navbar-brand .navbar-link.is-active,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-brand .navbar-link.is-active{background-color:#2f4d6d;color:#fff}html.theme--documenter-dark .navbar.is-primary .navbar-brand .navbar-link::after,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-brand .navbar-link::after{border-color:#fff}html.theme--documenter-dark .navbar.is-primary .navbar-burger,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-burger{color:#fff}@media screen and (min-width: 1056px){html.theme--documenter-dark .navbar.is-primary .navbar-start>.navbar-item,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-start>.navbar-item,html.theme--documenter-dark .navbar.is-primary .navbar-start .navbar-link,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-start .navbar-link,html.theme--documenter-dark .navbar.is-primary .navbar-end>.navbar-item,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-end>.navbar-item,html.theme--documenter-dark .navbar.is-primary .navbar-end .navbar-link,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-end .navbar-link{color:#fff}html.theme--documenter-dark .navbar.is-primary .navbar-start>a.navbar-item:focus,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-start>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-primary .navbar-start>a.navbar-item:hover,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-start>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-primary .navbar-start>a.navbar-item.is-active,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-start>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-primary .navbar-start .navbar-link:focus,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-start .navbar-link:focus,html.theme--documenter-dark .navbar.is-primary .navbar-start .navbar-link:hover,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-start .navbar-link:hover,html.theme--documenter-dark .navbar.is-primary .navbar-start .navbar-link.is-active,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-start .navbar-link.is-active,html.theme--documenter-dark .navbar.is-primary .navbar-end>a.navbar-item:focus,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-end>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-primary .navbar-end>a.navbar-item:hover,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-end>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-primary .navbar-end>a.navbar-item.is-active,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-end>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-primary .navbar-end .navbar-link:focus,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-end .navbar-link:focus,html.theme--documenter-dark .navbar.is-primary .navbar-end .navbar-link:hover,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-end .navbar-link:hover,html.theme--documenter-dark .navbar.is-primary .navbar-end .navbar-link.is-active,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-end .navbar-link.is-active{background-color:#2f4d6d;color:#fff}html.theme--documenter-dark .navbar.is-primary .navbar-start .navbar-link::after,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-start .navbar-link::after,html.theme--documenter-dark .navbar.is-primary .navbar-end .navbar-link::after,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-end .navbar-link::after{border-color:#fff}html.theme--documenter-dark .navbar.is-primary .navbar-item.has-dropdown:focus .navbar-link,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-item.has-dropdown:focus .navbar-link,html.theme--documenter-dark .navbar.is-primary .navbar-item.has-dropdown:hover .navbar-link,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-item.has-dropdown:hover .navbar-link,html.theme--documenter-dark .navbar.is-primary .navbar-item.has-dropdown.is-active .navbar-link,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-item.has-dropdown.is-active .navbar-link{background-color:#2f4d6d;color:#fff}html.theme--documenter-dark .navbar.is-primary .navbar-dropdown a.navbar-item.is-active,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-dropdown a.navbar-item.is-active{background-color:#375a7f;color:#fff}}html.theme--documenter-dark .navbar.is-link{background-color:#1abc9c;color:#fff}html.theme--documenter-dark .navbar.is-link .navbar-brand>.navbar-item,html.theme--documenter-dark .navbar.is-link .navbar-brand .navbar-link{color:#fff}html.theme--documenter-dark .navbar.is-link .navbar-brand>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-link .navbar-brand>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-link .navbar-brand>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-link .navbar-brand .navbar-link:focus,html.theme--documenter-dark .navbar.is-link .navbar-brand .navbar-link:hover,html.theme--documenter-dark .navbar.is-link .navbar-brand .navbar-link.is-active{background-color:#17a689;color:#fff}html.theme--documenter-dark .navbar.is-link .navbar-brand .navbar-link::after{border-color:#fff}html.theme--documenter-dark .navbar.is-link .navbar-burger{color:#fff}@media screen and (min-width: 1056px){html.theme--documenter-dark .navbar.is-link .navbar-start>.navbar-item,html.theme--documenter-dark .navbar.is-link .navbar-start .navbar-link,html.theme--documenter-dark .navbar.is-link .navbar-end>.navbar-item,html.theme--documenter-dark .navbar.is-link .navbar-end .navbar-link{color:#fff}html.theme--documenter-dark .navbar.is-link .navbar-start>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-link .navbar-start>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-link .navbar-start>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-link .navbar-start .navbar-link:focus,html.theme--documenter-dark .navbar.is-link .navbar-start .navbar-link:hover,html.theme--documenter-dark .navbar.is-link .navbar-start .navbar-link.is-active,html.theme--documenter-dark .navbar.is-link .navbar-end>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-link .navbar-end>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-link .navbar-end>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-link .navbar-end .navbar-link:focus,html.theme--documenter-dark .navbar.is-link .navbar-end .navbar-link:hover,html.theme--documenter-dark .navbar.is-link .navbar-end .navbar-link.is-active{background-color:#17a689;color:#fff}html.theme--documenter-dark .navbar.is-link .navbar-start .navbar-link::after,html.theme--documenter-dark .navbar.is-link .navbar-end .navbar-link::after{border-color:#fff}html.theme--documenter-dark .navbar.is-link .navbar-item.has-dropdown:focus .navbar-link,html.theme--documenter-dark .navbar.is-link .navbar-item.has-dropdown:hover .navbar-link,html.theme--documenter-dark .navbar.is-link .navbar-item.has-dropdown.is-active .navbar-link{background-color:#17a689;color:#fff}html.theme--documenter-dark .navbar.is-link .navbar-dropdown a.navbar-item.is-active{background-color:#1abc9c;color:#fff}}html.theme--documenter-dark .navbar.is-info{background-color:#024c7d;color:#fff}html.theme--documenter-dark .navbar.is-info .navbar-brand>.navbar-item,html.theme--documenter-dark .navbar.is-info .navbar-brand .navbar-link{color:#fff}html.theme--documenter-dark .navbar.is-info .navbar-brand>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-info .navbar-brand>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-info .navbar-brand>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-info .navbar-brand .navbar-link:focus,html.theme--documenter-dark .navbar.is-info .navbar-brand .navbar-link:hover,html.theme--documenter-dark .navbar.is-info .navbar-brand .navbar-link.is-active{background-color:#023d64;color:#fff}html.theme--documenter-dark .navbar.is-info .navbar-brand .navbar-link::after{border-color:#fff}html.theme--documenter-dark .navbar.is-info .navbar-burger{color:#fff}@media screen and (min-width: 1056px){html.theme--documenter-dark .navbar.is-info .navbar-start>.navbar-item,html.theme--documenter-dark .navbar.is-info .navbar-start .navbar-link,html.theme--documenter-dark .navbar.is-info .navbar-end>.navbar-item,html.theme--documenter-dark .navbar.is-info .navbar-end .navbar-link{color:#fff}html.theme--documenter-dark .navbar.is-info .navbar-start>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-info .navbar-start>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-info .navbar-start>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-info .navbar-start .navbar-link:focus,html.theme--documenter-dark .navbar.is-info .navbar-start .navbar-link:hover,html.theme--documenter-dark .navbar.is-info .navbar-start .navbar-link.is-active,html.theme--documenter-dark .navbar.is-info .navbar-end>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-info .navbar-end>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-info .navbar-end>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-info .navbar-end .navbar-link:focus,html.theme--documenter-dark .navbar.is-info .navbar-end .navbar-link:hover,html.theme--documenter-dark .navbar.is-info .navbar-end .navbar-link.is-active{background-color:#023d64;color:#fff}html.theme--documenter-dark .navbar.is-info .navbar-start .navbar-link::after,html.theme--documenter-dark .navbar.is-info .navbar-end .navbar-link::after{border-color:#fff}html.theme--documenter-dark .navbar.is-info .navbar-item.has-dropdown:focus .navbar-link,html.theme--documenter-dark .navbar.is-info .navbar-item.has-dropdown:hover .navbar-link,html.theme--documenter-dark .navbar.is-info .navbar-item.has-dropdown.is-active .navbar-link{background-color:#023d64;color:#fff}html.theme--documenter-dark .navbar.is-info .navbar-dropdown a.navbar-item.is-active{background-color:#024c7d;color:#fff}}html.theme--documenter-dark .navbar.is-success{background-color:#008438;color:#fff}html.theme--documenter-dark .navbar.is-success .navbar-brand>.navbar-item,html.theme--documenter-dark .navbar.is-success .navbar-brand .navbar-link{color:#fff}html.theme--documenter-dark .navbar.is-success .navbar-brand>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-success .navbar-brand>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-success .navbar-brand>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-success .navbar-brand .navbar-link:focus,html.theme--documenter-dark .navbar.is-success .navbar-brand .navbar-link:hover,html.theme--documenter-dark .navbar.is-success .navbar-brand .navbar-link.is-active{background-color:#006b2d;color:#fff}html.theme--documenter-dark .navbar.is-success .navbar-brand .navbar-link::after{border-color:#fff}html.theme--documenter-dark .navbar.is-success .navbar-burger{color:#fff}@media screen and (min-width: 1056px){html.theme--documenter-dark .navbar.is-success .navbar-start>.navbar-item,html.theme--documenter-dark .navbar.is-success .navbar-start .navbar-link,html.theme--documenter-dark .navbar.is-success .navbar-end>.navbar-item,html.theme--documenter-dark .navbar.is-success .navbar-end .navbar-link{color:#fff}html.theme--documenter-dark .navbar.is-success .navbar-start>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-success .navbar-start>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-success .navbar-start>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-success .navbar-start .navbar-link:focus,html.theme--documenter-dark .navbar.is-success .navbar-start .navbar-link:hover,html.theme--documenter-dark .navbar.is-success .navbar-start .navbar-link.is-active,html.theme--documenter-dark .navbar.is-success .navbar-end>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-success .navbar-end>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-success .navbar-end>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-success .navbar-end .navbar-link:focus,html.theme--documenter-dark .navbar.is-success .navbar-end .navbar-link:hover,html.theme--documenter-dark .navbar.is-success .navbar-end .navbar-link.is-active{background-color:#006b2d;color:#fff}html.theme--documenter-dark .navbar.is-success .navbar-start .navbar-link::after,html.theme--documenter-dark .navbar.is-success .navbar-end .navbar-link::after{border-color:#fff}html.theme--documenter-dark .navbar.is-success .navbar-item.has-dropdown:focus .navbar-link,html.theme--documenter-dark .navbar.is-success .navbar-item.has-dropdown:hover .navbar-link,html.theme--documenter-dark .navbar.is-success .navbar-item.has-dropdown.is-active .navbar-link{background-color:#006b2d;color:#fff}html.theme--documenter-dark .navbar.is-success .navbar-dropdown a.navbar-item.is-active{background-color:#008438;color:#fff}}html.theme--documenter-dark .navbar.is-warning{background-color:#ad8100;color:#fff}html.theme--documenter-dark .navbar.is-warning .navbar-brand>.navbar-item,html.theme--documenter-dark .navbar.is-warning .navbar-brand .navbar-link{color:#fff}html.theme--documenter-dark .navbar.is-warning .navbar-brand>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-warning .navbar-brand>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-warning .navbar-brand>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-warning .navbar-brand .navbar-link:focus,html.theme--documenter-dark .navbar.is-warning .navbar-brand .navbar-link:hover,html.theme--documenter-dark .navbar.is-warning .navbar-brand .navbar-link.is-active{background-color:#946e00;color:#fff}html.theme--documenter-dark .navbar.is-warning .navbar-brand .navbar-link::after{border-color:#fff}html.theme--documenter-dark .navbar.is-warning .navbar-burger{color:#fff}@media screen and (min-width: 1056px){html.theme--documenter-dark .navbar.is-warning .navbar-start>.navbar-item,html.theme--documenter-dark .navbar.is-warning .navbar-start .navbar-link,html.theme--documenter-dark .navbar.is-warning .navbar-end>.navbar-item,html.theme--documenter-dark .navbar.is-warning .navbar-end .navbar-link{color:#fff}html.theme--documenter-dark .navbar.is-warning .navbar-start>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-warning .navbar-start>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-warning .navbar-start>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-warning .navbar-start .navbar-link:focus,html.theme--documenter-dark .navbar.is-warning .navbar-start .navbar-link:hover,html.theme--documenter-dark .navbar.is-warning .navbar-start .navbar-link.is-active,html.theme--documenter-dark .navbar.is-warning .navbar-end>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-warning .navbar-end>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-warning .navbar-end>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-warning .navbar-end .navbar-link:focus,html.theme--documenter-dark .navbar.is-warning .navbar-end .navbar-link:hover,html.theme--documenter-dark .navbar.is-warning .navbar-end .navbar-link.is-active{background-color:#946e00;color:#fff}html.theme--documenter-dark .navbar.is-warning .navbar-start .navbar-link::after,html.theme--documenter-dark .navbar.is-warning .navbar-end .navbar-link::after{border-color:#fff}html.theme--documenter-dark .navbar.is-warning .navbar-item.has-dropdown:focus .navbar-link,html.theme--documenter-dark .navbar.is-warning .navbar-item.has-dropdown:hover .navbar-link,html.theme--documenter-dark .navbar.is-warning .navbar-item.has-dropdown.is-active .navbar-link{background-color:#946e00;color:#fff}html.theme--documenter-dark .navbar.is-warning .navbar-dropdown a.navbar-item.is-active{background-color:#ad8100;color:#fff}}html.theme--documenter-dark .navbar.is-danger{background-color:#9e1b0d;color:#fff}html.theme--documenter-dark .navbar.is-danger .navbar-brand>.navbar-item,html.theme--documenter-dark .navbar.is-danger .navbar-brand .navbar-link{color:#fff}html.theme--documenter-dark .navbar.is-danger .navbar-brand>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-danger .navbar-brand>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-danger .navbar-brand>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-danger .navbar-brand .navbar-link:focus,html.theme--documenter-dark .navbar.is-danger .navbar-brand .navbar-link:hover,html.theme--documenter-dark .navbar.is-danger .navbar-brand .navbar-link.is-active{background-color:#86170b;color:#fff}html.theme--documenter-dark .navbar.is-danger .navbar-brand .navbar-link::after{border-color:#fff}html.theme--documenter-dark .navbar.is-danger .navbar-burger{color:#fff}@media screen and (min-width: 1056px){html.theme--documenter-dark .navbar.is-danger .navbar-start>.navbar-item,html.theme--documenter-dark .navbar.is-danger .navbar-start .navbar-link,html.theme--documenter-dark .navbar.is-danger .navbar-end>.navbar-item,html.theme--documenter-dark .navbar.is-danger .navbar-end .navbar-link{color:#fff}html.theme--documenter-dark .navbar.is-danger .navbar-start>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-danger .navbar-start>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-danger .navbar-start>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-danger .navbar-start .navbar-link:focus,html.theme--documenter-dark .navbar.is-danger .navbar-start .navbar-link:hover,html.theme--documenter-dark .navbar.is-danger .navbar-start .navbar-link.is-active,html.theme--documenter-dark .navbar.is-danger .navbar-end>a.navbar-item:focus,html.theme--documenter-dark .navbar.is-danger .navbar-end>a.navbar-item:hover,html.theme--documenter-dark .navbar.is-danger .navbar-end>a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-danger .navbar-end .navbar-link:focus,html.theme--documenter-dark .navbar.is-danger .navbar-end .navbar-link:hover,html.theme--documenter-dark .navbar.is-danger .navbar-end .navbar-link.is-active{background-color:#86170b;color:#fff}html.theme--documenter-dark .navbar.is-danger .navbar-start .navbar-link::after,html.theme--documenter-dark .navbar.is-danger .navbar-end .navbar-link::after{border-color:#fff}html.theme--documenter-dark .navbar.is-danger .navbar-item.has-dropdown:focus .navbar-link,html.theme--documenter-dark .navbar.is-danger .navbar-item.has-dropdown:hover .navbar-link,html.theme--documenter-dark .navbar.is-danger .navbar-item.has-dropdown.is-active .navbar-link{background-color:#86170b;color:#fff}html.theme--documenter-dark .navbar.is-danger .navbar-dropdown a.navbar-item.is-active{background-color:#9e1b0d;color:#fff}}html.theme--documenter-dark .navbar>.container{align-items:stretch;display:flex;min-height:4rem;width:100%}html.theme--documenter-dark .navbar.has-shadow{box-shadow:0 2px 0 0 #282f2f}html.theme--documenter-dark .navbar.is-fixed-bottom,html.theme--documenter-dark .navbar.is-fixed-top{left:0;position:fixed;right:0;z-index:30}html.theme--documenter-dark .navbar.is-fixed-bottom{bottom:0}html.theme--documenter-dark .navbar.is-fixed-bottom.has-shadow{box-shadow:0 -2px 0 0 #282f2f}html.theme--documenter-dark .navbar.is-fixed-top{top:0}html.theme--documenter-dark html.has-navbar-fixed-top,html.theme--documenter-dark body.has-navbar-fixed-top{padding-top:4rem}html.theme--documenter-dark html.has-navbar-fixed-bottom,html.theme--documenter-dark body.has-navbar-fixed-bottom{padding-bottom:4rem}html.theme--documenter-dark .navbar-brand,html.theme--documenter-dark .navbar-tabs{align-items:stretch;display:flex;flex-shrink:0;min-height:4rem}html.theme--documenter-dark .navbar-brand a.navbar-item:focus,html.theme--documenter-dark .navbar-brand a.navbar-item:hover{background-color:transparent}html.theme--documenter-dark .navbar-tabs{-webkit-overflow-scrolling:touch;max-width:100vw;overflow-x:auto;overflow-y:hidden}html.theme--documenter-dark .navbar-burger{color:#fff;-moz-appearance:none;-webkit-appearance:none;appearance:none;background:none;border:none;cursor:pointer;display:block;height:4rem;position:relative;width:4rem;margin-left:auto}html.theme--documenter-dark .navbar-burger span{background-color:currentColor;display:block;height:1px;left:calc(50% - 8px);position:absolute;transform-origin:center;transition-duration:86ms;transition-property:background-color, opacity, transform;transition-timing-function:ease-out;width:16px}html.theme--documenter-dark .navbar-burger span:nth-child(1){top:calc(50% - 6px)}html.theme--documenter-dark .navbar-burger span:nth-child(2){top:calc(50% - 1px)}html.theme--documenter-dark .navbar-burger span:nth-child(3){top:calc(50% + 4px)}html.theme--documenter-dark .navbar-burger:hover{background-color:rgba(0,0,0,0.05)}html.theme--documenter-dark .navbar-burger.is-active span:nth-child(1){transform:translateY(5px) rotate(45deg)}html.theme--documenter-dark .navbar-burger.is-active span:nth-child(2){opacity:0}html.theme--documenter-dark .navbar-burger.is-active span:nth-child(3){transform:translateY(-5px) rotate(-45deg)}html.theme--documenter-dark .navbar-menu{display:none}html.theme--documenter-dark .navbar-item,html.theme--documenter-dark .navbar-link{color:#fff;display:block;line-height:1.5;padding:0.5rem 0.75rem;position:relative}html.theme--documenter-dark .navbar-item .icon:only-child,html.theme--documenter-dark .navbar-link .icon:only-child{margin-left:-0.25rem;margin-right:-0.25rem}html.theme--documenter-dark a.navbar-item,html.theme--documenter-dark .navbar-link{cursor:pointer}html.theme--documenter-dark a.navbar-item:focus,html.theme--documenter-dark a.navbar-item:focus-within,html.theme--documenter-dark a.navbar-item:hover,html.theme--documenter-dark a.navbar-item.is-active,html.theme--documenter-dark .navbar-link:focus,html.theme--documenter-dark .navbar-link:focus-within,html.theme--documenter-dark .navbar-link:hover,html.theme--documenter-dark .navbar-link.is-active{background-color:rgba(0,0,0,0);color:#1abc9c}html.theme--documenter-dark .navbar-item{flex-grow:0;flex-shrink:0}html.theme--documenter-dark .navbar-item img{max-height:1.75rem}html.theme--documenter-dark .navbar-item.has-dropdown{padding:0}html.theme--documenter-dark .navbar-item.is-expanded{flex-grow:1;flex-shrink:1}html.theme--documenter-dark .navbar-item.is-tab{border-bottom:1px solid transparent;min-height:4rem;padding-bottom:calc(0.5rem - 1px)}html.theme--documenter-dark .navbar-item.is-tab:focus,html.theme--documenter-dark .navbar-item.is-tab:hover{background-color:rgba(0,0,0,0);border-bottom-color:#1abc9c}html.theme--documenter-dark .navbar-item.is-tab.is-active{background-color:rgba(0,0,0,0);border-bottom-color:#1abc9c;border-bottom-style:solid;border-bottom-width:3px;color:#1abc9c;padding-bottom:calc(0.5rem - 3px)}html.theme--documenter-dark .navbar-content{flex-grow:1;flex-shrink:1}html.theme--documenter-dark .navbar-link:not(.is-arrowless){padding-right:2.5em}html.theme--documenter-dark .navbar-link:not(.is-arrowless)::after{border-color:#fff;margin-top:-0.375em;right:1.125em}html.theme--documenter-dark .navbar-dropdown{font-size:0.875rem;padding-bottom:0.5rem;padding-top:0.5rem}html.theme--documenter-dark .navbar-dropdown .navbar-item{padding-left:1.5rem;padding-right:1.5rem}html.theme--documenter-dark .navbar-divider{background-color:rgba(0,0,0,0.2);border:none;display:none;height:2px;margin:0.5rem 0}@media screen and (max-width: 1055px){html.theme--documenter-dark .navbar>.container{display:block}html.theme--documenter-dark .navbar-brand .navbar-item,html.theme--documenter-dark .navbar-tabs .navbar-item{align-items:center;display:flex}html.theme--documenter-dark .navbar-link::after{display:none}html.theme--documenter-dark .navbar-menu{background-color:#375a7f;box-shadow:0 8px 16px rgba(10,10,10,0.1);padding:0.5rem 0}html.theme--documenter-dark .navbar-menu.is-active{display:block}html.theme--documenter-dark .navbar.is-fixed-bottom-touch,html.theme--documenter-dark .navbar.is-fixed-top-touch{left:0;position:fixed;right:0;z-index:30}html.theme--documenter-dark .navbar.is-fixed-bottom-touch{bottom:0}html.theme--documenter-dark .navbar.is-fixed-bottom-touch.has-shadow{box-shadow:0 -2px 3px rgba(10,10,10,0.1)}html.theme--documenter-dark .navbar.is-fixed-top-touch{top:0}html.theme--documenter-dark .navbar.is-fixed-top .navbar-menu,html.theme--documenter-dark .navbar.is-fixed-top-touch .navbar-menu{-webkit-overflow-scrolling:touch;max-height:calc(100vh - 4rem);overflow:auto}html.theme--documenter-dark html.has-navbar-fixed-top-touch,html.theme--documenter-dark body.has-navbar-fixed-top-touch{padding-top:4rem}html.theme--documenter-dark html.has-navbar-fixed-bottom-touch,html.theme--documenter-dark body.has-navbar-fixed-bottom-touch{padding-bottom:4rem}}@media screen and (min-width: 1056px){html.theme--documenter-dark .navbar,html.theme--documenter-dark .navbar-menu,html.theme--documenter-dark .navbar-start,html.theme--documenter-dark .navbar-end{align-items:stretch;display:flex}html.theme--documenter-dark .navbar{min-height:4rem}html.theme--documenter-dark .navbar.is-spaced{padding:1rem 2rem}html.theme--documenter-dark .navbar.is-spaced .navbar-start,html.theme--documenter-dark .navbar.is-spaced .navbar-end{align-items:center}html.theme--documenter-dark .navbar.is-spaced a.navbar-item,html.theme--documenter-dark .navbar.is-spaced .navbar-link{border-radius:.4em}html.theme--documenter-dark .navbar.is-transparent a.navbar-item:focus,html.theme--documenter-dark .navbar.is-transparent a.navbar-item:hover,html.theme--documenter-dark .navbar.is-transparent a.navbar-item.is-active,html.theme--documenter-dark .navbar.is-transparent .navbar-link:focus,html.theme--documenter-dark .navbar.is-transparent .navbar-link:hover,html.theme--documenter-dark .navbar.is-transparent .navbar-link.is-active{background-color:transparent !important}html.theme--documenter-dark .navbar.is-transparent .navbar-item.has-dropdown.is-active .navbar-link,html.theme--documenter-dark .navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:focus .navbar-link,html.theme--documenter-dark .navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:focus-within .navbar-link,html.theme--documenter-dark .navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:hover .navbar-link{background-color:transparent !important}html.theme--documenter-dark .navbar.is-transparent .navbar-dropdown a.navbar-item:focus,html.theme--documenter-dark .navbar.is-transparent .navbar-dropdown a.navbar-item:hover{background-color:rgba(0,0,0,0);color:#dbdee0}html.theme--documenter-dark .navbar.is-transparent .navbar-dropdown a.navbar-item.is-active{background-color:rgba(0,0,0,0);color:#1abc9c}html.theme--documenter-dark .navbar-burger{display:none}html.theme--documenter-dark .navbar-item,html.theme--documenter-dark .navbar-link{align-items:center;display:flex}html.theme--documenter-dark .navbar-item.has-dropdown{align-items:stretch}html.theme--documenter-dark .navbar-item.has-dropdown-up .navbar-link::after{transform:rotate(135deg) translate(0.25em, -0.25em)}html.theme--documenter-dark .navbar-item.has-dropdown-up .navbar-dropdown{border-bottom:1px solid rgba(0,0,0,0.2);border-radius:8px 8px 0 0;border-top:none;bottom:100%;box-shadow:0 -8px 8px rgba(10,10,10,0.1);top:auto}html.theme--documenter-dark .navbar-item.is-active .navbar-dropdown,html.theme--documenter-dark .navbar-item.is-hoverable:focus .navbar-dropdown,html.theme--documenter-dark .navbar-item.is-hoverable:focus-within .navbar-dropdown,html.theme--documenter-dark .navbar-item.is-hoverable:hover .navbar-dropdown{display:block}.navbar.is-spaced html.theme--documenter-dark .navbar-item.is-active .navbar-dropdown,html.theme--documenter-dark .navbar-item.is-active .navbar-dropdown.is-boxed,.navbar.is-spaced html.theme--documenter-dark .navbar-item.is-hoverable:focus .navbar-dropdown,html.theme--documenter-dark .navbar-item.is-hoverable:focus .navbar-dropdown.is-boxed,.navbar.is-spaced html.theme--documenter-dark .navbar-item.is-hoverable:focus-within .navbar-dropdown,html.theme--documenter-dark .navbar-item.is-hoverable:focus-within .navbar-dropdown.is-boxed,.navbar.is-spaced html.theme--documenter-dark .navbar-item.is-hoverable:hover .navbar-dropdown,html.theme--documenter-dark .navbar-item.is-hoverable:hover .navbar-dropdown.is-boxed{opacity:1;pointer-events:auto;transform:translateY(0)}html.theme--documenter-dark .navbar-menu{flex-grow:1;flex-shrink:0}html.theme--documenter-dark .navbar-start{justify-content:flex-start;margin-right:auto}html.theme--documenter-dark .navbar-end{justify-content:flex-end;margin-left:auto}html.theme--documenter-dark .navbar-dropdown{background-color:#375a7f;border-bottom-left-radius:8px;border-bottom-right-radius:8px;border-top:1px solid rgba(0,0,0,0.2);box-shadow:0 8px 8px rgba(10,10,10,0.1);display:none;font-size:0.875rem;left:0;min-width:100%;position:absolute;top:100%;z-index:20}html.theme--documenter-dark .navbar-dropdown .navbar-item{padding:0.375rem 1rem;white-space:nowrap}html.theme--documenter-dark .navbar-dropdown a.navbar-item{padding-right:3rem}html.theme--documenter-dark .navbar-dropdown a.navbar-item:focus,html.theme--documenter-dark .navbar-dropdown a.navbar-item:hover{background-color:rgba(0,0,0,0);color:#dbdee0}html.theme--documenter-dark .navbar-dropdown a.navbar-item.is-active{background-color:rgba(0,0,0,0);color:#1abc9c}.navbar.is-spaced html.theme--documenter-dark .navbar-dropdown,html.theme--documenter-dark .navbar-dropdown.is-boxed{border-radius:8px;border-top:none;box-shadow:0 8px 8px rgba(10,10,10,0.1), 0 0 0 1px rgba(10,10,10,0.1);display:block;opacity:0;pointer-events:none;top:calc(100% + (-4px));transform:translateY(-5px);transition-duration:86ms;transition-property:opacity, transform}html.theme--documenter-dark .navbar-dropdown.is-right{left:auto;right:0}html.theme--documenter-dark .navbar-divider{display:block}html.theme--documenter-dark .navbar>.container .navbar-brand,html.theme--documenter-dark .container>.navbar .navbar-brand{margin-left:-.75rem}html.theme--documenter-dark .navbar>.container .navbar-menu,html.theme--documenter-dark .container>.navbar .navbar-menu{margin-right:-.75rem}html.theme--documenter-dark .navbar.is-fixed-bottom-desktop,html.theme--documenter-dark .navbar.is-fixed-top-desktop{left:0;position:fixed;right:0;z-index:30}html.theme--documenter-dark .navbar.is-fixed-bottom-desktop{bottom:0}html.theme--documenter-dark .navbar.is-fixed-bottom-desktop.has-shadow{box-shadow:0 -2px 3px rgba(10,10,10,0.1)}html.theme--documenter-dark .navbar.is-fixed-top-desktop{top:0}html.theme--documenter-dark html.has-navbar-fixed-top-desktop,html.theme--documenter-dark body.has-navbar-fixed-top-desktop{padding-top:4rem}html.theme--documenter-dark html.has-navbar-fixed-bottom-desktop,html.theme--documenter-dark body.has-navbar-fixed-bottom-desktop{padding-bottom:4rem}html.theme--documenter-dark html.has-spaced-navbar-fixed-top,html.theme--documenter-dark body.has-spaced-navbar-fixed-top{padding-top:6rem}html.theme--documenter-dark html.has-spaced-navbar-fixed-bottom,html.theme--documenter-dark body.has-spaced-navbar-fixed-bottom{padding-bottom:6rem}html.theme--documenter-dark a.navbar-item.is-active,html.theme--documenter-dark .navbar-link.is-active{color:#1abc9c}html.theme--documenter-dark a.navbar-item.is-active:not(:focus):not(:hover),html.theme--documenter-dark .navbar-link.is-active:not(:focus):not(:hover){background-color:rgba(0,0,0,0)}html.theme--documenter-dark .navbar-item.has-dropdown:focus .navbar-link,html.theme--documenter-dark .navbar-item.has-dropdown:hover .navbar-link,html.theme--documenter-dark .navbar-item.has-dropdown.is-active .navbar-link{background-color:rgba(0,0,0,0)}}html.theme--documenter-dark .hero.is-fullheight-with-navbar{min-height:calc(100vh - 4rem)}html.theme--documenter-dark .pagination{font-size:1rem;margin:-.25rem}html.theme--documenter-dark .pagination.is-small,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.pagination{font-size:.75rem}html.theme--documenter-dark .pagination.is-medium{font-size:1.25rem}html.theme--documenter-dark .pagination.is-large{font-size:1.5rem}html.theme--documenter-dark .pagination.is-rounded .pagination-previous,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.pagination .pagination-previous,html.theme--documenter-dark .pagination.is-rounded .pagination-next,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.pagination .pagination-next{padding-left:1em;padding-right:1em;border-radius:9999px}html.theme--documenter-dark .pagination.is-rounded .pagination-link,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.pagination .pagination-link{border-radius:9999px}html.theme--documenter-dark .pagination,html.theme--documenter-dark .pagination-list{align-items:center;display:flex;justify-content:center;text-align:center}html.theme--documenter-dark .pagination-previous,html.theme--documenter-dark .pagination-next,html.theme--documenter-dark .pagination-link,html.theme--documenter-dark .pagination-ellipsis{font-size:1em;justify-content:center;margin:.25rem;padding-left:.5em;padding-right:.5em;text-align:center}html.theme--documenter-dark .pagination-previous,html.theme--documenter-dark .pagination-next,html.theme--documenter-dark .pagination-link{border-color:#5e6d6f;color:#1abc9c;min-width:2.5em}html.theme--documenter-dark .pagination-previous:hover,html.theme--documenter-dark .pagination-next:hover,html.theme--documenter-dark .pagination-link:hover{border-color:#8c9b9d;color:#1dd2af}html.theme--documenter-dark .pagination-previous:focus,html.theme--documenter-dark .pagination-next:focus,html.theme--documenter-dark .pagination-link:focus{border-color:#8c9b9d}html.theme--documenter-dark .pagination-previous:active,html.theme--documenter-dark .pagination-next:active,html.theme--documenter-dark .pagination-link:active{box-shadow:inset 0 1px 2px rgba(10,10,10,0.2)}html.theme--documenter-dark .pagination-previous[disabled],html.theme--documenter-dark .pagination-previous.is-disabled,html.theme--documenter-dark .pagination-next[disabled],html.theme--documenter-dark .pagination-next.is-disabled,html.theme--documenter-dark .pagination-link[disabled],html.theme--documenter-dark .pagination-link.is-disabled{background-color:#5e6d6f;border-color:#5e6d6f;box-shadow:none;color:#fff;opacity:0.5}html.theme--documenter-dark .pagination-previous,html.theme--documenter-dark .pagination-next{padding-left:.75em;padding-right:.75em;white-space:nowrap}html.theme--documenter-dark .pagination-link.is-current{background-color:#1abc9c;border-color:#1abc9c;color:#fff}html.theme--documenter-dark .pagination-ellipsis{color:#8c9b9d;pointer-events:none}html.theme--documenter-dark .pagination-list{flex-wrap:wrap}html.theme--documenter-dark .pagination-list li{list-style:none}@media screen and (max-width: 768px){html.theme--documenter-dark .pagination{flex-wrap:wrap}html.theme--documenter-dark .pagination-previous,html.theme--documenter-dark .pagination-next{flex-grow:1;flex-shrink:1}html.theme--documenter-dark .pagination-list li{flex-grow:1;flex-shrink:1}}@media screen and (min-width: 769px),print{html.theme--documenter-dark .pagination-list{flex-grow:1;flex-shrink:1;justify-content:flex-start;order:1}html.theme--documenter-dark .pagination-previous,html.theme--documenter-dark .pagination-next,html.theme--documenter-dark .pagination-link,html.theme--documenter-dark .pagination-ellipsis{margin-bottom:0;margin-top:0}html.theme--documenter-dark .pagination-previous{order:2}html.theme--documenter-dark .pagination-next{order:3}html.theme--documenter-dark .pagination{justify-content:space-between;margin-bottom:0;margin-top:0}html.theme--documenter-dark .pagination.is-centered .pagination-previous{order:1}html.theme--documenter-dark .pagination.is-centered .pagination-list{justify-content:center;order:2}html.theme--documenter-dark .pagination.is-centered .pagination-next{order:3}html.theme--documenter-dark .pagination.is-right .pagination-previous{order:1}html.theme--documenter-dark .pagination.is-right .pagination-next{order:2}html.theme--documenter-dark .pagination.is-right .pagination-list{justify-content:flex-end;order:3}}html.theme--documenter-dark .panel{border-radius:8px;box-shadow:#171717;font-size:1rem}html.theme--documenter-dark .panel:not(:last-child){margin-bottom:1.5rem}html.theme--documenter-dark .panel.is-white .panel-heading{background-color:#fff;color:#0a0a0a}html.theme--documenter-dark .panel.is-white .panel-tabs a.is-active{border-bottom-color:#fff}html.theme--documenter-dark .panel.is-white .panel-block.is-active .panel-icon{color:#fff}html.theme--documenter-dark .panel.is-black .panel-heading{background-color:#0a0a0a;color:#fff}html.theme--documenter-dark .panel.is-black .panel-tabs a.is-active{border-bottom-color:#0a0a0a}html.theme--documenter-dark .panel.is-black .panel-block.is-active .panel-icon{color:#0a0a0a}html.theme--documenter-dark .panel.is-light .panel-heading{background-color:#ecf0f1;color:rgba(0,0,0,0.7)}html.theme--documenter-dark .panel.is-light .panel-tabs a.is-active{border-bottom-color:#ecf0f1}html.theme--documenter-dark .panel.is-light .panel-block.is-active .panel-icon{color:#ecf0f1}html.theme--documenter-dark .panel.is-dark .panel-heading,html.theme--documenter-dark .content kbd.panel .panel-heading{background-color:#282f2f;color:#fff}html.theme--documenter-dark .panel.is-dark .panel-tabs a.is-active,html.theme--documenter-dark .content kbd.panel .panel-tabs a.is-active{border-bottom-color:#282f2f}html.theme--documenter-dark .panel.is-dark .panel-block.is-active .panel-icon,html.theme--documenter-dark .content kbd.panel .panel-block.is-active .panel-icon{color:#282f2f}html.theme--documenter-dark .panel.is-primary .panel-heading,html.theme--documenter-dark .docstring>section>a.panel.docs-sourcelink .panel-heading{background-color:#375a7f;color:#fff}html.theme--documenter-dark .panel.is-primary .panel-tabs a.is-active,html.theme--documenter-dark .docstring>section>a.panel.docs-sourcelink .panel-tabs a.is-active{border-bottom-color:#375a7f}html.theme--documenter-dark .panel.is-primary .panel-block.is-active .panel-icon,html.theme--documenter-dark .docstring>section>a.panel.docs-sourcelink .panel-block.is-active .panel-icon{color:#375a7f}html.theme--documenter-dark .panel.is-link .panel-heading{background-color:#1abc9c;color:#fff}html.theme--documenter-dark .panel.is-link .panel-tabs a.is-active{border-bottom-color:#1abc9c}html.theme--documenter-dark .panel.is-link .panel-block.is-active .panel-icon{color:#1abc9c}html.theme--documenter-dark .panel.is-info .panel-heading{background-color:#024c7d;color:#fff}html.theme--documenter-dark .panel.is-info .panel-tabs a.is-active{border-bottom-color:#024c7d}html.theme--documenter-dark .panel.is-info .panel-block.is-active .panel-icon{color:#024c7d}html.theme--documenter-dark .panel.is-success .panel-heading{background-color:#008438;color:#fff}html.theme--documenter-dark .panel.is-success .panel-tabs a.is-active{border-bottom-color:#008438}html.theme--documenter-dark .panel.is-success .panel-block.is-active .panel-icon{color:#008438}html.theme--documenter-dark .panel.is-warning .panel-heading{background-color:#ad8100;color:#fff}html.theme--documenter-dark .panel.is-warning .panel-tabs a.is-active{border-bottom-color:#ad8100}html.theme--documenter-dark .panel.is-warning .panel-block.is-active .panel-icon{color:#ad8100}html.theme--documenter-dark .panel.is-danger .panel-heading{background-color:#9e1b0d;color:#fff}html.theme--documenter-dark .panel.is-danger .panel-tabs a.is-active{border-bottom-color:#9e1b0d}html.theme--documenter-dark .panel.is-danger .panel-block.is-active .panel-icon{color:#9e1b0d}html.theme--documenter-dark .panel-tabs:not(:last-child),html.theme--documenter-dark .panel-block:not(:last-child){border-bottom:1px solid #ededed}html.theme--documenter-dark .panel-heading{background-color:#343c3d;border-radius:8px 8px 0 0;color:#f2f2f2;font-size:1.25em;font-weight:700;line-height:1.25;padding:0.75em 1em}html.theme--documenter-dark .panel-tabs{align-items:flex-end;display:flex;font-size:.875em;justify-content:center}html.theme--documenter-dark .panel-tabs a{border-bottom:1px solid #5e6d6f;margin-bottom:-1px;padding:0.5em}html.theme--documenter-dark .panel-tabs a.is-active{border-bottom-color:#343c3d;color:#17a689}html.theme--documenter-dark .panel-list a{color:#fff}html.theme--documenter-dark .panel-list a:hover{color:#1abc9c}html.theme--documenter-dark .panel-block{align-items:center;color:#f2f2f2;display:flex;justify-content:flex-start;padding:0.5em 0.75em}html.theme--documenter-dark .panel-block input[type="checkbox"]{margin-right:.75em}html.theme--documenter-dark .panel-block>.control{flex-grow:1;flex-shrink:1;width:100%}html.theme--documenter-dark .panel-block.is-wrapped{flex-wrap:wrap}html.theme--documenter-dark .panel-block.is-active{border-left-color:#1abc9c;color:#17a689}html.theme--documenter-dark .panel-block.is-active .panel-icon{color:#1abc9c}html.theme--documenter-dark .panel-block:last-child{border-bottom-left-radius:8px;border-bottom-right-radius:8px}html.theme--documenter-dark a.panel-block,html.theme--documenter-dark label.panel-block{cursor:pointer}html.theme--documenter-dark a.panel-block:hover,html.theme--documenter-dark label.panel-block:hover{background-color:#282f2f}html.theme--documenter-dark .panel-icon{display:inline-block;font-size:14px;height:1em;line-height:1em;text-align:center;vertical-align:top;width:1em;color:#fff;margin-right:.75em}html.theme--documenter-dark .panel-icon .fa{font-size:inherit;line-height:inherit}html.theme--documenter-dark .tabs{-webkit-overflow-scrolling:touch;align-items:stretch;display:flex;font-size:1rem;justify-content:space-between;overflow:hidden;overflow-x:auto;white-space:nowrap}html.theme--documenter-dark .tabs a{align-items:center;border-bottom-color:#5e6d6f;border-bottom-style:solid;border-bottom-width:1px;color:#fff;display:flex;justify-content:center;margin-bottom:-1px;padding:0.5em 1em;vertical-align:top}html.theme--documenter-dark .tabs a:hover{border-bottom-color:#f2f2f2;color:#f2f2f2}html.theme--documenter-dark .tabs li{display:block}html.theme--documenter-dark .tabs li.is-active a{border-bottom-color:#1abc9c;color:#1abc9c}html.theme--documenter-dark .tabs ul{align-items:center;border-bottom-color:#5e6d6f;border-bottom-style:solid;border-bottom-width:1px;display:flex;flex-grow:1;flex-shrink:0;justify-content:flex-start}html.theme--documenter-dark .tabs ul.is-left{padding-right:0.75em}html.theme--documenter-dark .tabs ul.is-center{flex:none;justify-content:center;padding-left:0.75em;padding-right:0.75em}html.theme--documenter-dark .tabs ul.is-right{justify-content:flex-end;padding-left:0.75em}html.theme--documenter-dark .tabs .icon:first-child{margin-right:.5em}html.theme--documenter-dark .tabs .icon:last-child{margin-left:.5em}html.theme--documenter-dark .tabs.is-centered ul{justify-content:center}html.theme--documenter-dark .tabs.is-right ul{justify-content:flex-end}html.theme--documenter-dark .tabs.is-boxed a{border:1px solid transparent;border-radius:.4em .4em 0 0}html.theme--documenter-dark .tabs.is-boxed a:hover{background-color:#282f2f;border-bottom-color:#5e6d6f}html.theme--documenter-dark .tabs.is-boxed li.is-active a{background-color:#fff;border-color:#5e6d6f;border-bottom-color:rgba(0,0,0,0) !important}html.theme--documenter-dark .tabs.is-fullwidth li{flex-grow:1;flex-shrink:0}html.theme--documenter-dark .tabs.is-toggle a{border-color:#5e6d6f;border-style:solid;border-width:1px;margin-bottom:0;position:relative}html.theme--documenter-dark .tabs.is-toggle a:hover{background-color:#282f2f;border-color:#8c9b9d;z-index:2}html.theme--documenter-dark .tabs.is-toggle li+li{margin-left:-1px}html.theme--documenter-dark .tabs.is-toggle li:first-child a{border-top-left-radius:.4em;border-bottom-left-radius:.4em}html.theme--documenter-dark .tabs.is-toggle li:last-child a{border-top-right-radius:.4em;border-bottom-right-radius:.4em}html.theme--documenter-dark .tabs.is-toggle li.is-active a{background-color:#1abc9c;border-color:#1abc9c;color:#fff;z-index:1}html.theme--documenter-dark .tabs.is-toggle ul{border-bottom:none}html.theme--documenter-dark .tabs.is-toggle.is-toggle-rounded li:first-child a{border-bottom-left-radius:9999px;border-top-left-radius:9999px;padding-left:1.25em}html.theme--documenter-dark .tabs.is-toggle.is-toggle-rounded li:last-child a{border-bottom-right-radius:9999px;border-top-right-radius:9999px;padding-right:1.25em}html.theme--documenter-dark .tabs.is-small,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.tabs{font-size:.75rem}html.theme--documenter-dark .tabs.is-medium{font-size:1.25rem}html.theme--documenter-dark .tabs.is-large{font-size:1.5rem}html.theme--documenter-dark .column{display:block;flex-basis:0;flex-grow:1;flex-shrink:1;padding:.75rem}.columns.is-mobile>html.theme--documenter-dark .column.is-narrow{flex:none;width:unset}.columns.is-mobile>html.theme--documenter-dark .column.is-full{flex:none;width:100%}.columns.is-mobile>html.theme--documenter-dark .column.is-three-quarters{flex:none;width:75%}.columns.is-mobile>html.theme--documenter-dark .column.is-two-thirds{flex:none;width:66.6666%}.columns.is-mobile>html.theme--documenter-dark .column.is-half{flex:none;width:50%}.columns.is-mobile>html.theme--documenter-dark .column.is-one-third{flex:none;width:33.3333%}.columns.is-mobile>html.theme--documenter-dark .column.is-one-quarter{flex:none;width:25%}.columns.is-mobile>html.theme--documenter-dark .column.is-one-fifth{flex:none;width:20%}.columns.is-mobile>html.theme--documenter-dark .column.is-two-fifths{flex:none;width:40%}.columns.is-mobile>html.theme--documenter-dark .column.is-three-fifths{flex:none;width:60%}.columns.is-mobile>html.theme--documenter-dark .column.is-four-fifths{flex:none;width:80%}.columns.is-mobile>html.theme--documenter-dark .column.is-offset-three-quarters{margin-left:75%}.columns.is-mobile>html.theme--documenter-dark .column.is-offset-two-thirds{margin-left:66.6666%}.columns.is-mobile>html.theme--documenter-dark .column.is-offset-half{margin-left:50%}.columns.is-mobile>html.theme--documenter-dark .column.is-offset-one-third{margin-left:33.3333%}.columns.is-mobile>html.theme--documenter-dark .column.is-offset-one-quarter{margin-left:25%}.columns.is-mobile>html.theme--documenter-dark .column.is-offset-one-fifth{margin-left:20%}.columns.is-mobile>html.theme--documenter-dark .column.is-offset-two-fifths{margin-left:40%}.columns.is-mobile>html.theme--documenter-dark .column.is-offset-three-fifths{margin-left:60%}.columns.is-mobile>html.theme--documenter-dark .column.is-offset-four-fifths{margin-left:80%}.columns.is-mobile>html.theme--documenter-dark .column.is-0{flex:none;width:0%}.columns.is-mobile>html.theme--documenter-dark .column.is-offset-0{margin-left:0%}.columns.is-mobile>html.theme--documenter-dark .column.is-1{flex:none;width:8.33333337%}.columns.is-mobile>html.theme--documenter-dark .column.is-offset-1{margin-left:8.33333337%}.columns.is-mobile>html.theme--documenter-dark .column.is-2{flex:none;width:16.66666674%}.columns.is-mobile>html.theme--documenter-dark .column.is-offset-2{margin-left:16.66666674%}.columns.is-mobile>html.theme--documenter-dark .column.is-3{flex:none;width:25%}.columns.is-mobile>html.theme--documenter-dark .column.is-offset-3{margin-left:25%}.columns.is-mobile>html.theme--documenter-dark .column.is-4{flex:none;width:33.33333337%}.columns.is-mobile>html.theme--documenter-dark .column.is-offset-4{margin-left:33.33333337%}.columns.is-mobile>html.theme--documenter-dark .column.is-5{flex:none;width:41.66666674%}.columns.is-mobile>html.theme--documenter-dark .column.is-offset-5{margin-left:41.66666674%}.columns.is-mobile>html.theme--documenter-dark .column.is-6{flex:none;width:50%}.columns.is-mobile>html.theme--documenter-dark .column.is-offset-6{margin-left:50%}.columns.is-mobile>html.theme--documenter-dark .column.is-7{flex:none;width:58.33333337%}.columns.is-mobile>html.theme--documenter-dark .column.is-offset-7{margin-left:58.33333337%}.columns.is-mobile>html.theme--documenter-dark .column.is-8{flex:none;width:66.66666674%}.columns.is-mobile>html.theme--documenter-dark .column.is-offset-8{margin-left:66.66666674%}.columns.is-mobile>html.theme--documenter-dark .column.is-9{flex:none;width:75%}.columns.is-mobile>html.theme--documenter-dark .column.is-offset-9{margin-left:75%}.columns.is-mobile>html.theme--documenter-dark .column.is-10{flex:none;width:83.33333337%}.columns.is-mobile>html.theme--documenter-dark .column.is-offset-10{margin-left:83.33333337%}.columns.is-mobile>html.theme--documenter-dark .column.is-11{flex:none;width:91.66666674%}.columns.is-mobile>html.theme--documenter-dark .column.is-offset-11{margin-left:91.66666674%}.columns.is-mobile>html.theme--documenter-dark .column.is-12{flex:none;width:100%}.columns.is-mobile>html.theme--documenter-dark .column.is-offset-12{margin-left:100%}@media screen and (max-width: 768px){html.theme--documenter-dark .column.is-narrow-mobile{flex:none;width:unset}html.theme--documenter-dark .column.is-full-mobile{flex:none;width:100%}html.theme--documenter-dark .column.is-three-quarters-mobile{flex:none;width:75%}html.theme--documenter-dark .column.is-two-thirds-mobile{flex:none;width:66.6666%}html.theme--documenter-dark .column.is-half-mobile{flex:none;width:50%}html.theme--documenter-dark .column.is-one-third-mobile{flex:none;width:33.3333%}html.theme--documenter-dark .column.is-one-quarter-mobile{flex:none;width:25%}html.theme--documenter-dark .column.is-one-fifth-mobile{flex:none;width:20%}html.theme--documenter-dark .column.is-two-fifths-mobile{flex:none;width:40%}html.theme--documenter-dark .column.is-three-fifths-mobile{flex:none;width:60%}html.theme--documenter-dark .column.is-four-fifths-mobile{flex:none;width:80%}html.theme--documenter-dark .column.is-offset-three-quarters-mobile{margin-left:75%}html.theme--documenter-dark .column.is-offset-two-thirds-mobile{margin-left:66.6666%}html.theme--documenter-dark .column.is-offset-half-mobile{margin-left:50%}html.theme--documenter-dark .column.is-offset-one-third-mobile{margin-left:33.3333%}html.theme--documenter-dark .column.is-offset-one-quarter-mobile{margin-left:25%}html.theme--documenter-dark .column.is-offset-one-fifth-mobile{margin-left:20%}html.theme--documenter-dark .column.is-offset-two-fifths-mobile{margin-left:40%}html.theme--documenter-dark .column.is-offset-three-fifths-mobile{margin-left:60%}html.theme--documenter-dark .column.is-offset-four-fifths-mobile{margin-left:80%}html.theme--documenter-dark .column.is-0-mobile{flex:none;width:0%}html.theme--documenter-dark .column.is-offset-0-mobile{margin-left:0%}html.theme--documenter-dark .column.is-1-mobile{flex:none;width:8.33333337%}html.theme--documenter-dark .column.is-offset-1-mobile{margin-left:8.33333337%}html.theme--documenter-dark .column.is-2-mobile{flex:none;width:16.66666674%}html.theme--documenter-dark .column.is-offset-2-mobile{margin-left:16.66666674%}html.theme--documenter-dark .column.is-3-mobile{flex:none;width:25%}html.theme--documenter-dark .column.is-offset-3-mobile{margin-left:25%}html.theme--documenter-dark .column.is-4-mobile{flex:none;width:33.33333337%}html.theme--documenter-dark .column.is-offset-4-mobile{margin-left:33.33333337%}html.theme--documenter-dark .column.is-5-mobile{flex:none;width:41.66666674%}html.theme--documenter-dark .column.is-offset-5-mobile{margin-left:41.66666674%}html.theme--documenter-dark .column.is-6-mobile{flex:none;width:50%}html.theme--documenter-dark .column.is-offset-6-mobile{margin-left:50%}html.theme--documenter-dark .column.is-7-mobile{flex:none;width:58.33333337%}html.theme--documenter-dark .column.is-offset-7-mobile{margin-left:58.33333337%}html.theme--documenter-dark .column.is-8-mobile{flex:none;width:66.66666674%}html.theme--documenter-dark .column.is-offset-8-mobile{margin-left:66.66666674%}html.theme--documenter-dark .column.is-9-mobile{flex:none;width:75%}html.theme--documenter-dark .column.is-offset-9-mobile{margin-left:75%}html.theme--documenter-dark .column.is-10-mobile{flex:none;width:83.33333337%}html.theme--documenter-dark .column.is-offset-10-mobile{margin-left:83.33333337%}html.theme--documenter-dark .column.is-11-mobile{flex:none;width:91.66666674%}html.theme--documenter-dark .column.is-offset-11-mobile{margin-left:91.66666674%}html.theme--documenter-dark .column.is-12-mobile{flex:none;width:100%}html.theme--documenter-dark .column.is-offset-12-mobile{margin-left:100%}}@media screen and (min-width: 769px),print{html.theme--documenter-dark .column.is-narrow,html.theme--documenter-dark .column.is-narrow-tablet{flex:none;width:unset}html.theme--documenter-dark .column.is-full,html.theme--documenter-dark .column.is-full-tablet{flex:none;width:100%}html.theme--documenter-dark .column.is-three-quarters,html.theme--documenter-dark .column.is-three-quarters-tablet{flex:none;width:75%}html.theme--documenter-dark .column.is-two-thirds,html.theme--documenter-dark .column.is-two-thirds-tablet{flex:none;width:66.6666%}html.theme--documenter-dark .column.is-half,html.theme--documenter-dark .column.is-half-tablet{flex:none;width:50%}html.theme--documenter-dark .column.is-one-third,html.theme--documenter-dark .column.is-one-third-tablet{flex:none;width:33.3333%}html.theme--documenter-dark .column.is-one-quarter,html.theme--documenter-dark .column.is-one-quarter-tablet{flex:none;width:25%}html.theme--documenter-dark .column.is-one-fifth,html.theme--documenter-dark .column.is-one-fifth-tablet{flex:none;width:20%}html.theme--documenter-dark .column.is-two-fifths,html.theme--documenter-dark .column.is-two-fifths-tablet{flex:none;width:40%}html.theme--documenter-dark .column.is-three-fifths,html.theme--documenter-dark .column.is-three-fifths-tablet{flex:none;width:60%}html.theme--documenter-dark .column.is-four-fifths,html.theme--documenter-dark .column.is-four-fifths-tablet{flex:none;width:80%}html.theme--documenter-dark .column.is-offset-three-quarters,html.theme--documenter-dark .column.is-offset-three-quarters-tablet{margin-left:75%}html.theme--documenter-dark .column.is-offset-two-thirds,html.theme--documenter-dark .column.is-offset-two-thirds-tablet{margin-left:66.6666%}html.theme--documenter-dark .column.is-offset-half,html.theme--documenter-dark .column.is-offset-half-tablet{margin-left:50%}html.theme--documenter-dark .column.is-offset-one-third,html.theme--documenter-dark .column.is-offset-one-third-tablet{margin-left:33.3333%}html.theme--documenter-dark .column.is-offset-one-quarter,html.theme--documenter-dark .column.is-offset-one-quarter-tablet{margin-left:25%}html.theme--documenter-dark .column.is-offset-one-fifth,html.theme--documenter-dark .column.is-offset-one-fifth-tablet{margin-left:20%}html.theme--documenter-dark .column.is-offset-two-fifths,html.theme--documenter-dark .column.is-offset-two-fifths-tablet{margin-left:40%}html.theme--documenter-dark .column.is-offset-three-fifths,html.theme--documenter-dark .column.is-offset-three-fifths-tablet{margin-left:60%}html.theme--documenter-dark .column.is-offset-four-fifths,html.theme--documenter-dark .column.is-offset-four-fifths-tablet{margin-left:80%}html.theme--documenter-dark .column.is-0,html.theme--documenter-dark .column.is-0-tablet{flex:none;width:0%}html.theme--documenter-dark .column.is-offset-0,html.theme--documenter-dark .column.is-offset-0-tablet{margin-left:0%}html.theme--documenter-dark .column.is-1,html.theme--documenter-dark .column.is-1-tablet{flex:none;width:8.33333337%}html.theme--documenter-dark .column.is-offset-1,html.theme--documenter-dark .column.is-offset-1-tablet{margin-left:8.33333337%}html.theme--documenter-dark .column.is-2,html.theme--documenter-dark .column.is-2-tablet{flex:none;width:16.66666674%}html.theme--documenter-dark .column.is-offset-2,html.theme--documenter-dark .column.is-offset-2-tablet{margin-left:16.66666674%}html.theme--documenter-dark .column.is-3,html.theme--documenter-dark .column.is-3-tablet{flex:none;width:25%}html.theme--documenter-dark .column.is-offset-3,html.theme--documenter-dark .column.is-offset-3-tablet{margin-left:25%}html.theme--documenter-dark .column.is-4,html.theme--documenter-dark .column.is-4-tablet{flex:none;width:33.33333337%}html.theme--documenter-dark .column.is-offset-4,html.theme--documenter-dark .column.is-offset-4-tablet{margin-left:33.33333337%}html.theme--documenter-dark .column.is-5,html.theme--documenter-dark .column.is-5-tablet{flex:none;width:41.66666674%}html.theme--documenter-dark .column.is-offset-5,html.theme--documenter-dark .column.is-offset-5-tablet{margin-left:41.66666674%}html.theme--documenter-dark .column.is-6,html.theme--documenter-dark .column.is-6-tablet{flex:none;width:50%}html.theme--documenter-dark .column.is-offset-6,html.theme--documenter-dark .column.is-offset-6-tablet{margin-left:50%}html.theme--documenter-dark .column.is-7,html.theme--documenter-dark .column.is-7-tablet{flex:none;width:58.33333337%}html.theme--documenter-dark .column.is-offset-7,html.theme--documenter-dark .column.is-offset-7-tablet{margin-left:58.33333337%}html.theme--documenter-dark .column.is-8,html.theme--documenter-dark .column.is-8-tablet{flex:none;width:66.66666674%}html.theme--documenter-dark .column.is-offset-8,html.theme--documenter-dark .column.is-offset-8-tablet{margin-left:66.66666674%}html.theme--documenter-dark .column.is-9,html.theme--documenter-dark .column.is-9-tablet{flex:none;width:75%}html.theme--documenter-dark .column.is-offset-9,html.theme--documenter-dark .column.is-offset-9-tablet{margin-left:75%}html.theme--documenter-dark .column.is-10,html.theme--documenter-dark .column.is-10-tablet{flex:none;width:83.33333337%}html.theme--documenter-dark .column.is-offset-10,html.theme--documenter-dark .column.is-offset-10-tablet{margin-left:83.33333337%}html.theme--documenter-dark .column.is-11,html.theme--documenter-dark .column.is-11-tablet{flex:none;width:91.66666674%}html.theme--documenter-dark .column.is-offset-11,html.theme--documenter-dark .column.is-offset-11-tablet{margin-left:91.66666674%}html.theme--documenter-dark .column.is-12,html.theme--documenter-dark .column.is-12-tablet{flex:none;width:100%}html.theme--documenter-dark .column.is-offset-12,html.theme--documenter-dark .column.is-offset-12-tablet{margin-left:100%}}@media screen and (max-width: 1055px){html.theme--documenter-dark .column.is-narrow-touch{flex:none;width:unset}html.theme--documenter-dark .column.is-full-touch{flex:none;width:100%}html.theme--documenter-dark .column.is-three-quarters-touch{flex:none;width:75%}html.theme--documenter-dark .column.is-two-thirds-touch{flex:none;width:66.6666%}html.theme--documenter-dark .column.is-half-touch{flex:none;width:50%}html.theme--documenter-dark .column.is-one-third-touch{flex:none;width:33.3333%}html.theme--documenter-dark .column.is-one-quarter-touch{flex:none;width:25%}html.theme--documenter-dark .column.is-one-fifth-touch{flex:none;width:20%}html.theme--documenter-dark .column.is-two-fifths-touch{flex:none;width:40%}html.theme--documenter-dark .column.is-three-fifths-touch{flex:none;width:60%}html.theme--documenter-dark .column.is-four-fifths-touch{flex:none;width:80%}html.theme--documenter-dark .column.is-offset-three-quarters-touch{margin-left:75%}html.theme--documenter-dark .column.is-offset-two-thirds-touch{margin-left:66.6666%}html.theme--documenter-dark .column.is-offset-half-touch{margin-left:50%}html.theme--documenter-dark .column.is-offset-one-third-touch{margin-left:33.3333%}html.theme--documenter-dark .column.is-offset-one-quarter-touch{margin-left:25%}html.theme--documenter-dark .column.is-offset-one-fifth-touch{margin-left:20%}html.theme--documenter-dark .column.is-offset-two-fifths-touch{margin-left:40%}html.theme--documenter-dark .column.is-offset-three-fifths-touch{margin-left:60%}html.theme--documenter-dark .column.is-offset-four-fifths-touch{margin-left:80%}html.theme--documenter-dark .column.is-0-touch{flex:none;width:0%}html.theme--documenter-dark .column.is-offset-0-touch{margin-left:0%}html.theme--documenter-dark .column.is-1-touch{flex:none;width:8.33333337%}html.theme--documenter-dark .column.is-offset-1-touch{margin-left:8.33333337%}html.theme--documenter-dark .column.is-2-touch{flex:none;width:16.66666674%}html.theme--documenter-dark .column.is-offset-2-touch{margin-left:16.66666674%}html.theme--documenter-dark .column.is-3-touch{flex:none;width:25%}html.theme--documenter-dark .column.is-offset-3-touch{margin-left:25%}html.theme--documenter-dark .column.is-4-touch{flex:none;width:33.33333337%}html.theme--documenter-dark .column.is-offset-4-touch{margin-left:33.33333337%}html.theme--documenter-dark .column.is-5-touch{flex:none;width:41.66666674%}html.theme--documenter-dark .column.is-offset-5-touch{margin-left:41.66666674%}html.theme--documenter-dark .column.is-6-touch{flex:none;width:50%}html.theme--documenter-dark .column.is-offset-6-touch{margin-left:50%}html.theme--documenter-dark .column.is-7-touch{flex:none;width:58.33333337%}html.theme--documenter-dark .column.is-offset-7-touch{margin-left:58.33333337%}html.theme--documenter-dark .column.is-8-touch{flex:none;width:66.66666674%}html.theme--documenter-dark .column.is-offset-8-touch{margin-left:66.66666674%}html.theme--documenter-dark .column.is-9-touch{flex:none;width:75%}html.theme--documenter-dark .column.is-offset-9-touch{margin-left:75%}html.theme--documenter-dark .column.is-10-touch{flex:none;width:83.33333337%}html.theme--documenter-dark .column.is-offset-10-touch{margin-left:83.33333337%}html.theme--documenter-dark .column.is-11-touch{flex:none;width:91.66666674%}html.theme--documenter-dark .column.is-offset-11-touch{margin-left:91.66666674%}html.theme--documenter-dark .column.is-12-touch{flex:none;width:100%}html.theme--documenter-dark .column.is-offset-12-touch{margin-left:100%}}@media screen and (min-width: 1056px){html.theme--documenter-dark .column.is-narrow-desktop{flex:none;width:unset}html.theme--documenter-dark .column.is-full-desktop{flex:none;width:100%}html.theme--documenter-dark .column.is-three-quarters-desktop{flex:none;width:75%}html.theme--documenter-dark .column.is-two-thirds-desktop{flex:none;width:66.6666%}html.theme--documenter-dark .column.is-half-desktop{flex:none;width:50%}html.theme--documenter-dark .column.is-one-third-desktop{flex:none;width:33.3333%}html.theme--documenter-dark .column.is-one-quarter-desktop{flex:none;width:25%}html.theme--documenter-dark .column.is-one-fifth-desktop{flex:none;width:20%}html.theme--documenter-dark .column.is-two-fifths-desktop{flex:none;width:40%}html.theme--documenter-dark .column.is-three-fifths-desktop{flex:none;width:60%}html.theme--documenter-dark .column.is-four-fifths-desktop{flex:none;width:80%}html.theme--documenter-dark .column.is-offset-three-quarters-desktop{margin-left:75%}html.theme--documenter-dark .column.is-offset-two-thirds-desktop{margin-left:66.6666%}html.theme--documenter-dark .column.is-offset-half-desktop{margin-left:50%}html.theme--documenter-dark .column.is-offset-one-third-desktop{margin-left:33.3333%}html.theme--documenter-dark .column.is-offset-one-quarter-desktop{margin-left:25%}html.theme--documenter-dark .column.is-offset-one-fifth-desktop{margin-left:20%}html.theme--documenter-dark .column.is-offset-two-fifths-desktop{margin-left:40%}html.theme--documenter-dark .column.is-offset-three-fifths-desktop{margin-left:60%}html.theme--documenter-dark .column.is-offset-four-fifths-desktop{margin-left:80%}html.theme--documenter-dark .column.is-0-desktop{flex:none;width:0%}html.theme--documenter-dark .column.is-offset-0-desktop{margin-left:0%}html.theme--documenter-dark .column.is-1-desktop{flex:none;width:8.33333337%}html.theme--documenter-dark .column.is-offset-1-desktop{margin-left:8.33333337%}html.theme--documenter-dark .column.is-2-desktop{flex:none;width:16.66666674%}html.theme--documenter-dark .column.is-offset-2-desktop{margin-left:16.66666674%}html.theme--documenter-dark .column.is-3-desktop{flex:none;width:25%}html.theme--documenter-dark .column.is-offset-3-desktop{margin-left:25%}html.theme--documenter-dark .column.is-4-desktop{flex:none;width:33.33333337%}html.theme--documenter-dark .column.is-offset-4-desktop{margin-left:33.33333337%}html.theme--documenter-dark .column.is-5-desktop{flex:none;width:41.66666674%}html.theme--documenter-dark .column.is-offset-5-desktop{margin-left:41.66666674%}html.theme--documenter-dark .column.is-6-desktop{flex:none;width:50%}html.theme--documenter-dark .column.is-offset-6-desktop{margin-left:50%}html.theme--documenter-dark .column.is-7-desktop{flex:none;width:58.33333337%}html.theme--documenter-dark .column.is-offset-7-desktop{margin-left:58.33333337%}html.theme--documenter-dark .column.is-8-desktop{flex:none;width:66.66666674%}html.theme--documenter-dark .column.is-offset-8-desktop{margin-left:66.66666674%}html.theme--documenter-dark .column.is-9-desktop{flex:none;width:75%}html.theme--documenter-dark .column.is-offset-9-desktop{margin-left:75%}html.theme--documenter-dark .column.is-10-desktop{flex:none;width:83.33333337%}html.theme--documenter-dark .column.is-offset-10-desktop{margin-left:83.33333337%}html.theme--documenter-dark .column.is-11-desktop{flex:none;width:91.66666674%}html.theme--documenter-dark .column.is-offset-11-desktop{margin-left:91.66666674%}html.theme--documenter-dark .column.is-12-desktop{flex:none;width:100%}html.theme--documenter-dark .column.is-offset-12-desktop{margin-left:100%}}@media screen and (min-width: 1216px){html.theme--documenter-dark .column.is-narrow-widescreen{flex:none;width:unset}html.theme--documenter-dark .column.is-full-widescreen{flex:none;width:100%}html.theme--documenter-dark .column.is-three-quarters-widescreen{flex:none;width:75%}html.theme--documenter-dark .column.is-two-thirds-widescreen{flex:none;width:66.6666%}html.theme--documenter-dark .column.is-half-widescreen{flex:none;width:50%}html.theme--documenter-dark .column.is-one-third-widescreen{flex:none;width:33.3333%}html.theme--documenter-dark .column.is-one-quarter-widescreen{flex:none;width:25%}html.theme--documenter-dark .column.is-one-fifth-widescreen{flex:none;width:20%}html.theme--documenter-dark .column.is-two-fifths-widescreen{flex:none;width:40%}html.theme--documenter-dark .column.is-three-fifths-widescreen{flex:none;width:60%}html.theme--documenter-dark .column.is-four-fifths-widescreen{flex:none;width:80%}html.theme--documenter-dark .column.is-offset-three-quarters-widescreen{margin-left:75%}html.theme--documenter-dark .column.is-offset-two-thirds-widescreen{margin-left:66.6666%}html.theme--documenter-dark .column.is-offset-half-widescreen{margin-left:50%}html.theme--documenter-dark .column.is-offset-one-third-widescreen{margin-left:33.3333%}html.theme--documenter-dark .column.is-offset-one-quarter-widescreen{margin-left:25%}html.theme--documenter-dark .column.is-offset-one-fifth-widescreen{margin-left:20%}html.theme--documenter-dark .column.is-offset-two-fifths-widescreen{margin-left:40%}html.theme--documenter-dark .column.is-offset-three-fifths-widescreen{margin-left:60%}html.theme--documenter-dark .column.is-offset-four-fifths-widescreen{margin-left:80%}html.theme--documenter-dark .column.is-0-widescreen{flex:none;width:0%}html.theme--documenter-dark .column.is-offset-0-widescreen{margin-left:0%}html.theme--documenter-dark .column.is-1-widescreen{flex:none;width:8.33333337%}html.theme--documenter-dark .column.is-offset-1-widescreen{margin-left:8.33333337%}html.theme--documenter-dark .column.is-2-widescreen{flex:none;width:16.66666674%}html.theme--documenter-dark .column.is-offset-2-widescreen{margin-left:16.66666674%}html.theme--documenter-dark .column.is-3-widescreen{flex:none;width:25%}html.theme--documenter-dark .column.is-offset-3-widescreen{margin-left:25%}html.theme--documenter-dark .column.is-4-widescreen{flex:none;width:33.33333337%}html.theme--documenter-dark .column.is-offset-4-widescreen{margin-left:33.33333337%}html.theme--documenter-dark .column.is-5-widescreen{flex:none;width:41.66666674%}html.theme--documenter-dark .column.is-offset-5-widescreen{margin-left:41.66666674%}html.theme--documenter-dark .column.is-6-widescreen{flex:none;width:50%}html.theme--documenter-dark .column.is-offset-6-widescreen{margin-left:50%}html.theme--documenter-dark .column.is-7-widescreen{flex:none;width:58.33333337%}html.theme--documenter-dark .column.is-offset-7-widescreen{margin-left:58.33333337%}html.theme--documenter-dark .column.is-8-widescreen{flex:none;width:66.66666674%}html.theme--documenter-dark .column.is-offset-8-widescreen{margin-left:66.66666674%}html.theme--documenter-dark .column.is-9-widescreen{flex:none;width:75%}html.theme--documenter-dark .column.is-offset-9-widescreen{margin-left:75%}html.theme--documenter-dark .column.is-10-widescreen{flex:none;width:83.33333337%}html.theme--documenter-dark .column.is-offset-10-widescreen{margin-left:83.33333337%}html.theme--documenter-dark .column.is-11-widescreen{flex:none;width:91.66666674%}html.theme--documenter-dark .column.is-offset-11-widescreen{margin-left:91.66666674%}html.theme--documenter-dark .column.is-12-widescreen{flex:none;width:100%}html.theme--documenter-dark .column.is-offset-12-widescreen{margin-left:100%}}@media screen and (min-width: 1408px){html.theme--documenter-dark .column.is-narrow-fullhd{flex:none;width:unset}html.theme--documenter-dark .column.is-full-fullhd{flex:none;width:100%}html.theme--documenter-dark .column.is-three-quarters-fullhd{flex:none;width:75%}html.theme--documenter-dark .column.is-two-thirds-fullhd{flex:none;width:66.6666%}html.theme--documenter-dark .column.is-half-fullhd{flex:none;width:50%}html.theme--documenter-dark .column.is-one-third-fullhd{flex:none;width:33.3333%}html.theme--documenter-dark .column.is-one-quarter-fullhd{flex:none;width:25%}html.theme--documenter-dark .column.is-one-fifth-fullhd{flex:none;width:20%}html.theme--documenter-dark .column.is-two-fifths-fullhd{flex:none;width:40%}html.theme--documenter-dark .column.is-three-fifths-fullhd{flex:none;width:60%}html.theme--documenter-dark .column.is-four-fifths-fullhd{flex:none;width:80%}html.theme--documenter-dark .column.is-offset-three-quarters-fullhd{margin-left:75%}html.theme--documenter-dark .column.is-offset-two-thirds-fullhd{margin-left:66.6666%}html.theme--documenter-dark .column.is-offset-half-fullhd{margin-left:50%}html.theme--documenter-dark .column.is-offset-one-third-fullhd{margin-left:33.3333%}html.theme--documenter-dark .column.is-offset-one-quarter-fullhd{margin-left:25%}html.theme--documenter-dark .column.is-offset-one-fifth-fullhd{margin-left:20%}html.theme--documenter-dark .column.is-offset-two-fifths-fullhd{margin-left:40%}html.theme--documenter-dark .column.is-offset-three-fifths-fullhd{margin-left:60%}html.theme--documenter-dark .column.is-offset-four-fifths-fullhd{margin-left:80%}html.theme--documenter-dark .column.is-0-fullhd{flex:none;width:0%}html.theme--documenter-dark .column.is-offset-0-fullhd{margin-left:0%}html.theme--documenter-dark .column.is-1-fullhd{flex:none;width:8.33333337%}html.theme--documenter-dark .column.is-offset-1-fullhd{margin-left:8.33333337%}html.theme--documenter-dark .column.is-2-fullhd{flex:none;width:16.66666674%}html.theme--documenter-dark .column.is-offset-2-fullhd{margin-left:16.66666674%}html.theme--documenter-dark .column.is-3-fullhd{flex:none;width:25%}html.theme--documenter-dark .column.is-offset-3-fullhd{margin-left:25%}html.theme--documenter-dark .column.is-4-fullhd{flex:none;width:33.33333337%}html.theme--documenter-dark .column.is-offset-4-fullhd{margin-left:33.33333337%}html.theme--documenter-dark .column.is-5-fullhd{flex:none;width:41.66666674%}html.theme--documenter-dark .column.is-offset-5-fullhd{margin-left:41.66666674%}html.theme--documenter-dark .column.is-6-fullhd{flex:none;width:50%}html.theme--documenter-dark .column.is-offset-6-fullhd{margin-left:50%}html.theme--documenter-dark .column.is-7-fullhd{flex:none;width:58.33333337%}html.theme--documenter-dark .column.is-offset-7-fullhd{margin-left:58.33333337%}html.theme--documenter-dark .column.is-8-fullhd{flex:none;width:66.66666674%}html.theme--documenter-dark .column.is-offset-8-fullhd{margin-left:66.66666674%}html.theme--documenter-dark .column.is-9-fullhd{flex:none;width:75%}html.theme--documenter-dark .column.is-offset-9-fullhd{margin-left:75%}html.theme--documenter-dark .column.is-10-fullhd{flex:none;width:83.33333337%}html.theme--documenter-dark .column.is-offset-10-fullhd{margin-left:83.33333337%}html.theme--documenter-dark .column.is-11-fullhd{flex:none;width:91.66666674%}html.theme--documenter-dark .column.is-offset-11-fullhd{margin-left:91.66666674%}html.theme--documenter-dark .column.is-12-fullhd{flex:none;width:100%}html.theme--documenter-dark .column.is-offset-12-fullhd{margin-left:100%}}html.theme--documenter-dark .columns{margin-left:-.75rem;margin-right:-.75rem;margin-top:-.75rem}html.theme--documenter-dark .columns:last-child{margin-bottom:-.75rem}html.theme--documenter-dark .columns:not(:last-child){margin-bottom:calc(1.5rem - .75rem)}html.theme--documenter-dark .columns.is-centered{justify-content:center}html.theme--documenter-dark .columns.is-gapless{margin-left:0;margin-right:0;margin-top:0}html.theme--documenter-dark .columns.is-gapless>.column{margin:0;padding:0 !important}html.theme--documenter-dark .columns.is-gapless:not(:last-child){margin-bottom:1.5rem}html.theme--documenter-dark .columns.is-gapless:last-child{margin-bottom:0}html.theme--documenter-dark .columns.is-mobile{display:flex}html.theme--documenter-dark .columns.is-multiline{flex-wrap:wrap}html.theme--documenter-dark .columns.is-vcentered{align-items:center}@media screen and (min-width: 769px),print{html.theme--documenter-dark .columns:not(.is-desktop){display:flex}}@media screen and (min-width: 1056px){html.theme--documenter-dark .columns.is-desktop{display:flex}}html.theme--documenter-dark .columns.is-variable{--columnGap: 0.75rem;margin-left:calc(-1 * var(--columnGap));margin-right:calc(-1 * var(--columnGap))}html.theme--documenter-dark .columns.is-variable>.column{padding-left:var(--columnGap);padding-right:var(--columnGap)}html.theme--documenter-dark .columns.is-variable.is-0{--columnGap: 0rem}@media screen and (max-width: 768px){html.theme--documenter-dark .columns.is-variable.is-0-mobile{--columnGap: 0rem}}@media screen and (min-width: 769px),print{html.theme--documenter-dark .columns.is-variable.is-0-tablet{--columnGap: 0rem}}@media screen and (min-width: 769px) and (max-width: 1055px){html.theme--documenter-dark .columns.is-variable.is-0-tablet-only{--columnGap: 0rem}}@media screen and (max-width: 1055px){html.theme--documenter-dark .columns.is-variable.is-0-touch{--columnGap: 0rem}}@media screen and (min-width: 1056px){html.theme--documenter-dark .columns.is-variable.is-0-desktop{--columnGap: 0rem}}@media screen and (min-width: 1056px) and (max-width: 1215px){html.theme--documenter-dark .columns.is-variable.is-0-desktop-only{--columnGap: 0rem}}@media screen and (min-width: 1216px){html.theme--documenter-dark .columns.is-variable.is-0-widescreen{--columnGap: 0rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){html.theme--documenter-dark .columns.is-variable.is-0-widescreen-only{--columnGap: 0rem}}@media screen and (min-width: 1408px){html.theme--documenter-dark .columns.is-variable.is-0-fullhd{--columnGap: 0rem}}html.theme--documenter-dark .columns.is-variable.is-1{--columnGap: .25rem}@media screen and (max-width: 768px){html.theme--documenter-dark .columns.is-variable.is-1-mobile{--columnGap: .25rem}}@media screen and (min-width: 769px),print{html.theme--documenter-dark .columns.is-variable.is-1-tablet{--columnGap: .25rem}}@media screen and (min-width: 769px) and (max-width: 1055px){html.theme--documenter-dark .columns.is-variable.is-1-tablet-only{--columnGap: .25rem}}@media screen and (max-width: 1055px){html.theme--documenter-dark .columns.is-variable.is-1-touch{--columnGap: .25rem}}@media screen and (min-width: 1056px){html.theme--documenter-dark .columns.is-variable.is-1-desktop{--columnGap: .25rem}}@media screen and (min-width: 1056px) and (max-width: 1215px){html.theme--documenter-dark .columns.is-variable.is-1-desktop-only{--columnGap: .25rem}}@media screen and (min-width: 1216px){html.theme--documenter-dark .columns.is-variable.is-1-widescreen{--columnGap: .25rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){html.theme--documenter-dark .columns.is-variable.is-1-widescreen-only{--columnGap: .25rem}}@media screen and (min-width: 1408px){html.theme--documenter-dark .columns.is-variable.is-1-fullhd{--columnGap: .25rem}}html.theme--documenter-dark .columns.is-variable.is-2{--columnGap: .5rem}@media screen and (max-width: 768px){html.theme--documenter-dark .columns.is-variable.is-2-mobile{--columnGap: .5rem}}@media screen and (min-width: 769px),print{html.theme--documenter-dark .columns.is-variable.is-2-tablet{--columnGap: .5rem}}@media screen and (min-width: 769px) and (max-width: 1055px){html.theme--documenter-dark .columns.is-variable.is-2-tablet-only{--columnGap: .5rem}}@media screen and (max-width: 1055px){html.theme--documenter-dark .columns.is-variable.is-2-touch{--columnGap: .5rem}}@media screen and (min-width: 1056px){html.theme--documenter-dark .columns.is-variable.is-2-desktop{--columnGap: .5rem}}@media screen and (min-width: 1056px) and (max-width: 1215px){html.theme--documenter-dark .columns.is-variable.is-2-desktop-only{--columnGap: .5rem}}@media screen and (min-width: 1216px){html.theme--documenter-dark .columns.is-variable.is-2-widescreen{--columnGap: .5rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){html.theme--documenter-dark .columns.is-variable.is-2-widescreen-only{--columnGap: .5rem}}@media screen and (min-width: 1408px){html.theme--documenter-dark .columns.is-variable.is-2-fullhd{--columnGap: .5rem}}html.theme--documenter-dark .columns.is-variable.is-3{--columnGap: .75rem}@media screen and (max-width: 768px){html.theme--documenter-dark .columns.is-variable.is-3-mobile{--columnGap: .75rem}}@media screen and (min-width: 769px),print{html.theme--documenter-dark .columns.is-variable.is-3-tablet{--columnGap: .75rem}}@media screen and (min-width: 769px) and (max-width: 1055px){html.theme--documenter-dark .columns.is-variable.is-3-tablet-only{--columnGap: .75rem}}@media screen and (max-width: 1055px){html.theme--documenter-dark .columns.is-variable.is-3-touch{--columnGap: .75rem}}@media screen and (min-width: 1056px){html.theme--documenter-dark .columns.is-variable.is-3-desktop{--columnGap: .75rem}}@media screen and (min-width: 1056px) and (max-width: 1215px){html.theme--documenter-dark .columns.is-variable.is-3-desktop-only{--columnGap: .75rem}}@media screen and (min-width: 1216px){html.theme--documenter-dark .columns.is-variable.is-3-widescreen{--columnGap: .75rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){html.theme--documenter-dark .columns.is-variable.is-3-widescreen-only{--columnGap: .75rem}}@media screen and (min-width: 1408px){html.theme--documenter-dark .columns.is-variable.is-3-fullhd{--columnGap: .75rem}}html.theme--documenter-dark .columns.is-variable.is-4{--columnGap: 1rem}@media screen and (max-width: 768px){html.theme--documenter-dark .columns.is-variable.is-4-mobile{--columnGap: 1rem}}@media screen and (min-width: 769px),print{html.theme--documenter-dark .columns.is-variable.is-4-tablet{--columnGap: 1rem}}@media screen and (min-width: 769px) and (max-width: 1055px){html.theme--documenter-dark .columns.is-variable.is-4-tablet-only{--columnGap: 1rem}}@media screen and (max-width: 1055px){html.theme--documenter-dark .columns.is-variable.is-4-touch{--columnGap: 1rem}}@media screen and (min-width: 1056px){html.theme--documenter-dark .columns.is-variable.is-4-desktop{--columnGap: 1rem}}@media screen and (min-width: 1056px) and (max-width: 1215px){html.theme--documenter-dark .columns.is-variable.is-4-desktop-only{--columnGap: 1rem}}@media screen and (min-width: 1216px){html.theme--documenter-dark .columns.is-variable.is-4-widescreen{--columnGap: 1rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){html.theme--documenter-dark .columns.is-variable.is-4-widescreen-only{--columnGap: 1rem}}@media screen and (min-width: 1408px){html.theme--documenter-dark .columns.is-variable.is-4-fullhd{--columnGap: 1rem}}html.theme--documenter-dark .columns.is-variable.is-5{--columnGap: 1.25rem}@media screen and (max-width: 768px){html.theme--documenter-dark .columns.is-variable.is-5-mobile{--columnGap: 1.25rem}}@media screen and (min-width: 769px),print{html.theme--documenter-dark .columns.is-variable.is-5-tablet{--columnGap: 1.25rem}}@media screen and (min-width: 769px) and (max-width: 1055px){html.theme--documenter-dark .columns.is-variable.is-5-tablet-only{--columnGap: 1.25rem}}@media screen and (max-width: 1055px){html.theme--documenter-dark .columns.is-variable.is-5-touch{--columnGap: 1.25rem}}@media screen and (min-width: 1056px){html.theme--documenter-dark .columns.is-variable.is-5-desktop{--columnGap: 1.25rem}}@media screen and (min-width: 1056px) and (max-width: 1215px){html.theme--documenter-dark .columns.is-variable.is-5-desktop-only{--columnGap: 1.25rem}}@media screen and (min-width: 1216px){html.theme--documenter-dark .columns.is-variable.is-5-widescreen{--columnGap: 1.25rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){html.theme--documenter-dark .columns.is-variable.is-5-widescreen-only{--columnGap: 1.25rem}}@media screen and (min-width: 1408px){html.theme--documenter-dark .columns.is-variable.is-5-fullhd{--columnGap: 1.25rem}}html.theme--documenter-dark .columns.is-variable.is-6{--columnGap: 1.5rem}@media screen and (max-width: 768px){html.theme--documenter-dark .columns.is-variable.is-6-mobile{--columnGap: 1.5rem}}@media screen and (min-width: 769px),print{html.theme--documenter-dark .columns.is-variable.is-6-tablet{--columnGap: 1.5rem}}@media screen and (min-width: 769px) and (max-width: 1055px){html.theme--documenter-dark .columns.is-variable.is-6-tablet-only{--columnGap: 1.5rem}}@media screen and (max-width: 1055px){html.theme--documenter-dark .columns.is-variable.is-6-touch{--columnGap: 1.5rem}}@media screen and (min-width: 1056px){html.theme--documenter-dark .columns.is-variable.is-6-desktop{--columnGap: 1.5rem}}@media screen and (min-width: 1056px) and (max-width: 1215px){html.theme--documenter-dark .columns.is-variable.is-6-desktop-only{--columnGap: 1.5rem}}@media screen and (min-width: 1216px){html.theme--documenter-dark .columns.is-variable.is-6-widescreen{--columnGap: 1.5rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){html.theme--documenter-dark .columns.is-variable.is-6-widescreen-only{--columnGap: 1.5rem}}@media screen and (min-width: 1408px){html.theme--documenter-dark .columns.is-variable.is-6-fullhd{--columnGap: 1.5rem}}html.theme--documenter-dark .columns.is-variable.is-7{--columnGap: 1.75rem}@media screen and (max-width: 768px){html.theme--documenter-dark .columns.is-variable.is-7-mobile{--columnGap: 1.75rem}}@media screen and (min-width: 769px),print{html.theme--documenter-dark .columns.is-variable.is-7-tablet{--columnGap: 1.75rem}}@media screen and (min-width: 769px) and (max-width: 1055px){html.theme--documenter-dark .columns.is-variable.is-7-tablet-only{--columnGap: 1.75rem}}@media screen and (max-width: 1055px){html.theme--documenter-dark .columns.is-variable.is-7-touch{--columnGap: 1.75rem}}@media screen and (min-width: 1056px){html.theme--documenter-dark .columns.is-variable.is-7-desktop{--columnGap: 1.75rem}}@media screen and (min-width: 1056px) and (max-width: 1215px){html.theme--documenter-dark .columns.is-variable.is-7-desktop-only{--columnGap: 1.75rem}}@media screen and (min-width: 1216px){html.theme--documenter-dark .columns.is-variable.is-7-widescreen{--columnGap: 1.75rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){html.theme--documenter-dark .columns.is-variable.is-7-widescreen-only{--columnGap: 1.75rem}}@media screen and (min-width: 1408px){html.theme--documenter-dark .columns.is-variable.is-7-fullhd{--columnGap: 1.75rem}}html.theme--documenter-dark .columns.is-variable.is-8{--columnGap: 2rem}@media screen and (max-width: 768px){html.theme--documenter-dark .columns.is-variable.is-8-mobile{--columnGap: 2rem}}@media screen and (min-width: 769px),print{html.theme--documenter-dark .columns.is-variable.is-8-tablet{--columnGap: 2rem}}@media screen and (min-width: 769px) and (max-width: 1055px){html.theme--documenter-dark .columns.is-variable.is-8-tablet-only{--columnGap: 2rem}}@media screen and (max-width: 1055px){html.theme--documenter-dark .columns.is-variable.is-8-touch{--columnGap: 2rem}}@media screen and (min-width: 1056px){html.theme--documenter-dark .columns.is-variable.is-8-desktop{--columnGap: 2rem}}@media screen and (min-width: 1056px) and (max-width: 1215px){html.theme--documenter-dark .columns.is-variable.is-8-desktop-only{--columnGap: 2rem}}@media screen and (min-width: 1216px){html.theme--documenter-dark .columns.is-variable.is-8-widescreen{--columnGap: 2rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){html.theme--documenter-dark .columns.is-variable.is-8-widescreen-only{--columnGap: 2rem}}@media screen and (min-width: 1408px){html.theme--documenter-dark .columns.is-variable.is-8-fullhd{--columnGap: 2rem}}html.theme--documenter-dark .tile{align-items:stretch;display:block;flex-basis:0;flex-grow:1;flex-shrink:1;min-height:min-content}html.theme--documenter-dark .tile.is-ancestor{margin-left:-.75rem;margin-right:-.75rem;margin-top:-.75rem}html.theme--documenter-dark .tile.is-ancestor:last-child{margin-bottom:-.75rem}html.theme--documenter-dark .tile.is-ancestor:not(:last-child){margin-bottom:.75rem}html.theme--documenter-dark .tile.is-child{margin:0 !important}html.theme--documenter-dark .tile.is-parent{padding:.75rem}html.theme--documenter-dark .tile.is-vertical{flex-direction:column}html.theme--documenter-dark .tile.is-vertical>.tile.is-child:not(:last-child){margin-bottom:1.5rem !important}@media screen and (min-width: 769px),print{html.theme--documenter-dark .tile:not(.is-child){display:flex}html.theme--documenter-dark .tile.is-1{flex:none;width:8.33333337%}html.theme--documenter-dark .tile.is-2{flex:none;width:16.66666674%}html.theme--documenter-dark .tile.is-3{flex:none;width:25%}html.theme--documenter-dark .tile.is-4{flex:none;width:33.33333337%}html.theme--documenter-dark .tile.is-5{flex:none;width:41.66666674%}html.theme--documenter-dark .tile.is-6{flex:none;width:50%}html.theme--documenter-dark .tile.is-7{flex:none;width:58.33333337%}html.theme--documenter-dark .tile.is-8{flex:none;width:66.66666674%}html.theme--documenter-dark .tile.is-9{flex:none;width:75%}html.theme--documenter-dark .tile.is-10{flex:none;width:83.33333337%}html.theme--documenter-dark .tile.is-11{flex:none;width:91.66666674%}html.theme--documenter-dark .tile.is-12{flex:none;width:100%}}html.theme--documenter-dark .hero{align-items:stretch;display:flex;flex-direction:column;justify-content:space-between}html.theme--documenter-dark .hero .navbar{background:none}html.theme--documenter-dark .hero .tabs ul{border-bottom:none}html.theme--documenter-dark .hero.is-white{background-color:#fff;color:#0a0a0a}html.theme--documenter-dark .hero.is-white a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),html.theme--documenter-dark .hero.is-white strong{color:inherit}html.theme--documenter-dark .hero.is-white .title{color:#0a0a0a}html.theme--documenter-dark .hero.is-white .subtitle{color:rgba(10,10,10,0.9)}html.theme--documenter-dark .hero.is-white .subtitle a:not(.button),html.theme--documenter-dark .hero.is-white .subtitle strong{color:#0a0a0a}@media screen and (max-width: 1055px){html.theme--documenter-dark .hero.is-white .navbar-menu{background-color:#fff}}html.theme--documenter-dark .hero.is-white .navbar-item,html.theme--documenter-dark .hero.is-white .navbar-link{color:rgba(10,10,10,0.7)}html.theme--documenter-dark .hero.is-white a.navbar-item:hover,html.theme--documenter-dark .hero.is-white a.navbar-item.is-active,html.theme--documenter-dark .hero.is-white .navbar-link:hover,html.theme--documenter-dark .hero.is-white .navbar-link.is-active{background-color:#f2f2f2;color:#0a0a0a}html.theme--documenter-dark .hero.is-white .tabs a{color:#0a0a0a;opacity:0.9}html.theme--documenter-dark .hero.is-white .tabs a:hover{opacity:1}html.theme--documenter-dark .hero.is-white .tabs li.is-active a{color:#fff !important;opacity:1}html.theme--documenter-dark .hero.is-white .tabs.is-boxed a,html.theme--documenter-dark .hero.is-white .tabs.is-toggle a{color:#0a0a0a}html.theme--documenter-dark .hero.is-white .tabs.is-boxed a:hover,html.theme--documenter-dark .hero.is-white .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}html.theme--documenter-dark .hero.is-white .tabs.is-boxed li.is-active a,html.theme--documenter-dark .hero.is-white .tabs.is-boxed li.is-active a:hover,html.theme--documenter-dark .hero.is-white .tabs.is-toggle li.is-active a,html.theme--documenter-dark .hero.is-white .tabs.is-toggle li.is-active a:hover{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}html.theme--documenter-dark .hero.is-white.is-bold{background-image:linear-gradient(141deg, #e8e3e4 0%, #fff 71%, #fff 100%)}@media screen and (max-width: 768px){html.theme--documenter-dark .hero.is-white.is-bold .navbar-menu{background-image:linear-gradient(141deg, #e8e3e4 0%, #fff 71%, #fff 100%)}}html.theme--documenter-dark .hero.is-black{background-color:#0a0a0a;color:#fff}html.theme--documenter-dark .hero.is-black a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),html.theme--documenter-dark .hero.is-black strong{color:inherit}html.theme--documenter-dark .hero.is-black .title{color:#fff}html.theme--documenter-dark .hero.is-black .subtitle{color:rgba(255,255,255,0.9)}html.theme--documenter-dark .hero.is-black .subtitle a:not(.button),html.theme--documenter-dark .hero.is-black .subtitle strong{color:#fff}@media screen and (max-width: 1055px){html.theme--documenter-dark .hero.is-black .navbar-menu{background-color:#0a0a0a}}html.theme--documenter-dark .hero.is-black .navbar-item,html.theme--documenter-dark .hero.is-black .navbar-link{color:rgba(255,255,255,0.7)}html.theme--documenter-dark .hero.is-black a.navbar-item:hover,html.theme--documenter-dark .hero.is-black a.navbar-item.is-active,html.theme--documenter-dark .hero.is-black .navbar-link:hover,html.theme--documenter-dark .hero.is-black .navbar-link.is-active{background-color:#000;color:#fff}html.theme--documenter-dark .hero.is-black .tabs a{color:#fff;opacity:0.9}html.theme--documenter-dark .hero.is-black .tabs a:hover{opacity:1}html.theme--documenter-dark .hero.is-black .tabs li.is-active a{color:#0a0a0a !important;opacity:1}html.theme--documenter-dark .hero.is-black .tabs.is-boxed a,html.theme--documenter-dark .hero.is-black .tabs.is-toggle a{color:#fff}html.theme--documenter-dark .hero.is-black .tabs.is-boxed a:hover,html.theme--documenter-dark .hero.is-black .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}html.theme--documenter-dark .hero.is-black .tabs.is-boxed li.is-active a,html.theme--documenter-dark .hero.is-black .tabs.is-boxed li.is-active a:hover,html.theme--documenter-dark .hero.is-black .tabs.is-toggle li.is-active a,html.theme--documenter-dark .hero.is-black .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#0a0a0a}html.theme--documenter-dark .hero.is-black.is-bold{background-image:linear-gradient(141deg, #000 0%, #0a0a0a 71%, #181616 100%)}@media screen and (max-width: 768px){html.theme--documenter-dark .hero.is-black.is-bold .navbar-menu{background-image:linear-gradient(141deg, #000 0%, #0a0a0a 71%, #181616 100%)}}html.theme--documenter-dark .hero.is-light{background-color:#ecf0f1;color:rgba(0,0,0,0.7)}html.theme--documenter-dark .hero.is-light a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),html.theme--documenter-dark .hero.is-light strong{color:inherit}html.theme--documenter-dark .hero.is-light .title{color:rgba(0,0,0,0.7)}html.theme--documenter-dark .hero.is-light .subtitle{color:rgba(0,0,0,0.9)}html.theme--documenter-dark .hero.is-light .subtitle a:not(.button),html.theme--documenter-dark .hero.is-light .subtitle strong{color:rgba(0,0,0,0.7)}@media screen and (max-width: 1055px){html.theme--documenter-dark .hero.is-light .navbar-menu{background-color:#ecf0f1}}html.theme--documenter-dark .hero.is-light .navbar-item,html.theme--documenter-dark .hero.is-light .navbar-link{color:rgba(0,0,0,0.7)}html.theme--documenter-dark .hero.is-light a.navbar-item:hover,html.theme--documenter-dark .hero.is-light a.navbar-item.is-active,html.theme--documenter-dark .hero.is-light .navbar-link:hover,html.theme--documenter-dark .hero.is-light .navbar-link.is-active{background-color:#dde4e6;color:rgba(0,0,0,0.7)}html.theme--documenter-dark .hero.is-light .tabs a{color:rgba(0,0,0,0.7);opacity:0.9}html.theme--documenter-dark .hero.is-light .tabs a:hover{opacity:1}html.theme--documenter-dark .hero.is-light .tabs li.is-active a{color:#ecf0f1 !important;opacity:1}html.theme--documenter-dark .hero.is-light .tabs.is-boxed a,html.theme--documenter-dark .hero.is-light .tabs.is-toggle a{color:rgba(0,0,0,0.7)}html.theme--documenter-dark .hero.is-light .tabs.is-boxed a:hover,html.theme--documenter-dark .hero.is-light .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}html.theme--documenter-dark .hero.is-light .tabs.is-boxed li.is-active a,html.theme--documenter-dark .hero.is-light .tabs.is-boxed li.is-active a:hover,html.theme--documenter-dark .hero.is-light .tabs.is-toggle li.is-active a,html.theme--documenter-dark .hero.is-light .tabs.is-toggle li.is-active a:hover{background-color:rgba(0,0,0,0.7);border-color:rgba(0,0,0,0.7);color:#ecf0f1}html.theme--documenter-dark .hero.is-light.is-bold{background-image:linear-gradient(141deg, #cadfe0 0%, #ecf0f1 71%, #fafbfc 100%)}@media screen and (max-width: 768px){html.theme--documenter-dark .hero.is-light.is-bold .navbar-menu{background-image:linear-gradient(141deg, #cadfe0 0%, #ecf0f1 71%, #fafbfc 100%)}}html.theme--documenter-dark .hero.is-dark,html.theme--documenter-dark .content kbd.hero{background-color:#282f2f;color:#fff}html.theme--documenter-dark .hero.is-dark a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),html.theme--documenter-dark .content kbd.hero a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),html.theme--documenter-dark .hero.is-dark strong,html.theme--documenter-dark .content kbd.hero strong{color:inherit}html.theme--documenter-dark .hero.is-dark .title,html.theme--documenter-dark .content kbd.hero .title{color:#fff}html.theme--documenter-dark .hero.is-dark .subtitle,html.theme--documenter-dark .content kbd.hero .subtitle{color:rgba(255,255,255,0.9)}html.theme--documenter-dark .hero.is-dark .subtitle a:not(.button),html.theme--documenter-dark .content kbd.hero .subtitle a:not(.button),html.theme--documenter-dark .hero.is-dark .subtitle strong,html.theme--documenter-dark .content kbd.hero .subtitle strong{color:#fff}@media screen and (max-width: 1055px){html.theme--documenter-dark .hero.is-dark .navbar-menu,html.theme--documenter-dark .content kbd.hero .navbar-menu{background-color:#282f2f}}html.theme--documenter-dark .hero.is-dark .navbar-item,html.theme--documenter-dark .content kbd.hero .navbar-item,html.theme--documenter-dark .hero.is-dark .navbar-link,html.theme--documenter-dark .content kbd.hero .navbar-link{color:rgba(255,255,255,0.7)}html.theme--documenter-dark .hero.is-dark a.navbar-item:hover,html.theme--documenter-dark .content kbd.hero a.navbar-item:hover,html.theme--documenter-dark .hero.is-dark a.navbar-item.is-active,html.theme--documenter-dark .content kbd.hero a.navbar-item.is-active,html.theme--documenter-dark .hero.is-dark .navbar-link:hover,html.theme--documenter-dark .content kbd.hero .navbar-link:hover,html.theme--documenter-dark .hero.is-dark .navbar-link.is-active,html.theme--documenter-dark .content kbd.hero .navbar-link.is-active{background-color:#1d2122;color:#fff}html.theme--documenter-dark .hero.is-dark .tabs a,html.theme--documenter-dark .content kbd.hero .tabs a{color:#fff;opacity:0.9}html.theme--documenter-dark .hero.is-dark .tabs a:hover,html.theme--documenter-dark .content kbd.hero .tabs a:hover{opacity:1}html.theme--documenter-dark .hero.is-dark .tabs li.is-active a,html.theme--documenter-dark .content kbd.hero .tabs li.is-active a{color:#282f2f !important;opacity:1}html.theme--documenter-dark .hero.is-dark .tabs.is-boxed a,html.theme--documenter-dark .content kbd.hero .tabs.is-boxed a,html.theme--documenter-dark .hero.is-dark .tabs.is-toggle a,html.theme--documenter-dark .content kbd.hero .tabs.is-toggle a{color:#fff}html.theme--documenter-dark .hero.is-dark .tabs.is-boxed a:hover,html.theme--documenter-dark .content kbd.hero .tabs.is-boxed a:hover,html.theme--documenter-dark .hero.is-dark .tabs.is-toggle a:hover,html.theme--documenter-dark .content kbd.hero .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}html.theme--documenter-dark .hero.is-dark .tabs.is-boxed li.is-active a,html.theme--documenter-dark .content kbd.hero .tabs.is-boxed li.is-active a,html.theme--documenter-dark .hero.is-dark .tabs.is-boxed li.is-active a:hover,html.theme--documenter-dark .hero.is-dark .tabs.is-toggle li.is-active a,html.theme--documenter-dark .content kbd.hero .tabs.is-toggle li.is-active a,html.theme--documenter-dark .hero.is-dark .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#282f2f}html.theme--documenter-dark .hero.is-dark.is-bold,html.theme--documenter-dark .content kbd.hero.is-bold{background-image:linear-gradient(141deg, #0f1615 0%, #282f2f 71%, #313c40 100%)}@media screen and (max-width: 768px){html.theme--documenter-dark .hero.is-dark.is-bold .navbar-menu,html.theme--documenter-dark .content kbd.hero.is-bold .navbar-menu{background-image:linear-gradient(141deg, #0f1615 0%, #282f2f 71%, #313c40 100%)}}html.theme--documenter-dark .hero.is-primary,html.theme--documenter-dark .docstring>section>a.hero.docs-sourcelink{background-color:#375a7f;color:#fff}html.theme--documenter-dark .hero.is-primary a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),html.theme--documenter-dark .docstring>section>a.hero.docs-sourcelink a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),html.theme--documenter-dark .hero.is-primary strong,html.theme--documenter-dark .docstring>section>a.hero.docs-sourcelink strong{color:inherit}html.theme--documenter-dark .hero.is-primary .title,html.theme--documenter-dark .docstring>section>a.hero.docs-sourcelink .title{color:#fff}html.theme--documenter-dark .hero.is-primary .subtitle,html.theme--documenter-dark .docstring>section>a.hero.docs-sourcelink .subtitle{color:rgba(255,255,255,0.9)}html.theme--documenter-dark .hero.is-primary .subtitle a:not(.button),html.theme--documenter-dark .docstring>section>a.hero.docs-sourcelink .subtitle a:not(.button),html.theme--documenter-dark .hero.is-primary .subtitle strong,html.theme--documenter-dark .docstring>section>a.hero.docs-sourcelink .subtitle strong{color:#fff}@media screen and (max-width: 1055px){html.theme--documenter-dark .hero.is-primary .navbar-menu,html.theme--documenter-dark .docstring>section>a.hero.docs-sourcelink .navbar-menu{background-color:#375a7f}}html.theme--documenter-dark .hero.is-primary .navbar-item,html.theme--documenter-dark .docstring>section>a.hero.docs-sourcelink .navbar-item,html.theme--documenter-dark .hero.is-primary .navbar-link,html.theme--documenter-dark .docstring>section>a.hero.docs-sourcelink .navbar-link{color:rgba(255,255,255,0.7)}html.theme--documenter-dark .hero.is-primary a.navbar-item:hover,html.theme--documenter-dark .docstring>section>a.hero.docs-sourcelink a.navbar-item:hover,html.theme--documenter-dark .hero.is-primary a.navbar-item.is-active,html.theme--documenter-dark .docstring>section>a.hero.docs-sourcelink a.navbar-item.is-active,html.theme--documenter-dark .hero.is-primary .navbar-link:hover,html.theme--documenter-dark .docstring>section>a.hero.docs-sourcelink .navbar-link:hover,html.theme--documenter-dark .hero.is-primary .navbar-link.is-active,html.theme--documenter-dark .docstring>section>a.hero.docs-sourcelink .navbar-link.is-active{background-color:#2f4d6d;color:#fff}html.theme--documenter-dark .hero.is-primary .tabs a,html.theme--documenter-dark .docstring>section>a.hero.docs-sourcelink .tabs a{color:#fff;opacity:0.9}html.theme--documenter-dark .hero.is-primary .tabs a:hover,html.theme--documenter-dark .docstring>section>a.hero.docs-sourcelink .tabs a:hover{opacity:1}html.theme--documenter-dark .hero.is-primary .tabs li.is-active a,html.theme--documenter-dark .docstring>section>a.hero.docs-sourcelink .tabs li.is-active a{color:#375a7f !important;opacity:1}html.theme--documenter-dark .hero.is-primary .tabs.is-boxed a,html.theme--documenter-dark .docstring>section>a.hero.docs-sourcelink .tabs.is-boxed a,html.theme--documenter-dark .hero.is-primary .tabs.is-toggle a,html.theme--documenter-dark .docstring>section>a.hero.docs-sourcelink .tabs.is-toggle a{color:#fff}html.theme--documenter-dark .hero.is-primary .tabs.is-boxed a:hover,html.theme--documenter-dark .docstring>section>a.hero.docs-sourcelink .tabs.is-boxed a:hover,html.theme--documenter-dark .hero.is-primary .tabs.is-toggle a:hover,html.theme--documenter-dark .docstring>section>a.hero.docs-sourcelink .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}html.theme--documenter-dark .hero.is-primary .tabs.is-boxed li.is-active a,html.theme--documenter-dark .docstring>section>a.hero.docs-sourcelink .tabs.is-boxed li.is-active a,html.theme--documenter-dark .hero.is-primary .tabs.is-boxed li.is-active a:hover,html.theme--documenter-dark .hero.is-primary .tabs.is-toggle li.is-active a,html.theme--documenter-dark .docstring>section>a.hero.docs-sourcelink .tabs.is-toggle li.is-active a,html.theme--documenter-dark .hero.is-primary .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#375a7f}html.theme--documenter-dark .hero.is-primary.is-bold,html.theme--documenter-dark .docstring>section>a.hero.is-bold.docs-sourcelink{background-image:linear-gradient(141deg, #214b62 0%, #375a7f 71%, #3a5796 100%)}@media screen and (max-width: 768px){html.theme--documenter-dark .hero.is-primary.is-bold .navbar-menu,html.theme--documenter-dark .docstring>section>a.hero.is-bold.docs-sourcelink .navbar-menu{background-image:linear-gradient(141deg, #214b62 0%, #375a7f 71%, #3a5796 100%)}}html.theme--documenter-dark .hero.is-link{background-color:#1abc9c;color:#fff}html.theme--documenter-dark .hero.is-link a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),html.theme--documenter-dark .hero.is-link strong{color:inherit}html.theme--documenter-dark .hero.is-link .title{color:#fff}html.theme--documenter-dark .hero.is-link .subtitle{color:rgba(255,255,255,0.9)}html.theme--documenter-dark .hero.is-link .subtitle a:not(.button),html.theme--documenter-dark .hero.is-link .subtitle strong{color:#fff}@media screen and (max-width: 1055px){html.theme--documenter-dark .hero.is-link .navbar-menu{background-color:#1abc9c}}html.theme--documenter-dark .hero.is-link .navbar-item,html.theme--documenter-dark .hero.is-link .navbar-link{color:rgba(255,255,255,0.7)}html.theme--documenter-dark .hero.is-link a.navbar-item:hover,html.theme--documenter-dark .hero.is-link a.navbar-item.is-active,html.theme--documenter-dark .hero.is-link .navbar-link:hover,html.theme--documenter-dark .hero.is-link .navbar-link.is-active{background-color:#17a689;color:#fff}html.theme--documenter-dark .hero.is-link .tabs a{color:#fff;opacity:0.9}html.theme--documenter-dark .hero.is-link .tabs a:hover{opacity:1}html.theme--documenter-dark .hero.is-link .tabs li.is-active a{color:#1abc9c !important;opacity:1}html.theme--documenter-dark .hero.is-link .tabs.is-boxed a,html.theme--documenter-dark .hero.is-link .tabs.is-toggle a{color:#fff}html.theme--documenter-dark .hero.is-link .tabs.is-boxed a:hover,html.theme--documenter-dark .hero.is-link .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}html.theme--documenter-dark .hero.is-link .tabs.is-boxed li.is-active a,html.theme--documenter-dark .hero.is-link .tabs.is-boxed li.is-active a:hover,html.theme--documenter-dark .hero.is-link .tabs.is-toggle li.is-active a,html.theme--documenter-dark .hero.is-link .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#1abc9c}html.theme--documenter-dark .hero.is-link.is-bold{background-image:linear-gradient(141deg, #0c9764 0%, #1abc9c 71%, #17d8d2 100%)}@media screen and (max-width: 768px){html.theme--documenter-dark .hero.is-link.is-bold .navbar-menu{background-image:linear-gradient(141deg, #0c9764 0%, #1abc9c 71%, #17d8d2 100%)}}html.theme--documenter-dark .hero.is-info{background-color:#024c7d;color:#fff}html.theme--documenter-dark .hero.is-info a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),html.theme--documenter-dark .hero.is-info strong{color:inherit}html.theme--documenter-dark .hero.is-info .title{color:#fff}html.theme--documenter-dark .hero.is-info .subtitle{color:rgba(255,255,255,0.9)}html.theme--documenter-dark .hero.is-info .subtitle a:not(.button),html.theme--documenter-dark .hero.is-info .subtitle strong{color:#fff}@media screen and (max-width: 1055px){html.theme--documenter-dark .hero.is-info .navbar-menu{background-color:#024c7d}}html.theme--documenter-dark .hero.is-info .navbar-item,html.theme--documenter-dark .hero.is-info .navbar-link{color:rgba(255,255,255,0.7)}html.theme--documenter-dark .hero.is-info a.navbar-item:hover,html.theme--documenter-dark .hero.is-info a.navbar-item.is-active,html.theme--documenter-dark .hero.is-info .navbar-link:hover,html.theme--documenter-dark .hero.is-info .navbar-link.is-active{background-color:#023d64;color:#fff}html.theme--documenter-dark .hero.is-info .tabs a{color:#fff;opacity:0.9}html.theme--documenter-dark .hero.is-info .tabs a:hover{opacity:1}html.theme--documenter-dark .hero.is-info .tabs li.is-active a{color:#024c7d !important;opacity:1}html.theme--documenter-dark .hero.is-info .tabs.is-boxed a,html.theme--documenter-dark .hero.is-info .tabs.is-toggle a{color:#fff}html.theme--documenter-dark .hero.is-info .tabs.is-boxed a:hover,html.theme--documenter-dark .hero.is-info .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}html.theme--documenter-dark .hero.is-info .tabs.is-boxed li.is-active a,html.theme--documenter-dark .hero.is-info .tabs.is-boxed li.is-active a:hover,html.theme--documenter-dark .hero.is-info .tabs.is-toggle li.is-active a,html.theme--documenter-dark .hero.is-info .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#024c7d}html.theme--documenter-dark .hero.is-info.is-bold{background-image:linear-gradient(141deg, #003a4c 0%, #024c7d 71%, #004299 100%)}@media screen and (max-width: 768px){html.theme--documenter-dark .hero.is-info.is-bold .navbar-menu{background-image:linear-gradient(141deg, #003a4c 0%, #024c7d 71%, #004299 100%)}}html.theme--documenter-dark .hero.is-success{background-color:#008438;color:#fff}html.theme--documenter-dark .hero.is-success a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),html.theme--documenter-dark .hero.is-success strong{color:inherit}html.theme--documenter-dark .hero.is-success .title{color:#fff}html.theme--documenter-dark .hero.is-success .subtitle{color:rgba(255,255,255,0.9)}html.theme--documenter-dark .hero.is-success .subtitle a:not(.button),html.theme--documenter-dark .hero.is-success .subtitle strong{color:#fff}@media screen and (max-width: 1055px){html.theme--documenter-dark .hero.is-success .navbar-menu{background-color:#008438}}html.theme--documenter-dark .hero.is-success .navbar-item,html.theme--documenter-dark .hero.is-success .navbar-link{color:rgba(255,255,255,0.7)}html.theme--documenter-dark .hero.is-success a.navbar-item:hover,html.theme--documenter-dark .hero.is-success a.navbar-item.is-active,html.theme--documenter-dark .hero.is-success .navbar-link:hover,html.theme--documenter-dark .hero.is-success .navbar-link.is-active{background-color:#006b2d;color:#fff}html.theme--documenter-dark .hero.is-success .tabs a{color:#fff;opacity:0.9}html.theme--documenter-dark .hero.is-success .tabs a:hover{opacity:1}html.theme--documenter-dark .hero.is-success .tabs li.is-active a{color:#008438 !important;opacity:1}html.theme--documenter-dark .hero.is-success .tabs.is-boxed a,html.theme--documenter-dark .hero.is-success .tabs.is-toggle a{color:#fff}html.theme--documenter-dark .hero.is-success .tabs.is-boxed a:hover,html.theme--documenter-dark .hero.is-success .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}html.theme--documenter-dark .hero.is-success .tabs.is-boxed li.is-active a,html.theme--documenter-dark .hero.is-success .tabs.is-boxed li.is-active a:hover,html.theme--documenter-dark .hero.is-success .tabs.is-toggle li.is-active a,html.theme--documenter-dark .hero.is-success .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#008438}html.theme--documenter-dark .hero.is-success.is-bold{background-image:linear-gradient(141deg, #005115 0%, #008438 71%, #009e5d 100%)}@media screen and (max-width: 768px){html.theme--documenter-dark .hero.is-success.is-bold .navbar-menu{background-image:linear-gradient(141deg, #005115 0%, #008438 71%, #009e5d 100%)}}html.theme--documenter-dark .hero.is-warning{background-color:#ad8100;color:#fff}html.theme--documenter-dark .hero.is-warning a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),html.theme--documenter-dark .hero.is-warning strong{color:inherit}html.theme--documenter-dark .hero.is-warning .title{color:#fff}html.theme--documenter-dark .hero.is-warning .subtitle{color:rgba(255,255,255,0.9)}html.theme--documenter-dark .hero.is-warning .subtitle a:not(.button),html.theme--documenter-dark .hero.is-warning .subtitle strong{color:#fff}@media screen and (max-width: 1055px){html.theme--documenter-dark .hero.is-warning .navbar-menu{background-color:#ad8100}}html.theme--documenter-dark .hero.is-warning .navbar-item,html.theme--documenter-dark .hero.is-warning .navbar-link{color:rgba(255,255,255,0.7)}html.theme--documenter-dark .hero.is-warning a.navbar-item:hover,html.theme--documenter-dark .hero.is-warning a.navbar-item.is-active,html.theme--documenter-dark .hero.is-warning .navbar-link:hover,html.theme--documenter-dark .hero.is-warning .navbar-link.is-active{background-color:#946e00;color:#fff}html.theme--documenter-dark .hero.is-warning .tabs a{color:#fff;opacity:0.9}html.theme--documenter-dark .hero.is-warning .tabs a:hover{opacity:1}html.theme--documenter-dark .hero.is-warning .tabs li.is-active a{color:#ad8100 !important;opacity:1}html.theme--documenter-dark .hero.is-warning .tabs.is-boxed a,html.theme--documenter-dark .hero.is-warning .tabs.is-toggle a{color:#fff}html.theme--documenter-dark .hero.is-warning .tabs.is-boxed a:hover,html.theme--documenter-dark .hero.is-warning .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}html.theme--documenter-dark .hero.is-warning .tabs.is-boxed li.is-active a,html.theme--documenter-dark .hero.is-warning .tabs.is-boxed li.is-active a:hover,html.theme--documenter-dark .hero.is-warning .tabs.is-toggle li.is-active a,html.theme--documenter-dark .hero.is-warning .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#ad8100}html.theme--documenter-dark .hero.is-warning.is-bold{background-image:linear-gradient(141deg, #7a4700 0%, #ad8100 71%, #c7b500 100%)}@media screen and (max-width: 768px){html.theme--documenter-dark .hero.is-warning.is-bold .navbar-menu{background-image:linear-gradient(141deg, #7a4700 0%, #ad8100 71%, #c7b500 100%)}}html.theme--documenter-dark .hero.is-danger{background-color:#9e1b0d;color:#fff}html.theme--documenter-dark .hero.is-danger a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),html.theme--documenter-dark .hero.is-danger strong{color:inherit}html.theme--documenter-dark .hero.is-danger .title{color:#fff}html.theme--documenter-dark .hero.is-danger .subtitle{color:rgba(255,255,255,0.9)}html.theme--documenter-dark .hero.is-danger .subtitle a:not(.button),html.theme--documenter-dark .hero.is-danger .subtitle strong{color:#fff}@media screen and (max-width: 1055px){html.theme--documenter-dark .hero.is-danger .navbar-menu{background-color:#9e1b0d}}html.theme--documenter-dark .hero.is-danger .navbar-item,html.theme--documenter-dark .hero.is-danger .navbar-link{color:rgba(255,255,255,0.7)}html.theme--documenter-dark .hero.is-danger a.navbar-item:hover,html.theme--documenter-dark .hero.is-danger a.navbar-item.is-active,html.theme--documenter-dark .hero.is-danger .navbar-link:hover,html.theme--documenter-dark .hero.is-danger .navbar-link.is-active{background-color:#86170b;color:#fff}html.theme--documenter-dark .hero.is-danger .tabs a{color:#fff;opacity:0.9}html.theme--documenter-dark .hero.is-danger .tabs a:hover{opacity:1}html.theme--documenter-dark .hero.is-danger .tabs li.is-active a{color:#9e1b0d !important;opacity:1}html.theme--documenter-dark .hero.is-danger .tabs.is-boxed a,html.theme--documenter-dark .hero.is-danger .tabs.is-toggle a{color:#fff}html.theme--documenter-dark .hero.is-danger .tabs.is-boxed a:hover,html.theme--documenter-dark .hero.is-danger .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}html.theme--documenter-dark .hero.is-danger .tabs.is-boxed li.is-active a,html.theme--documenter-dark .hero.is-danger .tabs.is-boxed li.is-active a:hover,html.theme--documenter-dark .hero.is-danger .tabs.is-toggle li.is-active a,html.theme--documenter-dark .hero.is-danger .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#9e1b0d}html.theme--documenter-dark .hero.is-danger.is-bold{background-image:linear-gradient(141deg, #75030b 0%, #9e1b0d 71%, #ba380a 100%)}@media screen and (max-width: 768px){html.theme--documenter-dark .hero.is-danger.is-bold .navbar-menu{background-image:linear-gradient(141deg, #75030b 0%, #9e1b0d 71%, #ba380a 100%)}}html.theme--documenter-dark .hero.is-small .hero-body,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.hero .hero-body{padding:1.5rem}@media screen and (min-width: 769px),print{html.theme--documenter-dark .hero.is-medium .hero-body{padding:9rem 4.5rem}}@media screen and (min-width: 769px),print{html.theme--documenter-dark .hero.is-large .hero-body{padding:18rem 6rem}}html.theme--documenter-dark .hero.is-halfheight .hero-body,html.theme--documenter-dark .hero.is-fullheight .hero-body,html.theme--documenter-dark .hero.is-fullheight-with-navbar .hero-body{align-items:center;display:flex}html.theme--documenter-dark .hero.is-halfheight .hero-body>.container,html.theme--documenter-dark .hero.is-fullheight .hero-body>.container,html.theme--documenter-dark .hero.is-fullheight-with-navbar .hero-body>.container{flex-grow:1;flex-shrink:1}html.theme--documenter-dark .hero.is-halfheight{min-height:50vh}html.theme--documenter-dark .hero.is-fullheight{min-height:100vh}html.theme--documenter-dark .hero-video{overflow:hidden}html.theme--documenter-dark .hero-video video{left:50%;min-height:100%;min-width:100%;position:absolute;top:50%;transform:translate3d(-50%, -50%, 0)}html.theme--documenter-dark .hero-video.is-transparent{opacity:0.3}@media screen and (max-width: 768px){html.theme--documenter-dark .hero-video{display:none}}html.theme--documenter-dark .hero-buttons{margin-top:1.5rem}@media screen and (max-width: 768px){html.theme--documenter-dark .hero-buttons .button{display:flex}html.theme--documenter-dark .hero-buttons .button:not(:last-child){margin-bottom:0.75rem}}@media screen and (min-width: 769px),print{html.theme--documenter-dark .hero-buttons{display:flex;justify-content:center}html.theme--documenter-dark .hero-buttons .button:not(:last-child){margin-right:1.5rem}}html.theme--documenter-dark .hero-head,html.theme--documenter-dark .hero-foot{flex-grow:0;flex-shrink:0}html.theme--documenter-dark .hero-body{flex-grow:1;flex-shrink:0;padding:3rem 1.5rem}@media screen and (min-width: 769px),print{html.theme--documenter-dark .hero-body{padding:3rem 3rem}}html.theme--documenter-dark .section{padding:3rem 1.5rem}@media screen and (min-width: 1056px){html.theme--documenter-dark .section{padding:3rem 3rem}html.theme--documenter-dark .section.is-medium{padding:9rem 4.5rem}html.theme--documenter-dark .section.is-large{padding:18rem 6rem}}html.theme--documenter-dark .footer{background-color:#282f2f;padding:3rem 1.5rem 6rem}html.theme--documenter-dark hr{height:1px}html.theme--documenter-dark h6{text-transform:uppercase;letter-spacing:0.5px}html.theme--documenter-dark .hero{background-color:#343c3d}html.theme--documenter-dark a{transition:all 200ms ease}html.theme--documenter-dark .button{transition:all 200ms ease;border-width:1px;color:#fff}html.theme--documenter-dark .button.is-active,html.theme--documenter-dark .button.is-focused,html.theme--documenter-dark .button:active,html.theme--documenter-dark .button:focus{box-shadow:0 0 0 2px rgba(140,155,157,0.5)}html.theme--documenter-dark .button.is-white.is-hovered,html.theme--documenter-dark .button.is-white:hover{background-color:#fff}html.theme--documenter-dark .button.is-white.is-active,html.theme--documenter-dark .button.is-white.is-focused,html.theme--documenter-dark .button.is-white:active,html.theme--documenter-dark .button.is-white:focus{border-color:#fff;box-shadow:0 0 0 2px rgba(255,255,255,0.5)}html.theme--documenter-dark .button.is-black.is-hovered,html.theme--documenter-dark .button.is-black:hover{background-color:#1d1d1d}html.theme--documenter-dark .button.is-black.is-active,html.theme--documenter-dark .button.is-black.is-focused,html.theme--documenter-dark .button.is-black:active,html.theme--documenter-dark .button.is-black:focus{border-color:#0a0a0a;box-shadow:0 0 0 2px rgba(10,10,10,0.5)}html.theme--documenter-dark .button.is-light.is-hovered,html.theme--documenter-dark .button.is-light:hover{background-color:#fff}html.theme--documenter-dark .button.is-light.is-active,html.theme--documenter-dark .button.is-light.is-focused,html.theme--documenter-dark .button.is-light:active,html.theme--documenter-dark .button.is-light:focus{border-color:#ecf0f1;box-shadow:0 0 0 2px rgba(236,240,241,0.5)}html.theme--documenter-dark .button.is-dark.is-hovered,html.theme--documenter-dark .content kbd.button.is-hovered,html.theme--documenter-dark .button.is-dark:hover,html.theme--documenter-dark .content kbd.button:hover{background-color:#3a4344}html.theme--documenter-dark .button.is-dark.is-active,html.theme--documenter-dark .content kbd.button.is-active,html.theme--documenter-dark .button.is-dark.is-focused,html.theme--documenter-dark .content kbd.button.is-focused,html.theme--documenter-dark .button.is-dark:active,html.theme--documenter-dark .content kbd.button:active,html.theme--documenter-dark .button.is-dark:focus,html.theme--documenter-dark .content kbd.button:focus{border-color:#282f2f;box-shadow:0 0 0 2px rgba(40,47,47,0.5)}html.theme--documenter-dark .button.is-primary.is-hovered,html.theme--documenter-dark .docstring>section>a.button.is-hovered.docs-sourcelink,html.theme--documenter-dark .button.is-primary:hover,html.theme--documenter-dark .docstring>section>a.button.docs-sourcelink:hover{background-color:#436d9a}html.theme--documenter-dark .button.is-primary.is-active,html.theme--documenter-dark .docstring>section>a.button.is-active.docs-sourcelink,html.theme--documenter-dark .button.is-primary.is-focused,html.theme--documenter-dark .docstring>section>a.button.is-focused.docs-sourcelink,html.theme--documenter-dark .button.is-primary:active,html.theme--documenter-dark .docstring>section>a.button.docs-sourcelink:active,html.theme--documenter-dark .button.is-primary:focus,html.theme--documenter-dark .docstring>section>a.button.docs-sourcelink:focus{border-color:#375a7f;box-shadow:0 0 0 2px rgba(55,90,127,0.5)}html.theme--documenter-dark .button.is-link.is-hovered,html.theme--documenter-dark .button.is-link:hover{background-color:#1fdeb8}html.theme--documenter-dark .button.is-link.is-active,html.theme--documenter-dark .button.is-link.is-focused,html.theme--documenter-dark .button.is-link:active,html.theme--documenter-dark .button.is-link:focus{border-color:#1abc9c;box-shadow:0 0 0 2px rgba(26,188,156,0.5)}html.theme--documenter-dark .button.is-info.is-hovered,html.theme--documenter-dark .button.is-info:hover{background-color:#0363a3}html.theme--documenter-dark .button.is-info.is-active,html.theme--documenter-dark .button.is-info.is-focused,html.theme--documenter-dark .button.is-info:active,html.theme--documenter-dark .button.is-info:focus{border-color:#024c7d;box-shadow:0 0 0 2px rgba(2,76,125,0.5)}html.theme--documenter-dark .button.is-success.is-hovered,html.theme--documenter-dark .button.is-success:hover{background-color:#00aa48}html.theme--documenter-dark .button.is-success.is-active,html.theme--documenter-dark .button.is-success.is-focused,html.theme--documenter-dark .button.is-success:active,html.theme--documenter-dark .button.is-success:focus{border-color:#008438;box-shadow:0 0 0 2px rgba(0,132,56,0.5)}html.theme--documenter-dark .button.is-warning.is-hovered,html.theme--documenter-dark .button.is-warning:hover{background-color:#d39e00}html.theme--documenter-dark .button.is-warning.is-active,html.theme--documenter-dark .button.is-warning.is-focused,html.theme--documenter-dark .button.is-warning:active,html.theme--documenter-dark .button.is-warning:focus{border-color:#ad8100;box-shadow:0 0 0 2px rgba(173,129,0,0.5)}html.theme--documenter-dark .button.is-danger.is-hovered,html.theme--documenter-dark .button.is-danger:hover{background-color:#c12110}html.theme--documenter-dark .button.is-danger.is-active,html.theme--documenter-dark .button.is-danger.is-focused,html.theme--documenter-dark .button.is-danger:active,html.theme--documenter-dark .button.is-danger:focus{border-color:#9e1b0d;box-shadow:0 0 0 2px rgba(158,27,13,0.5)}html.theme--documenter-dark .label{color:#dbdee0}html.theme--documenter-dark .button,html.theme--documenter-dark .control.has-icons-left .icon,html.theme--documenter-dark .control.has-icons-right .icon,html.theme--documenter-dark .input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input,html.theme--documenter-dark .pagination-ellipsis,html.theme--documenter-dark .pagination-link,html.theme--documenter-dark .pagination-next,html.theme--documenter-dark .pagination-previous,html.theme--documenter-dark .select,html.theme--documenter-dark .select select,html.theme--documenter-dark .textarea{height:2.5em}html.theme--documenter-dark .input,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input,html.theme--documenter-dark .textarea{transition:all 200ms ease;box-shadow:none;border-width:1px;padding-left:1em;padding-right:1em}html.theme--documenter-dark .select:after,html.theme--documenter-dark .select select{border-width:1px}html.theme--documenter-dark .control.has-addons .button,html.theme--documenter-dark .control.has-addons .input,html.theme--documenter-dark .control.has-addons #documenter .docs-sidebar form.docs-search>input,html.theme--documenter-dark #documenter .docs-sidebar .control.has-addons form.docs-search>input,html.theme--documenter-dark .control.has-addons .select{margin-right:-1px}html.theme--documenter-dark .notification{background-color:#343c3d}html.theme--documenter-dark .card{box-shadow:none;border:1px solid #343c3d;background-color:#282f2f;border-radius:.4em}html.theme--documenter-dark .card .card-image img{border-radius:.4em .4em 0 0}html.theme--documenter-dark .card .card-header{box-shadow:none;background-color:rgba(18,18,18,0.2);border-radius:.4em .4em 0 0}html.theme--documenter-dark .card .card-footer{background-color:rgba(18,18,18,0.2)}html.theme--documenter-dark .card .card-footer,html.theme--documenter-dark .card .card-footer-item{border-width:1px;border-color:#343c3d}html.theme--documenter-dark .notification.is-white a:not(.button){color:#0a0a0a;text-decoration:underline}html.theme--documenter-dark .notification.is-black a:not(.button){color:#fff;text-decoration:underline}html.theme--documenter-dark .notification.is-light a:not(.button){color:rgba(0,0,0,0.7);text-decoration:underline}html.theme--documenter-dark .notification.is-dark a:not(.button),html.theme--documenter-dark .content kbd.notification a:not(.button){color:#fff;text-decoration:underline}html.theme--documenter-dark .notification.is-primary a:not(.button),html.theme--documenter-dark .docstring>section>a.notification.docs-sourcelink a:not(.button){color:#fff;text-decoration:underline}html.theme--documenter-dark .notification.is-link a:not(.button){color:#fff;text-decoration:underline}html.theme--documenter-dark .notification.is-info a:not(.button){color:#fff;text-decoration:underline}html.theme--documenter-dark .notification.is-success a:not(.button){color:#fff;text-decoration:underline}html.theme--documenter-dark .notification.is-warning a:not(.button){color:#fff;text-decoration:underline}html.theme--documenter-dark .notification.is-danger a:not(.button){color:#fff;text-decoration:underline}html.theme--documenter-dark .tag,html.theme--documenter-dark .content kbd,html.theme--documenter-dark .docstring>section>a.docs-sourcelink{border-radius:.4em}html.theme--documenter-dark .menu-list a{transition:all 300ms ease}html.theme--documenter-dark .modal-card-body{background-color:#282f2f}html.theme--documenter-dark .modal-card-foot,html.theme--documenter-dark .modal-card-head{border-color:#343c3d}html.theme--documenter-dark .message-header{font-weight:700;background-color:#343c3d;color:#fff}html.theme--documenter-dark .message-body{border-width:1px;border-color:#343c3d}html.theme--documenter-dark .navbar{border-radius:.4em}html.theme--documenter-dark .navbar.is-transparent{background:none}html.theme--documenter-dark .navbar.is-primary .navbar-dropdown a.navbar-item.is-active,html.theme--documenter-dark .docstring>section>a.navbar.docs-sourcelink .navbar-dropdown a.navbar-item.is-active{background-color:#1abc9c}@media screen and (max-width: 1055px){html.theme--documenter-dark .navbar .navbar-menu{background-color:#375a7f;border-radius:0 0 .4em .4em}}html.theme--documenter-dark .hero .navbar,html.theme--documenter-dark body>.navbar{border-radius:0}html.theme--documenter-dark .pagination-link,html.theme--documenter-dark .pagination-next,html.theme--documenter-dark .pagination-previous{border-width:1px}html.theme--documenter-dark .panel-block,html.theme--documenter-dark .panel-heading,html.theme--documenter-dark .panel-tabs{border-width:1px}html.theme--documenter-dark .panel-block:first-child,html.theme--documenter-dark .panel-heading:first-child,html.theme--documenter-dark .panel-tabs:first-child{border-top-width:1px}html.theme--documenter-dark .panel-heading{font-weight:700}html.theme--documenter-dark .panel-tabs a{border-width:1px;margin-bottom:-1px}html.theme--documenter-dark .panel-tabs a.is-active{border-bottom-color:#17a689}html.theme--documenter-dark .panel-block:hover{color:#1dd2af}html.theme--documenter-dark .panel-block:hover .panel-icon{color:#1dd2af}html.theme--documenter-dark .panel-block.is-active .panel-icon{color:#17a689}html.theme--documenter-dark .tabs a{border-bottom-width:1px;margin-bottom:-1px}html.theme--documenter-dark .tabs ul{border-bottom-width:1px}html.theme--documenter-dark .tabs.is-boxed a{border-width:1px}html.theme--documenter-dark .tabs.is-boxed li.is-active a{background-color:#1f2424}html.theme--documenter-dark .tabs.is-toggle li a{border-width:1px;margin-bottom:0}html.theme--documenter-dark .tabs.is-toggle li+li{margin-left:-1px}html.theme--documenter-dark .hero.is-white .navbar .navbar-dropdown .navbar-item:hover{background-color:rgba(0,0,0,0)}html.theme--documenter-dark .hero.is-black .navbar .navbar-dropdown .navbar-item:hover{background-color:rgba(0,0,0,0)}html.theme--documenter-dark .hero.is-light .navbar .navbar-dropdown .navbar-item:hover{background-color:rgba(0,0,0,0)}html.theme--documenter-dark .hero.is-dark .navbar .navbar-dropdown .navbar-item:hover,html.theme--documenter-dark .content kbd.hero .navbar .navbar-dropdown .navbar-item:hover{background-color:rgba(0,0,0,0)}html.theme--documenter-dark .hero.is-primary .navbar .navbar-dropdown .navbar-item:hover,html.theme--documenter-dark .docstring>section>a.hero.docs-sourcelink .navbar .navbar-dropdown .navbar-item:hover{background-color:rgba(0,0,0,0)}html.theme--documenter-dark .hero.is-link .navbar .navbar-dropdown .navbar-item:hover{background-color:rgba(0,0,0,0)}html.theme--documenter-dark .hero.is-info .navbar .navbar-dropdown .navbar-item:hover{background-color:rgba(0,0,0,0)}html.theme--documenter-dark .hero.is-success .navbar .navbar-dropdown .navbar-item:hover{background-color:rgba(0,0,0,0)}html.theme--documenter-dark .hero.is-warning .navbar .navbar-dropdown .navbar-item:hover{background-color:rgba(0,0,0,0)}html.theme--documenter-dark .hero.is-danger .navbar .navbar-dropdown .navbar-item:hover{background-color:rgba(0,0,0,0)}html.theme--documenter-dark h1 .docs-heading-anchor,html.theme--documenter-dark h1 .docs-heading-anchor:hover,html.theme--documenter-dark h1 .docs-heading-anchor:visited,html.theme--documenter-dark h2 .docs-heading-anchor,html.theme--documenter-dark h2 .docs-heading-anchor:hover,html.theme--documenter-dark h2 .docs-heading-anchor:visited,html.theme--documenter-dark h3 .docs-heading-anchor,html.theme--documenter-dark h3 .docs-heading-anchor:hover,html.theme--documenter-dark h3 .docs-heading-anchor:visited,html.theme--documenter-dark h4 .docs-heading-anchor,html.theme--documenter-dark h4 .docs-heading-anchor:hover,html.theme--documenter-dark h4 .docs-heading-anchor:visited,html.theme--documenter-dark h5 .docs-heading-anchor,html.theme--documenter-dark h5 .docs-heading-anchor:hover,html.theme--documenter-dark h5 .docs-heading-anchor:visited,html.theme--documenter-dark h6 .docs-heading-anchor,html.theme--documenter-dark h6 .docs-heading-anchor:hover,html.theme--documenter-dark h6 .docs-heading-anchor:visited{color:#f2f2f2}html.theme--documenter-dark h1 .docs-heading-anchor-permalink,html.theme--documenter-dark h2 .docs-heading-anchor-permalink,html.theme--documenter-dark h3 .docs-heading-anchor-permalink,html.theme--documenter-dark h4 .docs-heading-anchor-permalink,html.theme--documenter-dark h5 .docs-heading-anchor-permalink,html.theme--documenter-dark h6 .docs-heading-anchor-permalink{visibility:hidden;vertical-align:middle;margin-left:0.5em;font-size:0.7rem}html.theme--documenter-dark h1 .docs-heading-anchor-permalink::before,html.theme--documenter-dark h2 .docs-heading-anchor-permalink::before,html.theme--documenter-dark h3 .docs-heading-anchor-permalink::before,html.theme--documenter-dark h4 .docs-heading-anchor-permalink::before,html.theme--documenter-dark h5 .docs-heading-anchor-permalink::before,html.theme--documenter-dark h6 .docs-heading-anchor-permalink::before{font-family:"Font Awesome 6 Free";font-weight:900;content:"\f0c1"}html.theme--documenter-dark h1:hover .docs-heading-anchor-permalink,html.theme--documenter-dark h2:hover .docs-heading-anchor-permalink,html.theme--documenter-dark h3:hover .docs-heading-anchor-permalink,html.theme--documenter-dark h4:hover .docs-heading-anchor-permalink,html.theme--documenter-dark h5:hover .docs-heading-anchor-permalink,html.theme--documenter-dark h6:hover .docs-heading-anchor-permalink{visibility:visible}html.theme--documenter-dark .docs-light-only{display:none !important}html.theme--documenter-dark pre{position:relative;overflow:hidden}html.theme--documenter-dark pre code,html.theme--documenter-dark pre code.hljs{padding:0 .75rem !important;overflow:auto;display:block}html.theme--documenter-dark pre code:first-of-type,html.theme--documenter-dark pre code.hljs:first-of-type{padding-top:0.5rem !important}html.theme--documenter-dark pre code:last-of-type,html.theme--documenter-dark pre code.hljs:last-of-type{padding-bottom:0.5rem !important}html.theme--documenter-dark pre .copy-button{opacity:0.2;transition:opacity 0.2s;position:absolute;right:0em;top:0em;padding:0.5em;width:2.5em;height:2.5em;background:transparent;border:none;font-family:"Font Awesome 6 Free";color:#fff;cursor:pointer;text-align:center}html.theme--documenter-dark pre .copy-button:focus,html.theme--documenter-dark pre .copy-button:hover{opacity:1;background:rgba(255,255,255,0.1);color:#1abc9c}html.theme--documenter-dark pre .copy-button.success{color:#259a12;opacity:1}html.theme--documenter-dark pre .copy-button.error{color:#cb3c33;opacity:1}html.theme--documenter-dark pre:hover .copy-button{opacity:1}html.theme--documenter-dark .admonition{background-color:#282f2f;border-style:solid;border-width:1px;border-color:#5e6d6f;border-radius:.4em;font-size:1rem}html.theme--documenter-dark .admonition strong{color:currentColor}html.theme--documenter-dark .admonition.is-small,html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input.admonition{font-size:.75rem}html.theme--documenter-dark .admonition.is-medium{font-size:1.25rem}html.theme--documenter-dark .admonition.is-large{font-size:1.5rem}html.theme--documenter-dark .admonition.is-default{background-color:#282f2f;border-color:#5e6d6f}html.theme--documenter-dark .admonition.is-default>.admonition-header{background-color:#5e6d6f;color:#fff}html.theme--documenter-dark .admonition.is-default>.admonition-body{color:#fff}html.theme--documenter-dark .admonition.is-info{background-color:#282f2f;border-color:#024c7d}html.theme--documenter-dark .admonition.is-info>.admonition-header{background-color:#024c7d;color:#fff}html.theme--documenter-dark .admonition.is-info>.admonition-body{color:#fff}html.theme--documenter-dark .admonition.is-success{background-color:#282f2f;border-color:#008438}html.theme--documenter-dark .admonition.is-success>.admonition-header{background-color:#008438;color:#fff}html.theme--documenter-dark .admonition.is-success>.admonition-body{color:#fff}html.theme--documenter-dark .admonition.is-warning{background-color:#282f2f;border-color:#ad8100}html.theme--documenter-dark .admonition.is-warning>.admonition-header{background-color:#ad8100;color:#fff}html.theme--documenter-dark .admonition.is-warning>.admonition-body{color:#fff}html.theme--documenter-dark .admonition.is-danger{background-color:#282f2f;border-color:#9e1b0d}html.theme--documenter-dark .admonition.is-danger>.admonition-header{background-color:#9e1b0d;color:#fff}html.theme--documenter-dark .admonition.is-danger>.admonition-body{color:#fff}html.theme--documenter-dark .admonition.is-compat{background-color:#282f2f;border-color:#137886}html.theme--documenter-dark .admonition.is-compat>.admonition-header{background-color:#137886;color:#fff}html.theme--documenter-dark .admonition.is-compat>.admonition-body{color:#fff}html.theme--documenter-dark .admonition-header{color:#fff;background-color:#5e6d6f;align-items:center;font-weight:700;justify-content:space-between;line-height:1.25;padding:0.5rem .75rem;position:relative}html.theme--documenter-dark .admonition-header:before{font-family:"Font Awesome 6 Free";font-weight:900;margin-right:.75rem;content:"\f06a"}html.theme--documenter-dark details.admonition.is-details>.admonition-header{list-style:none}html.theme--documenter-dark details.admonition.is-details>.admonition-header:before{font-family:"Font Awesome 6 Free";font-weight:900;content:"\f055"}html.theme--documenter-dark details.admonition.is-details[open]>.admonition-header:before{font-family:"Font Awesome 6 Free";font-weight:900;content:"\f056"}html.theme--documenter-dark .admonition-body{color:#fff;padding:0.5rem .75rem}html.theme--documenter-dark .admonition-body pre{background-color:#282f2f}html.theme--documenter-dark .admonition-body code{background-color:rgba(255,255,255,0.05)}html.theme--documenter-dark .docstring{margin-bottom:1em;background-color:rgba(0,0,0,0);border:1px solid #5e6d6f;box-shadow:none;max-width:100%}html.theme--documenter-dark .docstring>header{cursor:pointer;display:flex;flex-grow:1;align-items:stretch;padding:0.5rem .75rem;background-color:#282f2f;box-shadow:0 0.125em 0.25em rgba(10,10,10,0.1);box-shadow:none;border-bottom:1px solid #5e6d6f;overflow:auto}html.theme--documenter-dark .docstring>header code{background-color:transparent}html.theme--documenter-dark .docstring>header .docstring-article-toggle-button{min-width:1.1rem;padding:0.2rem 0.2rem 0.2rem 0}html.theme--documenter-dark .docstring>header .docstring-binding{margin-right:0.3em}html.theme--documenter-dark .docstring>header .docstring-category{margin-left:0.3em}html.theme--documenter-dark .docstring>section{position:relative;padding:.75rem .75rem;border-bottom:1px solid #5e6d6f}html.theme--documenter-dark .docstring>section:last-child{border-bottom:none}html.theme--documenter-dark .docstring>section>a.docs-sourcelink{transition:opacity 0.3s;opacity:0;position:absolute;right:.375rem;bottom:.375rem}html.theme--documenter-dark .docstring>section>a.docs-sourcelink:focus{opacity:1 !important}html.theme--documenter-dark .docstring:hover>section>a.docs-sourcelink{opacity:0.2}html.theme--documenter-dark .docstring:focus-within>section>a.docs-sourcelink{opacity:0.2}html.theme--documenter-dark .docstring>section:hover a.docs-sourcelink{opacity:1}html.theme--documenter-dark .documenter-example-output{background-color:#1f2424}html.theme--documenter-dark .outdated-warning-overlay{position:fixed;top:0;left:0;right:0;box-shadow:0 0 10px rgba(0,0,0,0.3);z-index:999;background-color:#282f2f;color:#fff;border-bottom:3px solid #9e1b0d;padding:10px 35px;text-align:center;font-size:15px}html.theme--documenter-dark .outdated-warning-overlay .outdated-warning-closer{position:absolute;top:calc(50% - 10px);right:18px;cursor:pointer;width:12px}html.theme--documenter-dark .outdated-warning-overlay a{color:#1abc9c}html.theme--documenter-dark .outdated-warning-overlay a:hover{color:#1dd2af}html.theme--documenter-dark .content pre{border:1px solid #5e6d6f}html.theme--documenter-dark .content code{font-weight:inherit}html.theme--documenter-dark .content a code{color:#1abc9c}html.theme--documenter-dark .content h1 code,html.theme--documenter-dark .content h2 code,html.theme--documenter-dark .content h3 code,html.theme--documenter-dark .content h4 code,html.theme--documenter-dark .content h5 code,html.theme--documenter-dark .content h6 code{color:#f2f2f2}html.theme--documenter-dark .content table{display:block;width:initial;max-width:100%;overflow-x:auto}html.theme--documenter-dark .content blockquote>ul:first-child,html.theme--documenter-dark .content blockquote>ol:first-child,html.theme--documenter-dark .content .admonition-body>ul:first-child,html.theme--documenter-dark .content .admonition-body>ol:first-child{margin-top:0}html.theme--documenter-dark pre,html.theme--documenter-dark code{font-variant-ligatures:no-contextual}html.theme--documenter-dark .breadcrumb a.is-disabled{cursor:default;pointer-events:none}html.theme--documenter-dark .breadcrumb a.is-disabled,html.theme--documenter-dark .breadcrumb a.is-disabled:hover{color:#f2f2f2}html.theme--documenter-dark .hljs{background:initial !important}html.theme--documenter-dark .katex .katex-mathml{top:0;right:0}html.theme--documenter-dark .katex-display,html.theme--documenter-dark mjx-container,html.theme--documenter-dark .MathJax_Display{margin:0.5em 0 !important}html.theme--documenter-dark html{-moz-osx-font-smoothing:auto;-webkit-font-smoothing:auto}html.theme--documenter-dark li.no-marker{list-style:none}html.theme--documenter-dark #documenter .docs-main>article{overflow-wrap:break-word}html.theme--documenter-dark #documenter .docs-main>article .math-container{overflow-x:auto;overflow-y:hidden}@media screen and (min-width: 1056px){html.theme--documenter-dark #documenter .docs-main{max-width:52rem;margin-left:20rem;padding-right:1rem}}@media screen and (max-width: 1055px){html.theme--documenter-dark #documenter .docs-main{width:100%}html.theme--documenter-dark #documenter .docs-main>article{max-width:52rem;margin-left:auto;margin-right:auto;margin-bottom:1rem;padding:0 1rem}html.theme--documenter-dark #documenter .docs-main>header,html.theme--documenter-dark #documenter .docs-main>nav{max-width:100%;width:100%;margin:0}}html.theme--documenter-dark #documenter .docs-main header.docs-navbar{background-color:#1f2424;border-bottom:1px solid #5e6d6f;z-index:2;min-height:4rem;margin-bottom:1rem;display:flex}html.theme--documenter-dark #documenter .docs-main header.docs-navbar .breadcrumb{flex-grow:1;overflow-x:hidden}html.theme--documenter-dark #documenter .docs-main header.docs-navbar .docs-sidebar-button{display:block;font-size:1.5rem;padding-bottom:0.1rem;margin-right:1rem}html.theme--documenter-dark #documenter .docs-main header.docs-navbar .docs-right{display:flex;white-space:nowrap;gap:1rem;align-items:center}html.theme--documenter-dark #documenter .docs-main header.docs-navbar .docs-right .docs-icon,html.theme--documenter-dark #documenter .docs-main header.docs-navbar .docs-right .docs-label{display:inline-block}html.theme--documenter-dark #documenter .docs-main header.docs-navbar .docs-right .docs-label{padding:0;margin-left:0.3em}@media screen and (max-width: 1055px){html.theme--documenter-dark #documenter .docs-main header.docs-navbar .docs-right .docs-navbar-link{margin-left:0.4rem;margin-right:0.4rem}}html.theme--documenter-dark #documenter .docs-main header.docs-navbar>*{margin:auto 0}@media screen and (max-width: 1055px){html.theme--documenter-dark #documenter .docs-main header.docs-navbar{position:sticky;top:0;padding:0 1rem;transition-property:top, box-shadow;-webkit-transition-property:top, box-shadow;transition-duration:0.3s;-webkit-transition-duration:0.3s}html.theme--documenter-dark #documenter .docs-main header.docs-navbar.headroom--not-top{box-shadow:.2rem 0rem .4rem #171717;transition-duration:0.7s;-webkit-transition-duration:0.7s}html.theme--documenter-dark #documenter .docs-main header.docs-navbar.headroom--unpinned.headroom--not-top.headroom--not-bottom{top:-4.5rem;transition-duration:0.7s;-webkit-transition-duration:0.7s}}html.theme--documenter-dark #documenter .docs-main section.footnotes{border-top:1px solid #5e6d6f}html.theme--documenter-dark #documenter .docs-main section.footnotes li .tag:first-child,html.theme--documenter-dark #documenter .docs-main section.footnotes li .docstring>section>a.docs-sourcelink:first-child,html.theme--documenter-dark #documenter .docs-main section.footnotes li .content kbd:first-child,html.theme--documenter-dark .content #documenter .docs-main section.footnotes li kbd:first-child{margin-right:1em;margin-bottom:0.4em}html.theme--documenter-dark #documenter .docs-main .docs-footer{display:flex;flex-wrap:wrap;margin-left:0;margin-right:0;border-top:1px solid #5e6d6f;padding-top:1rem;padding-bottom:1rem}@media screen and (max-width: 1055px){html.theme--documenter-dark #documenter .docs-main .docs-footer{padding-left:1rem;padding-right:1rem}}html.theme--documenter-dark #documenter .docs-main .docs-footer .docs-footer-nextpage,html.theme--documenter-dark #documenter .docs-main .docs-footer .docs-footer-prevpage{flex-grow:1}html.theme--documenter-dark #documenter .docs-main .docs-footer .docs-footer-nextpage{text-align:right}html.theme--documenter-dark #documenter .docs-main .docs-footer .flexbox-break{flex-basis:100%;height:0}html.theme--documenter-dark #documenter .docs-main .docs-footer .footer-message{font-size:0.8em;margin:0.5em auto 0 auto;text-align:center}html.theme--documenter-dark #documenter .docs-sidebar{display:flex;flex-direction:column;color:#fff;background-color:#282f2f;border-right:1px solid #5e6d6f;padding:0;flex:0 0 18rem;z-index:5;font-size:1rem;position:fixed;left:-18rem;width:18rem;height:100%;transition:left 0.3s}html.theme--documenter-dark #documenter .docs-sidebar.visible{left:0;box-shadow:.4rem 0rem .8rem #171717}@media screen and (min-width: 1056px){html.theme--documenter-dark #documenter .docs-sidebar.visible{box-shadow:none}}@media screen and (min-width: 1056px){html.theme--documenter-dark #documenter .docs-sidebar{left:0;top:0}}html.theme--documenter-dark #documenter .docs-sidebar .docs-logo{margin-top:1rem;padding:0 1rem}html.theme--documenter-dark #documenter .docs-sidebar .docs-logo>img{max-height:6rem;margin:auto}html.theme--documenter-dark #documenter .docs-sidebar .docs-package-name{flex-shrink:0;font-size:1.5rem;font-weight:700;text-align:center;white-space:nowrap;overflow:hidden;padding:0.5rem 0}html.theme--documenter-dark #documenter .docs-sidebar .docs-package-name .docs-autofit{max-width:16.2rem}html.theme--documenter-dark #documenter .docs-sidebar .docs-package-name a,html.theme--documenter-dark #documenter .docs-sidebar .docs-package-name a:hover{color:#fff}html.theme--documenter-dark #documenter .docs-sidebar .docs-version-selector{border-top:1px solid #5e6d6f;display:none;padding:0.5rem}html.theme--documenter-dark #documenter .docs-sidebar .docs-version-selector.visible{display:flex}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu{flex-grow:1;user-select:none;border-top:1px solid #5e6d6f;padding-bottom:1.5rem}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu>li>.tocitem{font-weight:bold}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu>li li{font-size:.95rem;margin-left:1em;border-left:1px solid #5e6d6f}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu input.collapse-toggle{display:none}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu ul.collapsed{display:none}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu input:checked~ul.collapsed{display:block}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu label.tocitem{display:flex}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu label.tocitem .docs-label{flex-grow:2}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu label.tocitem .docs-chevron{display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1;font-size:.75rem;margin-left:1rem;margin-top:auto;margin-bottom:auto}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu label.tocitem .docs-chevron::before{font-family:"Font Awesome 6 Free";font-weight:900;content:"\f054"}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu input:checked~label.tocitem .docs-chevron::before{content:"\f078"}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu .tocitem{display:block;padding:0.5rem 0.5rem}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu .tocitem,html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu .tocitem:hover{color:#fff;background:#282f2f}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu a.tocitem:hover,html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu label.tocitem:hover{color:#fff;background-color:#32393a}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu li.is-active{border-top:1px solid #5e6d6f;border-bottom:1px solid #5e6d6f;background-color:#1f2424}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu li.is-active .tocitem,html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu li.is-active .tocitem:hover{background-color:#1f2424;color:#fff}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu li.is-active ul.internal .tocitem:hover{background-color:#32393a;color:#fff}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu>li.is-active:first-child{border-top:none}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu ul.internal{margin:0 0.5rem 0.5rem;border-top:1px solid #5e6d6f}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu ul.internal li{font-size:.85rem;border-left:none;margin-left:0;margin-top:0.5rem}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu ul.internal .tocitem{width:100%;padding:0}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu ul.internal .tocitem::before{content:"⚬";margin-right:0.4em}html.theme--documenter-dark #documenter .docs-sidebar form.docs-search{margin:auto;margin-top:0.5rem;margin-bottom:0.5rem}html.theme--documenter-dark #documenter .docs-sidebar form.docs-search>input{width:14.4rem}html.theme--documenter-dark #documenter .docs-sidebar #documenter-search-query{color:#868c98;width:14.4rem;box-shadow:inset 0 1px 2px rgba(10,10,10,0.1)}@media screen and (min-width: 1056px){html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu{overflow-y:auto;-webkit-overflow-scroll:touch}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu::-webkit-scrollbar{width:.3rem;background:none}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu::-webkit-scrollbar-thumb{border-radius:5px 0px 0px 5px;background:#3b4445}html.theme--documenter-dark #documenter .docs-sidebar ul.docs-menu::-webkit-scrollbar-thumb:hover{background:#4e5a5c}}@media screen and (max-width: 1055px){html.theme--documenter-dark #documenter .docs-sidebar{overflow-y:auto;-webkit-overflow-scroll:touch}html.theme--documenter-dark #documenter .docs-sidebar::-webkit-scrollbar{width:.3rem;background:none}html.theme--documenter-dark #documenter .docs-sidebar::-webkit-scrollbar-thumb{border-radius:5px 0px 0px 5px;background:#3b4445}html.theme--documenter-dark #documenter .docs-sidebar::-webkit-scrollbar-thumb:hover{background:#4e5a5c}}html.theme--documenter-dark kbd.search-modal-key-hints{border-radius:0.25rem;border:1px solid rgba(245,245,245,0.6);box-shadow:0 2px 0 1px rgba(245,245,245,0.6);cursor:default;font-size:0.9rem;line-height:1.5;min-width:0.75rem;text-align:center;padding:0.1rem 0.3rem;position:relative;top:-1px}html.theme--documenter-dark .search-min-width-50{min-width:50%}html.theme--documenter-dark .search-min-height-100{min-height:100%}html.theme--documenter-dark .search-modal-card-body{max-height:calc(100vh - 15rem)}html.theme--documenter-dark .search-result-link{border-radius:0.7em;transition:all 300ms}html.theme--documenter-dark .search-result-link:hover,html.theme--documenter-dark .search-result-link:focus{background-color:rgba(0,128,128,0.1)}html.theme--documenter-dark .search-result-link .property-search-result-badge,html.theme--documenter-dark .search-result-link .search-filter{transition:all 300ms}html.theme--documenter-dark .property-search-result-badge,html.theme--documenter-dark .search-filter{padding:0.15em 0.5em;font-size:0.8em;font-style:italic;text-transform:none !important;line-height:1.5;color:#f5f5f5;background-color:rgba(51,65,85,0.501961);border-radius:0.6rem}html.theme--documenter-dark .search-result-link:hover .property-search-result-badge,html.theme--documenter-dark .search-result-link:hover .search-filter,html.theme--documenter-dark .search-result-link:focus .property-search-result-badge,html.theme--documenter-dark .search-result-link:focus .search-filter{color:#333;background-color:#f1f5f9}html.theme--documenter-dark .search-filter{color:#333;background-color:#f5f5f5;transition:all 300ms}html.theme--documenter-dark .search-filter:hover,html.theme--documenter-dark .search-filter:focus{color:#333}html.theme--documenter-dark .search-filter-selected{color:#f5f5f5;background-color:rgba(139,0,139,0.5)}html.theme--documenter-dark .search-filter-selected:hover,html.theme--documenter-dark .search-filter-selected:focus{color:#f5f5f5}html.theme--documenter-dark .search-result-highlight{background-color:#ffdd57;color:black}html.theme--documenter-dark .search-divider{border-bottom:1px solid #5e6d6f}html.theme--documenter-dark .search-result-title{width:85%;color:#f5f5f5}html.theme--documenter-dark .search-result-code-title{font-size:0.875rem;font-family:"JuliaMono","SFMono-Regular","Menlo","Consolas","Liberation Mono","DejaVu Sans Mono",monospace}html.theme--documenter-dark #search-modal .modal-card-body::-webkit-scrollbar,html.theme--documenter-dark #search-modal .filter-tabs::-webkit-scrollbar{height:10px;width:10px;background-color:transparent}html.theme--documenter-dark #search-modal .modal-card-body::-webkit-scrollbar-thumb,html.theme--documenter-dark #search-modal .filter-tabs::-webkit-scrollbar-thumb{background-color:gray;border-radius:1rem}html.theme--documenter-dark #search-modal .modal-card-body::-webkit-scrollbar-track,html.theme--documenter-dark #search-modal .filter-tabs::-webkit-scrollbar-track{-webkit-box-shadow:inset 0 0 6px rgba(0,0,0,0.6);background-color:transparent}html.theme--documenter-dark .w-100{width:100%}html.theme--documenter-dark .gap-2{gap:0.5rem}html.theme--documenter-dark .gap-4{gap:1rem}html.theme--documenter-dark .gap-8{gap:2rem}html.theme--documenter-dark{background-color:#1f2424;font-size:16px;min-width:300px;overflow-x:auto;overflow-y:scroll;text-rendering:optimizeLegibility;text-size-adjust:100%}html.theme--documenter-dark .ansi span.sgr1{font-weight:bolder}html.theme--documenter-dark .ansi span.sgr2{font-weight:lighter}html.theme--documenter-dark .ansi span.sgr3{font-style:italic}html.theme--documenter-dark .ansi span.sgr4{text-decoration:underline}html.theme--documenter-dark .ansi span.sgr7{color:#1f2424;background-color:#fff}html.theme--documenter-dark .ansi span.sgr8{color:transparent}html.theme--documenter-dark .ansi span.sgr8 span{color:transparent}html.theme--documenter-dark .ansi span.sgr9{text-decoration:line-through}html.theme--documenter-dark .ansi span.sgr30{color:#242424}html.theme--documenter-dark .ansi span.sgr31{color:#f6705f}html.theme--documenter-dark .ansi span.sgr32{color:#4fb43a}html.theme--documenter-dark .ansi span.sgr33{color:#f4c72f}html.theme--documenter-dark .ansi span.sgr34{color:#7587f0}html.theme--documenter-dark .ansi span.sgr35{color:#bc89d3}html.theme--documenter-dark .ansi span.sgr36{color:#49b6ca}html.theme--documenter-dark .ansi span.sgr37{color:#b3bdbe}html.theme--documenter-dark .ansi span.sgr40{background-color:#242424}html.theme--documenter-dark .ansi span.sgr41{background-color:#f6705f}html.theme--documenter-dark .ansi span.sgr42{background-color:#4fb43a}html.theme--documenter-dark .ansi span.sgr43{background-color:#f4c72f}html.theme--documenter-dark .ansi span.sgr44{background-color:#7587f0}html.theme--documenter-dark .ansi span.sgr45{background-color:#bc89d3}html.theme--documenter-dark .ansi span.sgr46{background-color:#49b6ca}html.theme--documenter-dark .ansi span.sgr47{background-color:#b3bdbe}html.theme--documenter-dark .ansi span.sgr90{color:#92a0a2}html.theme--documenter-dark .ansi span.sgr91{color:#ff8674}html.theme--documenter-dark .ansi span.sgr92{color:#79d462}html.theme--documenter-dark .ansi span.sgr93{color:#ffe76b}html.theme--documenter-dark .ansi span.sgr94{color:#8a98ff}html.theme--documenter-dark .ansi span.sgr95{color:#d2a4e6}html.theme--documenter-dark .ansi span.sgr96{color:#6bc8db}html.theme--documenter-dark .ansi span.sgr97{color:#ecf0f1}html.theme--documenter-dark .ansi span.sgr100{background-color:#92a0a2}html.theme--documenter-dark .ansi span.sgr101{background-color:#ff8674}html.theme--documenter-dark .ansi span.sgr102{background-color:#79d462}html.theme--documenter-dark .ansi span.sgr103{background-color:#ffe76b}html.theme--documenter-dark .ansi span.sgr104{background-color:#8a98ff}html.theme--documenter-dark .ansi span.sgr105{background-color:#d2a4e6}html.theme--documenter-dark .ansi span.sgr106{background-color:#6bc8db}html.theme--documenter-dark .ansi span.sgr107{background-color:#ecf0f1}html.theme--documenter-dark code.language-julia-repl>span.hljs-meta{color:#4fb43a;font-weight:bolder}html.theme--documenter-dark .hljs{background:#2b2b2b;color:#f8f8f2}html.theme--documenter-dark .hljs-comment,html.theme--documenter-dark .hljs-quote{color:#d4d0ab}html.theme--documenter-dark .hljs-variable,html.theme--documenter-dark .hljs-template-variable,html.theme--documenter-dark .hljs-tag,html.theme--documenter-dark .hljs-name,html.theme--documenter-dark .hljs-selector-id,html.theme--documenter-dark .hljs-selector-class,html.theme--documenter-dark .hljs-regexp,html.theme--documenter-dark .hljs-deletion{color:#ffa07a}html.theme--documenter-dark .hljs-number,html.theme--documenter-dark .hljs-built_in,html.theme--documenter-dark .hljs-literal,html.theme--documenter-dark .hljs-type,html.theme--documenter-dark .hljs-params,html.theme--documenter-dark .hljs-meta,html.theme--documenter-dark .hljs-link{color:#f5ab35}html.theme--documenter-dark .hljs-attribute{color:#ffd700}html.theme--documenter-dark .hljs-string,html.theme--documenter-dark .hljs-symbol,html.theme--documenter-dark .hljs-bullet,html.theme--documenter-dark .hljs-addition{color:#abe338}html.theme--documenter-dark .hljs-title,html.theme--documenter-dark .hljs-section{color:#00e0e0}html.theme--documenter-dark .hljs-keyword,html.theme--documenter-dark .hljs-selector-tag{color:#dcc6e0}html.theme--documenter-dark .hljs-emphasis{font-style:italic}html.theme--documenter-dark .hljs-strong{font-weight:bold}@media screen and (-ms-high-contrast: active){html.theme--documenter-dark .hljs-addition,html.theme--documenter-dark .hljs-attribute,html.theme--documenter-dark .hljs-built_in,html.theme--documenter-dark .hljs-bullet,html.theme--documenter-dark .hljs-comment,html.theme--documenter-dark .hljs-link,html.theme--documenter-dark .hljs-literal,html.theme--documenter-dark .hljs-meta,html.theme--documenter-dark .hljs-number,html.theme--documenter-dark .hljs-params,html.theme--documenter-dark .hljs-string,html.theme--documenter-dark .hljs-symbol,html.theme--documenter-dark .hljs-type,html.theme--documenter-dark .hljs-quote{color:highlight}html.theme--documenter-dark .hljs-keyword,html.theme--documenter-dark .hljs-selector-tag{font-weight:bold}}html.theme--documenter-dark .hljs-subst{color:#f8f8f2}html.theme--documenter-dark .search-result-link{border-radius:0.7em;transition:all 300ms}html.theme--documenter-dark .search-result-link:hover,html.theme--documenter-dark .search-result-link:focus{background-color:rgba(0,128,128,0.1)}html.theme--documenter-dark .search-result-link .property-search-result-badge,html.theme--documenter-dark .search-result-link .search-filter{transition:all 300ms}html.theme--documenter-dark .search-result-link:hover .property-search-result-badge,html.theme--documenter-dark .search-result-link:hover .search-filter,html.theme--documenter-dark .search-result-link:focus .property-search-result-badge,html.theme--documenter-dark .search-result-link:focus .search-filter{color:#333 !important;background-color:#f1f5f9 !important}html.theme--documenter-dark .search-result-title{color:whitesmoke}html.theme--documenter-dark .search-result-highlight{background-color:greenyellow;color:black}html.theme--documenter-dark .search-divider{border-bottom:1px solid #5e6d6f50}html.theme--documenter-dark .w-100{width:100%}html.theme--documenter-dark .gap-2{gap:0.5rem}html.theme--documenter-dark .gap-4{gap:1rem} diff --git a/v1.19.1/assets/themes/documenter-light.css b/v1.19.1/assets/themes/documenter-light.css new file mode 100644 index 000000000..2f168c77b --- /dev/null +++ b/v1.19.1/assets/themes/documenter-light.css @@ -0,0 +1,9 @@ +.pagination-previous,.pagination-next,.pagination-link,.pagination-ellipsis,.file-cta,.file-name,.select select,.textarea,.input,#documenter .docs-sidebar form.docs-search>input,.button{-moz-appearance:none;-webkit-appearance:none;align-items:center;border:1px solid transparent;border-radius:4px;box-shadow:none;display:inline-flex;font-size:1rem;height:2.5em;justify-content:flex-start;line-height:1.5;padding-bottom:calc(0.5em - 1px);padding-left:calc(0.75em - 1px);padding-right:calc(0.75em - 1px);padding-top:calc(0.5em - 1px);position:relative;vertical-align:top}.pagination-previous:focus,.pagination-next:focus,.pagination-link:focus,.pagination-ellipsis:focus,.file-cta:focus,.file-name:focus,.select select:focus,.textarea:focus,.input:focus,#documenter .docs-sidebar form.docs-search>input:focus,.button:focus,.is-focused.pagination-previous,.is-focused.pagination-next,.is-focused.pagination-link,.is-focused.pagination-ellipsis,.is-focused.file-cta,.is-focused.file-name,.select select.is-focused,.is-focused.textarea,.is-focused.input,#documenter .docs-sidebar form.docs-search>input.is-focused,.is-focused.button,.pagination-previous:active,.pagination-next:active,.pagination-link:active,.pagination-ellipsis:active,.file-cta:active,.file-name:active,.select select:active,.textarea:active,.input:active,#documenter .docs-sidebar form.docs-search>input:active,.button:active,.is-active.pagination-previous,.is-active.pagination-next,.is-active.pagination-link,.is-active.pagination-ellipsis,.is-active.file-cta,.is-active.file-name,.select select.is-active,.is-active.textarea,.is-active.input,#documenter .docs-sidebar form.docs-search>input.is-active,.is-active.button{outline:none}.pagination-previous[disabled],.pagination-next[disabled],.pagination-link[disabled],.pagination-ellipsis[disabled],.file-cta[disabled],.file-name[disabled],.select select[disabled],.textarea[disabled],.input[disabled],#documenter .docs-sidebar form.docs-search>input[disabled],.button[disabled],fieldset[disabled] .pagination-previous,fieldset[disabled] .pagination-next,fieldset[disabled] .pagination-link,fieldset[disabled] .pagination-ellipsis,fieldset[disabled] .file-cta,fieldset[disabled] .file-name,fieldset[disabled] .select select,.select fieldset[disabled] select,fieldset[disabled] .textarea,fieldset[disabled] .input,fieldset[disabled] #documenter .docs-sidebar form.docs-search>input,#documenter .docs-sidebar fieldset[disabled] form.docs-search>input,fieldset[disabled] .button{cursor:not-allowed}.tabs,.pagination-previous,.pagination-next,.pagination-link,.pagination-ellipsis,.breadcrumb,.file,.button,.is-unselectable{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.navbar-link:not(.is-arrowless)::after,.select:not(.is-multiple):not(.is-loading)::after{border:3px solid rgba(0,0,0,0);border-radius:2px;border-right:0;border-top:0;content:" ";display:block;height:0.625em;margin-top:-0.4375em;pointer-events:none;position:absolute;top:50%;transform:rotate(-45deg);transform-origin:center;width:0.625em}.admonition:not(:last-child),.tabs:not(:last-child),.pagination:not(:last-child),.message:not(:last-child),.level:not(:last-child),.breadcrumb:not(:last-child),.block:not(:last-child),.title:not(:last-child),.subtitle:not(:last-child),.table-container:not(:last-child),.table:not(:last-child),.progress:not(:last-child),.notification:not(:last-child),.content:not(:last-child),.box:not(:last-child){margin-bottom:1.5rem}.modal-close,.delete{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-moz-appearance:none;-webkit-appearance:none;background-color:rgba(10,10,10,0.2);border:none;border-radius:9999px;cursor:pointer;pointer-events:auto;display:inline-block;flex-grow:0;flex-shrink:0;font-size:0;height:20px;max-height:20px;max-width:20px;min-height:20px;min-width:20px;outline:none;position:relative;vertical-align:top;width:20px}.modal-close::before,.delete::before,.modal-close::after,.delete::after{background-color:#fff;content:"";display:block;left:50%;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%) rotate(45deg);transform-origin:center center}.modal-close::before,.delete::before{height:2px;width:50%}.modal-close::after,.delete::after{height:50%;width:2px}.modal-close:hover,.delete:hover,.modal-close:focus,.delete:focus{background-color:rgba(10,10,10,0.3)}.modal-close:active,.delete:active{background-color:rgba(10,10,10,0.4)}.is-small.modal-close,#documenter .docs-sidebar form.docs-search>input.modal-close,.is-small.delete,#documenter .docs-sidebar form.docs-search>input.delete{height:16px;max-height:16px;max-width:16px;min-height:16px;min-width:16px;width:16px}.is-medium.modal-close,.is-medium.delete{height:24px;max-height:24px;max-width:24px;min-height:24px;min-width:24px;width:24px}.is-large.modal-close,.is-large.delete{height:32px;max-height:32px;max-width:32px;min-height:32px;min-width:32px;width:32px}.control.is-loading::after,.select.is-loading::after,.loader,.button.is-loading::after{animation:spinAround 500ms infinite linear;border:2px solid #dbdbdb;border-radius:9999px;border-right-color:transparent;border-top-color:transparent;content:"";display:block;height:1em;position:relative;width:1em}.hero-video,.modal-background,.modal,.image.is-square img,#documenter .docs-sidebar .docs-logo>img.is-square img,.image.is-square .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-square .has-ratio,.image.is-1by1 img,#documenter .docs-sidebar .docs-logo>img.is-1by1 img,.image.is-1by1 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-1by1 .has-ratio,.image.is-5by4 img,#documenter .docs-sidebar .docs-logo>img.is-5by4 img,.image.is-5by4 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-5by4 .has-ratio,.image.is-4by3 img,#documenter .docs-sidebar .docs-logo>img.is-4by3 img,.image.is-4by3 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-4by3 .has-ratio,.image.is-3by2 img,#documenter .docs-sidebar .docs-logo>img.is-3by2 img,.image.is-3by2 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-3by2 .has-ratio,.image.is-5by3 img,#documenter .docs-sidebar .docs-logo>img.is-5by3 img,.image.is-5by3 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-5by3 .has-ratio,.image.is-16by9 img,#documenter .docs-sidebar .docs-logo>img.is-16by9 img,.image.is-16by9 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-16by9 .has-ratio,.image.is-2by1 img,#documenter .docs-sidebar .docs-logo>img.is-2by1 img,.image.is-2by1 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-2by1 .has-ratio,.image.is-3by1 img,#documenter .docs-sidebar .docs-logo>img.is-3by1 img,.image.is-3by1 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-3by1 .has-ratio,.image.is-4by5 img,#documenter .docs-sidebar .docs-logo>img.is-4by5 img,.image.is-4by5 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-4by5 .has-ratio,.image.is-3by4 img,#documenter .docs-sidebar .docs-logo>img.is-3by4 img,.image.is-3by4 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-3by4 .has-ratio,.image.is-2by3 img,#documenter .docs-sidebar .docs-logo>img.is-2by3 img,.image.is-2by3 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-2by3 .has-ratio,.image.is-3by5 img,#documenter .docs-sidebar .docs-logo>img.is-3by5 img,.image.is-3by5 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-3by5 .has-ratio,.image.is-9by16 img,#documenter .docs-sidebar .docs-logo>img.is-9by16 img,.image.is-9by16 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-9by16 .has-ratio,.image.is-1by2 img,#documenter .docs-sidebar .docs-logo>img.is-1by2 img,.image.is-1by2 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-1by2 .has-ratio,.image.is-1by3 img,#documenter .docs-sidebar .docs-logo>img.is-1by3 img,.image.is-1by3 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-1by3 .has-ratio,.is-overlay{bottom:0;left:0;position:absolute;right:0;top:0}.navbar-burger{-moz-appearance:none;-webkit-appearance:none;appearance:none;background:none;border:none;color:currentColor;font-family:inherit;font-size:1em;margin:0;padding:0}.has-text-white{color:#fff !important}a.has-text-white:hover,a.has-text-white:focus{color:#e6e6e6 !important}.has-background-white{background-color:#fff !important}.has-text-black{color:#0a0a0a !important}a.has-text-black:hover,a.has-text-black:focus{color:#000 !important}.has-background-black{background-color:#0a0a0a !important}.has-text-light{color:#f5f5f5 !important}a.has-text-light:hover,a.has-text-light:focus{color:#dbdbdb !important}.has-background-light{background-color:#f5f5f5 !important}.has-text-dark{color:#363636 !important}a.has-text-dark:hover,a.has-text-dark:focus{color:#1c1c1c !important}.has-background-dark{background-color:#363636 !important}.has-text-primary{color:#4eb5de !important}a.has-text-primary:hover,a.has-text-primary:focus{color:#27a1d2 !important}.has-background-primary{background-color:#4eb5de !important}.has-text-primary-light{color:#eef8fc !important}a.has-text-primary-light:hover,a.has-text-primary-light:focus{color:#c3e6f4 !important}.has-background-primary-light{background-color:#eef8fc !important}.has-text-primary-dark{color:#1a6d8e !important}a.has-text-primary-dark:hover,a.has-text-primary-dark:focus{color:#228eb9 !important}.has-background-primary-dark{background-color:#1a6d8e !important}.has-text-link{color:#2e63b8 !important}a.has-text-link:hover,a.has-text-link:focus{color:#244d8f !important}.has-background-link{background-color:#2e63b8 !important}.has-text-link-light{color:#eff3fb !important}a.has-text-link-light:hover,a.has-text-link-light:focus{color:#c6d6f1 !important}.has-background-link-light{background-color:#eff3fb !important}.has-text-link-dark{color:#3169c4 !important}a.has-text-link-dark:hover,a.has-text-link-dark:focus{color:#5485d4 !important}.has-background-link-dark{background-color:#3169c4 !important}.has-text-info{color:#209cee !important}a.has-text-info:hover,a.has-text-info:focus{color:#1081cb !important}.has-background-info{background-color:#209cee !important}.has-text-info-light{color:#ecf7fe !important}a.has-text-info-light:hover,a.has-text-info-light:focus{color:#bde2fa !important}.has-background-info-light{background-color:#ecf7fe !important}.has-text-info-dark{color:#0e72b4 !important}a.has-text-info-dark:hover,a.has-text-info-dark:focus{color:#1190e3 !important}.has-background-info-dark{background-color:#0e72b4 !important}.has-text-success{color:#22c35b !important}a.has-text-success:hover,a.has-text-success:focus{color:#1a9847 !important}.has-background-success{background-color:#22c35b !important}.has-text-success-light{color:#eefcf3 !important}a.has-text-success-light:hover,a.has-text-success-light:focus{color:#c2f4d4 !important}.has-background-success-light{background-color:#eefcf3 !important}.has-text-success-dark{color:#198f43 !important}a.has-text-success-dark:hover,a.has-text-success-dark:focus{color:#21bb57 !important}.has-background-success-dark{background-color:#198f43 !important}.has-text-warning{color:#ffdd57 !important}a.has-text-warning:hover,a.has-text-warning:focus{color:#ffd324 !important}.has-background-warning{background-color:#ffdd57 !important}.has-text-warning-light{color:#fffbeb !important}a.has-text-warning-light:hover,a.has-text-warning-light:focus{color:#fff1b8 !important}.has-background-warning-light{background-color:#fffbeb !important}.has-text-warning-dark{color:#947600 !important}a.has-text-warning-dark:hover,a.has-text-warning-dark:focus{color:#c79f00 !important}.has-background-warning-dark{background-color:#947600 !important}.has-text-danger{color:#da0b00 !important}a.has-text-danger:hover,a.has-text-danger:focus{color:#a70800 !important}.has-background-danger{background-color:#da0b00 !important}.has-text-danger-light{color:#ffeceb !important}a.has-text-danger-light:hover,a.has-text-danger-light:focus{color:#ffbbb8 !important}.has-background-danger-light{background-color:#ffeceb !important}.has-text-danger-dark{color:#f50c00 !important}a.has-text-danger-dark:hover,a.has-text-danger-dark:focus{color:#ff3429 !important}.has-background-danger-dark{background-color:#f50c00 !important}.has-text-black-bis{color:#121212 !important}.has-background-black-bis{background-color:#121212 !important}.has-text-black-ter{color:#242424 !important}.has-background-black-ter{background-color:#242424 !important}.has-text-grey-darker{color:#363636 !important}.has-background-grey-darker{background-color:#363636 !important}.has-text-grey-dark{color:#4a4a4a !important}.has-background-grey-dark{background-color:#4a4a4a !important}.has-text-grey{color:#6b6b6b !important}.has-background-grey{background-color:#6b6b6b !important}.has-text-grey-light{color:#b5b5b5 !important}.has-background-grey-light{background-color:#b5b5b5 !important}.has-text-grey-lighter{color:#dbdbdb !important}.has-background-grey-lighter{background-color:#dbdbdb !important}.has-text-white-ter{color:#f5f5f5 !important}.has-background-white-ter{background-color:#f5f5f5 !important}.has-text-white-bis{color:#fafafa !important}.has-background-white-bis{background-color:#fafafa !important}.is-flex-direction-row{flex-direction:row !important}.is-flex-direction-row-reverse{flex-direction:row-reverse !important}.is-flex-direction-column{flex-direction:column !important}.is-flex-direction-column-reverse{flex-direction:column-reverse !important}.is-flex-wrap-nowrap{flex-wrap:nowrap !important}.is-flex-wrap-wrap{flex-wrap:wrap !important}.is-flex-wrap-wrap-reverse{flex-wrap:wrap-reverse !important}.is-justify-content-flex-start{justify-content:flex-start !important}.is-justify-content-flex-end{justify-content:flex-end !important}.is-justify-content-center{justify-content:center !important}.is-justify-content-space-between{justify-content:space-between !important}.is-justify-content-space-around{justify-content:space-around !important}.is-justify-content-space-evenly{justify-content:space-evenly !important}.is-justify-content-start{justify-content:start !important}.is-justify-content-end{justify-content:end !important}.is-justify-content-left{justify-content:left !important}.is-justify-content-right{justify-content:right !important}.is-align-content-flex-start{align-content:flex-start !important}.is-align-content-flex-end{align-content:flex-end !important}.is-align-content-center{align-content:center !important}.is-align-content-space-between{align-content:space-between !important}.is-align-content-space-around{align-content:space-around !important}.is-align-content-space-evenly{align-content:space-evenly !important}.is-align-content-stretch{align-content:stretch !important}.is-align-content-start{align-content:start !important}.is-align-content-end{align-content:end !important}.is-align-content-baseline{align-content:baseline !important}.is-align-items-stretch{align-items:stretch !important}.is-align-items-flex-start{align-items:flex-start !important}.is-align-items-flex-end{align-items:flex-end !important}.is-align-items-center{align-items:center !important}.is-align-items-baseline{align-items:baseline !important}.is-align-items-start{align-items:start !important}.is-align-items-end{align-items:end !important}.is-align-items-self-start{align-items:self-start !important}.is-align-items-self-end{align-items:self-end !important}.is-align-self-auto{align-self:auto !important}.is-align-self-flex-start{align-self:flex-start !important}.is-align-self-flex-end{align-self:flex-end !important}.is-align-self-center{align-self:center !important}.is-align-self-baseline{align-self:baseline !important}.is-align-self-stretch{align-self:stretch !important}.is-flex-grow-0{flex-grow:0 !important}.is-flex-grow-1{flex-grow:1 !important}.is-flex-grow-2{flex-grow:2 !important}.is-flex-grow-3{flex-grow:3 !important}.is-flex-grow-4{flex-grow:4 !important}.is-flex-grow-5{flex-grow:5 !important}.is-flex-shrink-0{flex-shrink:0 !important}.is-flex-shrink-1{flex-shrink:1 !important}.is-flex-shrink-2{flex-shrink:2 !important}.is-flex-shrink-3{flex-shrink:3 !important}.is-flex-shrink-4{flex-shrink:4 !important}.is-flex-shrink-5{flex-shrink:5 !important}.is-clearfix::after{clear:both;content:" ";display:table}.is-pulled-left{float:left !important}.is-pulled-right{float:right !important}.is-radiusless{border-radius:0 !important}.is-shadowless{box-shadow:none !important}.is-clickable{cursor:pointer !important;pointer-events:all !important}.is-clipped{overflow:hidden !important}.is-relative{position:relative !important}.is-marginless{margin:0 !important}.is-paddingless{padding:0 !important}.m-0{margin:0 !important}.mt-0{margin-top:0 !important}.mr-0{margin-right:0 !important}.mb-0{margin-bottom:0 !important}.ml-0{margin-left:0 !important}.mx-0{margin-left:0 !important;margin-right:0 !important}.my-0{margin-top:0 !important;margin-bottom:0 !important}.m-1{margin:.25rem !important}.mt-1{margin-top:.25rem !important}.mr-1{margin-right:.25rem !important}.mb-1{margin-bottom:.25rem !important}.ml-1{margin-left:.25rem !important}.mx-1{margin-left:.25rem !important;margin-right:.25rem !important}.my-1{margin-top:.25rem !important;margin-bottom:.25rem !important}.m-2{margin:.5rem !important}.mt-2{margin-top:.5rem !important}.mr-2{margin-right:.5rem !important}.mb-2{margin-bottom:.5rem !important}.ml-2{margin-left:.5rem !important}.mx-2{margin-left:.5rem !important;margin-right:.5rem !important}.my-2{margin-top:.5rem !important;margin-bottom:.5rem !important}.m-3{margin:.75rem !important}.mt-3{margin-top:.75rem !important}.mr-3{margin-right:.75rem !important}.mb-3{margin-bottom:.75rem !important}.ml-3{margin-left:.75rem !important}.mx-3{margin-left:.75rem !important;margin-right:.75rem !important}.my-3{margin-top:.75rem !important;margin-bottom:.75rem !important}.m-4{margin:1rem !important}.mt-4{margin-top:1rem !important}.mr-4{margin-right:1rem !important}.mb-4{margin-bottom:1rem !important}.ml-4{margin-left:1rem !important}.mx-4{margin-left:1rem !important;margin-right:1rem !important}.my-4{margin-top:1rem !important;margin-bottom:1rem !important}.m-5{margin:1.5rem !important}.mt-5{margin-top:1.5rem !important}.mr-5{margin-right:1.5rem !important}.mb-5{margin-bottom:1.5rem !important}.ml-5{margin-left:1.5rem !important}.mx-5{margin-left:1.5rem !important;margin-right:1.5rem !important}.my-5{margin-top:1.5rem !important;margin-bottom:1.5rem !important}.m-6{margin:3rem !important}.mt-6{margin-top:3rem !important}.mr-6{margin-right:3rem !important}.mb-6{margin-bottom:3rem !important}.ml-6{margin-left:3rem !important}.mx-6{margin-left:3rem !important;margin-right:3rem !important}.my-6{margin-top:3rem !important;margin-bottom:3rem !important}.m-auto{margin:auto !important}.mt-auto{margin-top:auto !important}.mr-auto{margin-right:auto !important}.mb-auto{margin-bottom:auto !important}.ml-auto{margin-left:auto !important}.mx-auto{margin-left:auto !important;margin-right:auto !important}.my-auto{margin-top:auto !important;margin-bottom:auto !important}.p-0{padding:0 !important}.pt-0{padding-top:0 !important}.pr-0{padding-right:0 !important}.pb-0{padding-bottom:0 !important}.pl-0{padding-left:0 !important}.px-0{padding-left:0 !important;padding-right:0 !important}.py-0{padding-top:0 !important;padding-bottom:0 !important}.p-1{padding:.25rem !important}.pt-1{padding-top:.25rem !important}.pr-1{padding-right:.25rem !important}.pb-1{padding-bottom:.25rem !important}.pl-1{padding-left:.25rem !important}.px-1{padding-left:.25rem !important;padding-right:.25rem !important}.py-1{padding-top:.25rem !important;padding-bottom:.25rem !important}.p-2{padding:.5rem !important}.pt-2{padding-top:.5rem !important}.pr-2{padding-right:.5rem !important}.pb-2{padding-bottom:.5rem !important}.pl-2{padding-left:.5rem !important}.px-2{padding-left:.5rem !important;padding-right:.5rem !important}.py-2{padding-top:.5rem !important;padding-bottom:.5rem !important}.p-3{padding:.75rem !important}.pt-3{padding-top:.75rem !important}.pr-3{padding-right:.75rem !important}.pb-3{padding-bottom:.75rem !important}.pl-3{padding-left:.75rem !important}.px-3{padding-left:.75rem !important;padding-right:.75rem !important}.py-3{padding-top:.75rem !important;padding-bottom:.75rem !important}.p-4{padding:1rem !important}.pt-4{padding-top:1rem !important}.pr-4{padding-right:1rem !important}.pb-4{padding-bottom:1rem !important}.pl-4{padding-left:1rem !important}.px-4{padding-left:1rem !important;padding-right:1rem !important}.py-4{padding-top:1rem !important;padding-bottom:1rem !important}.p-5{padding:1.5rem !important}.pt-5{padding-top:1.5rem !important}.pr-5{padding-right:1.5rem !important}.pb-5{padding-bottom:1.5rem !important}.pl-5{padding-left:1.5rem !important}.px-5{padding-left:1.5rem !important;padding-right:1.5rem !important}.py-5{padding-top:1.5rem !important;padding-bottom:1.5rem !important}.p-6{padding:3rem !important}.pt-6{padding-top:3rem !important}.pr-6{padding-right:3rem !important}.pb-6{padding-bottom:3rem !important}.pl-6{padding-left:3rem !important}.px-6{padding-left:3rem !important;padding-right:3rem !important}.py-6{padding-top:3rem !important;padding-bottom:3rem !important}.p-auto{padding:auto !important}.pt-auto{padding-top:auto !important}.pr-auto{padding-right:auto !important}.pb-auto{padding-bottom:auto !important}.pl-auto{padding-left:auto !important}.px-auto{padding-left:auto !important;padding-right:auto !important}.py-auto{padding-top:auto !important;padding-bottom:auto !important}.is-size-1{font-size:3rem !important}.is-size-2{font-size:2.5rem !important}.is-size-3{font-size:2rem !important}.is-size-4{font-size:1.5rem !important}.is-size-5{font-size:1.25rem !important}.is-size-6{font-size:1rem !important}.is-size-7,.docstring>section>a.docs-sourcelink{font-size:.75rem !important}@media screen and (max-width: 768px){.is-size-1-mobile{font-size:3rem !important}.is-size-2-mobile{font-size:2.5rem !important}.is-size-3-mobile{font-size:2rem !important}.is-size-4-mobile{font-size:1.5rem !important}.is-size-5-mobile{font-size:1.25rem !important}.is-size-6-mobile{font-size:1rem !important}.is-size-7-mobile{font-size:.75rem !important}}@media screen and (min-width: 769px),print{.is-size-1-tablet{font-size:3rem !important}.is-size-2-tablet{font-size:2.5rem !important}.is-size-3-tablet{font-size:2rem !important}.is-size-4-tablet{font-size:1.5rem !important}.is-size-5-tablet{font-size:1.25rem !important}.is-size-6-tablet{font-size:1rem !important}.is-size-7-tablet{font-size:.75rem !important}}@media screen and (max-width: 1055px){.is-size-1-touch{font-size:3rem !important}.is-size-2-touch{font-size:2.5rem !important}.is-size-3-touch{font-size:2rem !important}.is-size-4-touch{font-size:1.5rem !important}.is-size-5-touch{font-size:1.25rem !important}.is-size-6-touch{font-size:1rem !important}.is-size-7-touch{font-size:.75rem !important}}@media screen and (min-width: 1056px){.is-size-1-desktop{font-size:3rem !important}.is-size-2-desktop{font-size:2.5rem !important}.is-size-3-desktop{font-size:2rem !important}.is-size-4-desktop{font-size:1.5rem !important}.is-size-5-desktop{font-size:1.25rem !important}.is-size-6-desktop{font-size:1rem !important}.is-size-7-desktop{font-size:.75rem !important}}@media screen and (min-width: 1216px){.is-size-1-widescreen{font-size:3rem !important}.is-size-2-widescreen{font-size:2.5rem !important}.is-size-3-widescreen{font-size:2rem !important}.is-size-4-widescreen{font-size:1.5rem !important}.is-size-5-widescreen{font-size:1.25rem !important}.is-size-6-widescreen{font-size:1rem !important}.is-size-7-widescreen{font-size:.75rem !important}}@media screen and (min-width: 1408px){.is-size-1-fullhd{font-size:3rem !important}.is-size-2-fullhd{font-size:2.5rem !important}.is-size-3-fullhd{font-size:2rem !important}.is-size-4-fullhd{font-size:1.5rem !important}.is-size-5-fullhd{font-size:1.25rem !important}.is-size-6-fullhd{font-size:1rem !important}.is-size-7-fullhd{font-size:.75rem !important}}.has-text-centered{text-align:center !important}.has-text-justified{text-align:justify !important}.has-text-left{text-align:left !important}.has-text-right{text-align:right !important}@media screen and (max-width: 768px){.has-text-centered-mobile{text-align:center !important}}@media screen and (min-width: 769px),print{.has-text-centered-tablet{text-align:center !important}}@media screen and (min-width: 769px) and (max-width: 1055px){.has-text-centered-tablet-only{text-align:center !important}}@media screen and (max-width: 1055px){.has-text-centered-touch{text-align:center !important}}@media screen and (min-width: 1056px){.has-text-centered-desktop{text-align:center !important}}@media screen and (min-width: 1056px) and (max-width: 1215px){.has-text-centered-desktop-only{text-align:center !important}}@media screen and (min-width: 1216px){.has-text-centered-widescreen{text-align:center !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.has-text-centered-widescreen-only{text-align:center !important}}@media screen and (min-width: 1408px){.has-text-centered-fullhd{text-align:center !important}}@media screen and (max-width: 768px){.has-text-justified-mobile{text-align:justify !important}}@media screen and (min-width: 769px),print{.has-text-justified-tablet{text-align:justify !important}}@media screen and (min-width: 769px) and (max-width: 1055px){.has-text-justified-tablet-only{text-align:justify !important}}@media screen and (max-width: 1055px){.has-text-justified-touch{text-align:justify !important}}@media screen and (min-width: 1056px){.has-text-justified-desktop{text-align:justify !important}}@media screen and (min-width: 1056px) and (max-width: 1215px){.has-text-justified-desktop-only{text-align:justify !important}}@media screen and (min-width: 1216px){.has-text-justified-widescreen{text-align:justify !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.has-text-justified-widescreen-only{text-align:justify !important}}@media screen and (min-width: 1408px){.has-text-justified-fullhd{text-align:justify !important}}@media screen and (max-width: 768px){.has-text-left-mobile{text-align:left !important}}@media screen and (min-width: 769px),print{.has-text-left-tablet{text-align:left !important}}@media screen and (min-width: 769px) and (max-width: 1055px){.has-text-left-tablet-only{text-align:left !important}}@media screen and (max-width: 1055px){.has-text-left-touch{text-align:left !important}}@media screen and (min-width: 1056px){.has-text-left-desktop{text-align:left !important}}@media screen and (min-width: 1056px) and (max-width: 1215px){.has-text-left-desktop-only{text-align:left !important}}@media screen and (min-width: 1216px){.has-text-left-widescreen{text-align:left !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.has-text-left-widescreen-only{text-align:left !important}}@media screen and (min-width: 1408px){.has-text-left-fullhd{text-align:left !important}}@media screen and (max-width: 768px){.has-text-right-mobile{text-align:right !important}}@media screen and (min-width: 769px),print{.has-text-right-tablet{text-align:right !important}}@media screen and (min-width: 769px) and (max-width: 1055px){.has-text-right-tablet-only{text-align:right !important}}@media screen and (max-width: 1055px){.has-text-right-touch{text-align:right !important}}@media screen and (min-width: 1056px){.has-text-right-desktop{text-align:right !important}}@media screen and (min-width: 1056px) and (max-width: 1215px){.has-text-right-desktop-only{text-align:right !important}}@media screen and (min-width: 1216px){.has-text-right-widescreen{text-align:right !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.has-text-right-widescreen-only{text-align:right !important}}@media screen and (min-width: 1408px){.has-text-right-fullhd{text-align:right !important}}.is-capitalized{text-transform:capitalize !important}.is-lowercase{text-transform:lowercase !important}.is-uppercase{text-transform:uppercase !important}.is-italic{font-style:italic !important}.is-underlined{text-decoration:underline !important}.has-text-weight-light{font-weight:300 !important}.has-text-weight-normal{font-weight:400 !important}.has-text-weight-medium{font-weight:500 !important}.has-text-weight-semibold{font-weight:600 !important}.has-text-weight-bold{font-weight:700 !important}.is-family-primary{font-family:"Lato Medium",-apple-system,BlinkMacSystemFont,"Segoe UI","Helvetica Neue","Helvetica","Arial",sans-serif !important}.is-family-secondary{font-family:"Lato Medium",-apple-system,BlinkMacSystemFont,"Segoe UI","Helvetica Neue","Helvetica","Arial",sans-serif !important}.is-family-sans-serif{font-family:"Lato Medium",-apple-system,BlinkMacSystemFont,"Segoe UI","Helvetica Neue","Helvetica","Arial",sans-serif !important}.is-family-monospace{font-family:"JuliaMono","SFMono-Regular","Menlo","Consolas","Liberation Mono","DejaVu Sans Mono",monospace !important}.is-family-code{font-family:"JuliaMono","SFMono-Regular","Menlo","Consolas","Liberation Mono","DejaVu Sans Mono",monospace !important}.is-block{display:block !important}@media screen and (max-width: 768px){.is-block-mobile{display:block !important}}@media screen and (min-width: 769px),print{.is-block-tablet{display:block !important}}@media screen and (min-width: 769px) and (max-width: 1055px){.is-block-tablet-only{display:block !important}}@media screen and (max-width: 1055px){.is-block-touch{display:block !important}}@media screen and (min-width: 1056px){.is-block-desktop{display:block !important}}@media screen and (min-width: 1056px) and (max-width: 1215px){.is-block-desktop-only{display:block !important}}@media screen and (min-width: 1216px){.is-block-widescreen{display:block !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.is-block-widescreen-only{display:block !important}}@media screen and (min-width: 1408px){.is-block-fullhd{display:block !important}}.is-flex{display:flex !important}@media screen and (max-width: 768px){.is-flex-mobile{display:flex !important}}@media screen and (min-width: 769px),print{.is-flex-tablet{display:flex !important}}@media screen and (min-width: 769px) and (max-width: 1055px){.is-flex-tablet-only{display:flex !important}}@media screen and (max-width: 1055px){.is-flex-touch{display:flex !important}}@media screen and (min-width: 1056px){.is-flex-desktop{display:flex !important}}@media screen and (min-width: 1056px) and (max-width: 1215px){.is-flex-desktop-only{display:flex !important}}@media screen and (min-width: 1216px){.is-flex-widescreen{display:flex !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.is-flex-widescreen-only{display:flex !important}}@media screen and (min-width: 1408px){.is-flex-fullhd{display:flex !important}}.is-inline{display:inline !important}@media screen and (max-width: 768px){.is-inline-mobile{display:inline !important}}@media screen and (min-width: 769px),print{.is-inline-tablet{display:inline !important}}@media screen and (min-width: 769px) and (max-width: 1055px){.is-inline-tablet-only{display:inline !important}}@media screen and (max-width: 1055px){.is-inline-touch{display:inline !important}}@media screen and (min-width: 1056px){.is-inline-desktop{display:inline !important}}@media screen and (min-width: 1056px) and (max-width: 1215px){.is-inline-desktop-only{display:inline !important}}@media screen and (min-width: 1216px){.is-inline-widescreen{display:inline !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.is-inline-widescreen-only{display:inline !important}}@media screen and (min-width: 1408px){.is-inline-fullhd{display:inline !important}}.is-inline-block{display:inline-block !important}@media screen and (max-width: 768px){.is-inline-block-mobile{display:inline-block !important}}@media screen and (min-width: 769px),print{.is-inline-block-tablet{display:inline-block !important}}@media screen and (min-width: 769px) and (max-width: 1055px){.is-inline-block-tablet-only{display:inline-block !important}}@media screen and (max-width: 1055px){.is-inline-block-touch{display:inline-block !important}}@media screen and (min-width: 1056px){.is-inline-block-desktop{display:inline-block !important}}@media screen and (min-width: 1056px) and (max-width: 1215px){.is-inline-block-desktop-only{display:inline-block !important}}@media screen and (min-width: 1216px){.is-inline-block-widescreen{display:inline-block !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.is-inline-block-widescreen-only{display:inline-block !important}}@media screen and (min-width: 1408px){.is-inline-block-fullhd{display:inline-block !important}}.is-inline-flex{display:inline-flex !important}@media screen and (max-width: 768px){.is-inline-flex-mobile{display:inline-flex !important}}@media screen and (min-width: 769px),print{.is-inline-flex-tablet{display:inline-flex !important}}@media screen and (min-width: 769px) and (max-width: 1055px){.is-inline-flex-tablet-only{display:inline-flex !important}}@media screen and (max-width: 1055px){.is-inline-flex-touch{display:inline-flex !important}}@media screen and (min-width: 1056px){.is-inline-flex-desktop{display:inline-flex !important}}@media screen and (min-width: 1056px) and (max-width: 1215px){.is-inline-flex-desktop-only{display:inline-flex !important}}@media screen and (min-width: 1216px){.is-inline-flex-widescreen{display:inline-flex !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.is-inline-flex-widescreen-only{display:inline-flex !important}}@media screen and (min-width: 1408px){.is-inline-flex-fullhd{display:inline-flex !important}}.is-hidden{display:none !important}.is-sr-only{border:none !important;clip:rect(0, 0, 0, 0) !important;height:0.01em !important;overflow:hidden !important;padding:0 !important;position:absolute !important;white-space:nowrap !important;width:0.01em !important}@media screen and (max-width: 768px){.is-hidden-mobile{display:none !important}}@media screen and (min-width: 769px),print{.is-hidden-tablet{display:none !important}}@media screen and (min-width: 769px) and (max-width: 1055px){.is-hidden-tablet-only{display:none !important}}@media screen and (max-width: 1055px){.is-hidden-touch{display:none !important}}@media screen and (min-width: 1056px){.is-hidden-desktop{display:none !important}}@media screen and (min-width: 1056px) and (max-width: 1215px){.is-hidden-desktop-only{display:none !important}}@media screen and (min-width: 1216px){.is-hidden-widescreen{display:none !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.is-hidden-widescreen-only{display:none !important}}@media screen and (min-width: 1408px){.is-hidden-fullhd{display:none !important}}.is-invisible{visibility:hidden !important}@media screen and (max-width: 768px){.is-invisible-mobile{visibility:hidden !important}}@media screen and (min-width: 769px),print{.is-invisible-tablet{visibility:hidden !important}}@media screen and (min-width: 769px) and (max-width: 1055px){.is-invisible-tablet-only{visibility:hidden !important}}@media screen and (max-width: 1055px){.is-invisible-touch{visibility:hidden !important}}@media screen and (min-width: 1056px){.is-invisible-desktop{visibility:hidden !important}}@media screen and (min-width: 1056px) and (max-width: 1215px){.is-invisible-desktop-only{visibility:hidden !important}}@media screen and (min-width: 1216px){.is-invisible-widescreen{visibility:hidden !important}}@media screen and (min-width: 1216px) and (max-width: 1407px){.is-invisible-widescreen-only{visibility:hidden !important}}@media screen and (min-width: 1408px){.is-invisible-fullhd{visibility:hidden !important}}/*! minireset.css v0.0.6 | MIT License | github.com/jgthms/minireset.css */html,body,p,ol,ul,li,dl,dt,dd,blockquote,figure,fieldset,legend,textarea,pre,iframe,hr,h1,h2,h3,h4,h5,h6{margin:0;padding:0}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal}ul{list-style:none}button,input,select,textarea{margin:0}html{box-sizing:border-box}*,*::before,*::after{box-sizing:inherit}img,video{height:auto;max-width:100%}iframe{border:0}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}td:not([align]),th:not([align]){text-align:inherit}html{background-color:#fff;font-size:16px;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;min-width:300px;overflow-x:auto;overflow-y:scroll;text-rendering:optimizeLegibility;text-size-adjust:100%}article,aside,figure,footer,header,hgroup,section{display:block}body,button,input,optgroup,select,textarea{font-family:"Lato Medium",-apple-system,BlinkMacSystemFont,"Segoe UI","Helvetica Neue","Helvetica","Arial",sans-serif}code,pre{-moz-osx-font-smoothing:auto;-webkit-font-smoothing:auto;font-family:"JuliaMono","SFMono-Regular","Menlo","Consolas","Liberation Mono","DejaVu Sans Mono",monospace}body{color:#222;font-size:1em;font-weight:400;line-height:1.5}a{color:#2e63b8;cursor:pointer;text-decoration:none}a strong{color:currentColor}a:hover{color:#363636}code{background-color:rgba(0,0,0,0.05);color:#000;font-size:.875em;font-weight:normal;padding:.1em}hr{background-color:#f5f5f5;border:none;display:block;height:2px;margin:1.5rem 0}img{height:auto;max-width:100%}input[type="checkbox"],input[type="radio"]{vertical-align:baseline}small{font-size:.875em}span{font-style:inherit;font-weight:inherit}strong{color:#222;font-weight:700}fieldset{border:none}pre{-webkit-overflow-scrolling:touch;background-color:#f5f5f5;color:#222;font-size:.875em;overflow-x:auto;padding:1.25rem 1.5rem;white-space:pre;word-wrap:normal}pre code{background-color:transparent;color:currentColor;font-size:1em;padding:0}table td,table th{vertical-align:top}table td:not([align]),table th:not([align]){text-align:inherit}table th{color:#222}@keyframes spinAround{from{transform:rotate(0deg)}to{transform:rotate(359deg)}}.box{background-color:#fff;border-radius:6px;box-shadow:#bbb;color:#222;display:block;padding:1.25rem}a.box:hover,a.box:focus{box-shadow:0 0.5em 1em -0.125em rgba(10,10,10,0.1),0 0 0 1px #2e63b8}a.box:active{box-shadow:inset 0 1px 2px rgba(10,10,10,0.2),0 0 0 1px #2e63b8}.button{background-color:#fff;border-color:#dbdbdb;border-width:1px;color:#222;cursor:pointer;justify-content:center;padding-bottom:calc(0.5em - 1px);padding-left:1em;padding-right:1em;padding-top:calc(0.5em - 1px);text-align:center;white-space:nowrap}.button strong{color:inherit}.button .icon,.button .icon.is-small,.button #documenter .docs-sidebar form.docs-search>input.icon,#documenter .docs-sidebar .button form.docs-search>input.icon,.button .icon.is-medium,.button .icon.is-large{height:1.5em;width:1.5em}.button .icon:first-child:not(:last-child){margin-left:calc(-0.5em - 1px);margin-right:.25em}.button .icon:last-child:not(:first-child){margin-left:.25em;margin-right:calc(-0.5em - 1px)}.button .icon:first-child:last-child{margin-left:calc(-0.5em - 1px);margin-right:calc(-0.5em - 1px)}.button:hover,.button.is-hovered{border-color:#b5b5b5;color:#363636}.button:focus,.button.is-focused{border-color:#3c5dcd;color:#363636}.button:focus:not(:active),.button.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(46,99,184,0.25)}.button:active,.button.is-active{border-color:#4a4a4a;color:#363636}.button.is-text{background-color:transparent;border-color:transparent;color:#222;text-decoration:underline}.button.is-text:hover,.button.is-text.is-hovered,.button.is-text:focus,.button.is-text.is-focused{background-color:#f5f5f5;color:#222}.button.is-text:active,.button.is-text.is-active{background-color:#e8e8e8;color:#222}.button.is-text[disabled],fieldset[disabled] .button.is-text{background-color:transparent;border-color:transparent;box-shadow:none}.button.is-ghost{background:none;border-color:rgba(0,0,0,0);color:#2e63b8;text-decoration:none}.button.is-ghost:hover,.button.is-ghost.is-hovered{color:#2e63b8;text-decoration:underline}.button.is-white{background-color:#fff;border-color:transparent;color:#0a0a0a}.button.is-white:hover,.button.is-white.is-hovered{background-color:#f9f9f9;border-color:transparent;color:#0a0a0a}.button.is-white:focus,.button.is-white.is-focused{border-color:transparent;color:#0a0a0a}.button.is-white:focus:not(:active),.button.is-white.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(255,255,255,0.25)}.button.is-white:active,.button.is-white.is-active{background-color:#f2f2f2;border-color:transparent;color:#0a0a0a}.button.is-white[disabled],fieldset[disabled] .button.is-white{background-color:#fff;border-color:#fff;box-shadow:none}.button.is-white.is-inverted{background-color:#0a0a0a;color:#fff}.button.is-white.is-inverted:hover,.button.is-white.is-inverted.is-hovered{background-color:#000}.button.is-white.is-inverted[disabled],fieldset[disabled] .button.is-white.is-inverted{background-color:#0a0a0a;border-color:transparent;box-shadow:none;color:#fff}.button.is-white.is-loading::after{border-color:transparent transparent #0a0a0a #0a0a0a !important}.button.is-white.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-white.is-outlined:hover,.button.is-white.is-outlined.is-hovered,.button.is-white.is-outlined:focus,.button.is-white.is-outlined.is-focused{background-color:#fff;border-color:#fff;color:#0a0a0a}.button.is-white.is-outlined.is-loading::after{border-color:transparent transparent #fff #fff !important}.button.is-white.is-outlined.is-loading:hover::after,.button.is-white.is-outlined.is-loading.is-hovered::after,.button.is-white.is-outlined.is-loading:focus::after,.button.is-white.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #0a0a0a #0a0a0a !important}.button.is-white.is-outlined[disabled],fieldset[disabled] .button.is-white.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-white.is-inverted.is-outlined{background-color:transparent;border-color:#0a0a0a;color:#0a0a0a}.button.is-white.is-inverted.is-outlined:hover,.button.is-white.is-inverted.is-outlined.is-hovered,.button.is-white.is-inverted.is-outlined:focus,.button.is-white.is-inverted.is-outlined.is-focused{background-color:#0a0a0a;color:#fff}.button.is-white.is-inverted.is-outlined.is-loading:hover::after,.button.is-white.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-white.is-inverted.is-outlined.is-loading:focus::after,.button.is-white.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #fff #fff !important}.button.is-white.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-white.is-inverted.is-outlined{background-color:transparent;border-color:#0a0a0a;box-shadow:none;color:#0a0a0a}.button.is-black{background-color:#0a0a0a;border-color:transparent;color:#fff}.button.is-black:hover,.button.is-black.is-hovered{background-color:#040404;border-color:transparent;color:#fff}.button.is-black:focus,.button.is-black.is-focused{border-color:transparent;color:#fff}.button.is-black:focus:not(:active),.button.is-black.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(10,10,10,0.25)}.button.is-black:active,.button.is-black.is-active{background-color:#000;border-color:transparent;color:#fff}.button.is-black[disabled],fieldset[disabled] .button.is-black{background-color:#0a0a0a;border-color:#0a0a0a;box-shadow:none}.button.is-black.is-inverted{background-color:#fff;color:#0a0a0a}.button.is-black.is-inverted:hover,.button.is-black.is-inverted.is-hovered{background-color:#f2f2f2}.button.is-black.is-inverted[disabled],fieldset[disabled] .button.is-black.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#0a0a0a}.button.is-black.is-loading::after{border-color:transparent transparent #fff #fff !important}.button.is-black.is-outlined{background-color:transparent;border-color:#0a0a0a;color:#0a0a0a}.button.is-black.is-outlined:hover,.button.is-black.is-outlined.is-hovered,.button.is-black.is-outlined:focus,.button.is-black.is-outlined.is-focused{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.button.is-black.is-outlined.is-loading::after{border-color:transparent transparent #0a0a0a #0a0a0a !important}.button.is-black.is-outlined.is-loading:hover::after,.button.is-black.is-outlined.is-loading.is-hovered::after,.button.is-black.is-outlined.is-loading:focus::after,.button.is-black.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #fff #fff !important}.button.is-black.is-outlined[disabled],fieldset[disabled] .button.is-black.is-outlined{background-color:transparent;border-color:#0a0a0a;box-shadow:none;color:#0a0a0a}.button.is-black.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-black.is-inverted.is-outlined:hover,.button.is-black.is-inverted.is-outlined.is-hovered,.button.is-black.is-inverted.is-outlined:focus,.button.is-black.is-inverted.is-outlined.is-focused{background-color:#fff;color:#0a0a0a}.button.is-black.is-inverted.is-outlined.is-loading:hover::after,.button.is-black.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-black.is-inverted.is-outlined.is-loading:focus::after,.button.is-black.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #0a0a0a #0a0a0a !important}.button.is-black.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-black.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-light{background-color:#f5f5f5;border-color:transparent;color:rgba(0,0,0,0.7)}.button.is-light:hover,.button.is-light.is-hovered{background-color:#eee;border-color:transparent;color:rgba(0,0,0,0.7)}.button.is-light:focus,.button.is-light.is-focused{border-color:transparent;color:rgba(0,0,0,0.7)}.button.is-light:focus:not(:active),.button.is-light.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(245,245,245,0.25)}.button.is-light:active,.button.is-light.is-active{background-color:#e8e8e8;border-color:transparent;color:rgba(0,0,0,0.7)}.button.is-light[disabled],fieldset[disabled] .button.is-light{background-color:#f5f5f5;border-color:#f5f5f5;box-shadow:none}.button.is-light.is-inverted{background-color:rgba(0,0,0,0.7);color:#f5f5f5}.button.is-light.is-inverted:hover,.button.is-light.is-inverted.is-hovered{background-color:rgba(0,0,0,0.7)}.button.is-light.is-inverted[disabled],fieldset[disabled] .button.is-light.is-inverted{background-color:rgba(0,0,0,0.7);border-color:transparent;box-shadow:none;color:#f5f5f5}.button.is-light.is-loading::after{border-color:transparent transparent rgba(0,0,0,0.7) rgba(0,0,0,0.7) !important}.button.is-light.is-outlined{background-color:transparent;border-color:#f5f5f5;color:#f5f5f5}.button.is-light.is-outlined:hover,.button.is-light.is-outlined.is-hovered,.button.is-light.is-outlined:focus,.button.is-light.is-outlined.is-focused{background-color:#f5f5f5;border-color:#f5f5f5;color:rgba(0,0,0,0.7)}.button.is-light.is-outlined.is-loading::after{border-color:transparent transparent #f5f5f5 #f5f5f5 !important}.button.is-light.is-outlined.is-loading:hover::after,.button.is-light.is-outlined.is-loading.is-hovered::after,.button.is-light.is-outlined.is-loading:focus::after,.button.is-light.is-outlined.is-loading.is-focused::after{border-color:transparent transparent rgba(0,0,0,0.7) rgba(0,0,0,0.7) !important}.button.is-light.is-outlined[disabled],fieldset[disabled] .button.is-light.is-outlined{background-color:transparent;border-color:#f5f5f5;box-shadow:none;color:#f5f5f5}.button.is-light.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,0.7);color:rgba(0,0,0,0.7)}.button.is-light.is-inverted.is-outlined:hover,.button.is-light.is-inverted.is-outlined.is-hovered,.button.is-light.is-inverted.is-outlined:focus,.button.is-light.is-inverted.is-outlined.is-focused{background-color:rgba(0,0,0,0.7);color:#f5f5f5}.button.is-light.is-inverted.is-outlined.is-loading:hover::after,.button.is-light.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-light.is-inverted.is-outlined.is-loading:focus::after,.button.is-light.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #f5f5f5 #f5f5f5 !important}.button.is-light.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-light.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,0.7);box-shadow:none;color:rgba(0,0,0,0.7)}.button.is-dark,.content kbd.button{background-color:#363636;border-color:transparent;color:#fff}.button.is-dark:hover,.content kbd.button:hover,.button.is-dark.is-hovered,.content kbd.button.is-hovered{background-color:#2f2f2f;border-color:transparent;color:#fff}.button.is-dark:focus,.content kbd.button:focus,.button.is-dark.is-focused,.content kbd.button.is-focused{border-color:transparent;color:#fff}.button.is-dark:focus:not(:active),.content kbd.button:focus:not(:active),.button.is-dark.is-focused:not(:active),.content kbd.button.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(54,54,54,0.25)}.button.is-dark:active,.content kbd.button:active,.button.is-dark.is-active,.content kbd.button.is-active{background-color:#292929;border-color:transparent;color:#fff}.button.is-dark[disabled],.content kbd.button[disabled],fieldset[disabled] .button.is-dark,fieldset[disabled] .content kbd.button,.content fieldset[disabled] kbd.button{background-color:#363636;border-color:#363636;box-shadow:none}.button.is-dark.is-inverted,.content kbd.button.is-inverted{background-color:#fff;color:#363636}.button.is-dark.is-inverted:hover,.content kbd.button.is-inverted:hover,.button.is-dark.is-inverted.is-hovered,.content kbd.button.is-inverted.is-hovered{background-color:#f2f2f2}.button.is-dark.is-inverted[disabled],.content kbd.button.is-inverted[disabled],fieldset[disabled] .button.is-dark.is-inverted,fieldset[disabled] .content kbd.button.is-inverted,.content fieldset[disabled] kbd.button.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#363636}.button.is-dark.is-loading::after,.content kbd.button.is-loading::after{border-color:transparent transparent #fff #fff !important}.button.is-dark.is-outlined,.content kbd.button.is-outlined{background-color:transparent;border-color:#363636;color:#363636}.button.is-dark.is-outlined:hover,.content kbd.button.is-outlined:hover,.button.is-dark.is-outlined.is-hovered,.content kbd.button.is-outlined.is-hovered,.button.is-dark.is-outlined:focus,.content kbd.button.is-outlined:focus,.button.is-dark.is-outlined.is-focused,.content kbd.button.is-outlined.is-focused{background-color:#363636;border-color:#363636;color:#fff}.button.is-dark.is-outlined.is-loading::after,.content kbd.button.is-outlined.is-loading::after{border-color:transparent transparent #363636 #363636 !important}.button.is-dark.is-outlined.is-loading:hover::after,.content kbd.button.is-outlined.is-loading:hover::after,.button.is-dark.is-outlined.is-loading.is-hovered::after,.content kbd.button.is-outlined.is-loading.is-hovered::after,.button.is-dark.is-outlined.is-loading:focus::after,.content kbd.button.is-outlined.is-loading:focus::after,.button.is-dark.is-outlined.is-loading.is-focused::after,.content kbd.button.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #fff #fff !important}.button.is-dark.is-outlined[disabled],.content kbd.button.is-outlined[disabled],fieldset[disabled] .button.is-dark.is-outlined,fieldset[disabled] .content kbd.button.is-outlined,.content fieldset[disabled] kbd.button.is-outlined{background-color:transparent;border-color:#363636;box-shadow:none;color:#363636}.button.is-dark.is-inverted.is-outlined,.content kbd.button.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-dark.is-inverted.is-outlined:hover,.content kbd.button.is-inverted.is-outlined:hover,.button.is-dark.is-inverted.is-outlined.is-hovered,.content kbd.button.is-inverted.is-outlined.is-hovered,.button.is-dark.is-inverted.is-outlined:focus,.content kbd.button.is-inverted.is-outlined:focus,.button.is-dark.is-inverted.is-outlined.is-focused,.content kbd.button.is-inverted.is-outlined.is-focused{background-color:#fff;color:#363636}.button.is-dark.is-inverted.is-outlined.is-loading:hover::after,.content kbd.button.is-inverted.is-outlined.is-loading:hover::after,.button.is-dark.is-inverted.is-outlined.is-loading.is-hovered::after,.content kbd.button.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-dark.is-inverted.is-outlined.is-loading:focus::after,.content kbd.button.is-inverted.is-outlined.is-loading:focus::after,.button.is-dark.is-inverted.is-outlined.is-loading.is-focused::after,.content kbd.button.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #363636 #363636 !important}.button.is-dark.is-inverted.is-outlined[disabled],.content kbd.button.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-dark.is-inverted.is-outlined,fieldset[disabled] .content kbd.button.is-inverted.is-outlined,.content fieldset[disabled] kbd.button.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-primary,.docstring>section>a.button.docs-sourcelink{background-color:#4eb5de;border-color:transparent;color:#fff}.button.is-primary:hover,.docstring>section>a.button.docs-sourcelink:hover,.button.is-primary.is-hovered,.docstring>section>a.button.is-hovered.docs-sourcelink{background-color:#43b1dc;border-color:transparent;color:#fff}.button.is-primary:focus,.docstring>section>a.button.docs-sourcelink:focus,.button.is-primary.is-focused,.docstring>section>a.button.is-focused.docs-sourcelink{border-color:transparent;color:#fff}.button.is-primary:focus:not(:active),.docstring>section>a.button.docs-sourcelink:focus:not(:active),.button.is-primary.is-focused:not(:active),.docstring>section>a.button.is-focused.docs-sourcelink:not(:active){box-shadow:0 0 0 0.125em rgba(78,181,222,0.25)}.button.is-primary:active,.docstring>section>a.button.docs-sourcelink:active,.button.is-primary.is-active,.docstring>section>a.button.is-active.docs-sourcelink{background-color:#39acda;border-color:transparent;color:#fff}.button.is-primary[disabled],.docstring>section>a.button.docs-sourcelink[disabled],fieldset[disabled] .button.is-primary,fieldset[disabled] .docstring>section>a.button.docs-sourcelink{background-color:#4eb5de;border-color:#4eb5de;box-shadow:none}.button.is-primary.is-inverted,.docstring>section>a.button.is-inverted.docs-sourcelink{background-color:#fff;color:#4eb5de}.button.is-primary.is-inverted:hover,.docstring>section>a.button.is-inverted.docs-sourcelink:hover,.button.is-primary.is-inverted.is-hovered,.docstring>section>a.button.is-inverted.is-hovered.docs-sourcelink{background-color:#f2f2f2}.button.is-primary.is-inverted[disabled],.docstring>section>a.button.is-inverted.docs-sourcelink[disabled],fieldset[disabled] .button.is-primary.is-inverted,fieldset[disabled] .docstring>section>a.button.is-inverted.docs-sourcelink{background-color:#fff;border-color:transparent;box-shadow:none;color:#4eb5de}.button.is-primary.is-loading::after,.docstring>section>a.button.is-loading.docs-sourcelink::after{border-color:transparent transparent #fff #fff !important}.button.is-primary.is-outlined,.docstring>section>a.button.is-outlined.docs-sourcelink{background-color:transparent;border-color:#4eb5de;color:#4eb5de}.button.is-primary.is-outlined:hover,.docstring>section>a.button.is-outlined.docs-sourcelink:hover,.button.is-primary.is-outlined.is-hovered,.docstring>section>a.button.is-outlined.is-hovered.docs-sourcelink,.button.is-primary.is-outlined:focus,.docstring>section>a.button.is-outlined.docs-sourcelink:focus,.button.is-primary.is-outlined.is-focused,.docstring>section>a.button.is-outlined.is-focused.docs-sourcelink{background-color:#4eb5de;border-color:#4eb5de;color:#fff}.button.is-primary.is-outlined.is-loading::after,.docstring>section>a.button.is-outlined.is-loading.docs-sourcelink::after{border-color:transparent transparent #4eb5de #4eb5de !important}.button.is-primary.is-outlined.is-loading:hover::after,.docstring>section>a.button.is-outlined.is-loading.docs-sourcelink:hover::after,.button.is-primary.is-outlined.is-loading.is-hovered::after,.docstring>section>a.button.is-outlined.is-loading.is-hovered.docs-sourcelink::after,.button.is-primary.is-outlined.is-loading:focus::after,.docstring>section>a.button.is-outlined.is-loading.docs-sourcelink:focus::after,.button.is-primary.is-outlined.is-loading.is-focused::after,.docstring>section>a.button.is-outlined.is-loading.is-focused.docs-sourcelink::after{border-color:transparent transparent #fff #fff !important}.button.is-primary.is-outlined[disabled],.docstring>section>a.button.is-outlined.docs-sourcelink[disabled],fieldset[disabled] .button.is-primary.is-outlined,fieldset[disabled] .docstring>section>a.button.is-outlined.docs-sourcelink{background-color:transparent;border-color:#4eb5de;box-shadow:none;color:#4eb5de}.button.is-primary.is-inverted.is-outlined,.docstring>section>a.button.is-inverted.is-outlined.docs-sourcelink{background-color:transparent;border-color:#fff;color:#fff}.button.is-primary.is-inverted.is-outlined:hover,.docstring>section>a.button.is-inverted.is-outlined.docs-sourcelink:hover,.button.is-primary.is-inverted.is-outlined.is-hovered,.docstring>section>a.button.is-inverted.is-outlined.is-hovered.docs-sourcelink,.button.is-primary.is-inverted.is-outlined:focus,.docstring>section>a.button.is-inverted.is-outlined.docs-sourcelink:focus,.button.is-primary.is-inverted.is-outlined.is-focused,.docstring>section>a.button.is-inverted.is-outlined.is-focused.docs-sourcelink{background-color:#fff;color:#4eb5de}.button.is-primary.is-inverted.is-outlined.is-loading:hover::after,.docstring>section>a.button.is-inverted.is-outlined.is-loading.docs-sourcelink:hover::after,.button.is-primary.is-inverted.is-outlined.is-loading.is-hovered::after,.docstring>section>a.button.is-inverted.is-outlined.is-loading.is-hovered.docs-sourcelink::after,.button.is-primary.is-inverted.is-outlined.is-loading:focus::after,.docstring>section>a.button.is-inverted.is-outlined.is-loading.docs-sourcelink:focus::after,.button.is-primary.is-inverted.is-outlined.is-loading.is-focused::after,.docstring>section>a.button.is-inverted.is-outlined.is-loading.is-focused.docs-sourcelink::after{border-color:transparent transparent #4eb5de #4eb5de !important}.button.is-primary.is-inverted.is-outlined[disabled],.docstring>section>a.button.is-inverted.is-outlined.docs-sourcelink[disabled],fieldset[disabled] .button.is-primary.is-inverted.is-outlined,fieldset[disabled] .docstring>section>a.button.is-inverted.is-outlined.docs-sourcelink{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-primary.is-light,.docstring>section>a.button.is-light.docs-sourcelink{background-color:#eef8fc;color:#1a6d8e}.button.is-primary.is-light:hover,.docstring>section>a.button.is-light.docs-sourcelink:hover,.button.is-primary.is-light.is-hovered,.docstring>section>a.button.is-light.is-hovered.docs-sourcelink{background-color:#e3f3fa;border-color:transparent;color:#1a6d8e}.button.is-primary.is-light:active,.docstring>section>a.button.is-light.docs-sourcelink:active,.button.is-primary.is-light.is-active,.docstring>section>a.button.is-light.is-active.docs-sourcelink{background-color:#d8eff8;border-color:transparent;color:#1a6d8e}.button.is-link{background-color:#2e63b8;border-color:transparent;color:#fff}.button.is-link:hover,.button.is-link.is-hovered{background-color:#2b5eae;border-color:transparent;color:#fff}.button.is-link:focus,.button.is-link.is-focused{border-color:transparent;color:#fff}.button.is-link:focus:not(:active),.button.is-link.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(46,99,184,0.25)}.button.is-link:active,.button.is-link.is-active{background-color:#2958a4;border-color:transparent;color:#fff}.button.is-link[disabled],fieldset[disabled] .button.is-link{background-color:#2e63b8;border-color:#2e63b8;box-shadow:none}.button.is-link.is-inverted{background-color:#fff;color:#2e63b8}.button.is-link.is-inverted:hover,.button.is-link.is-inverted.is-hovered{background-color:#f2f2f2}.button.is-link.is-inverted[disabled],fieldset[disabled] .button.is-link.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#2e63b8}.button.is-link.is-loading::after{border-color:transparent transparent #fff #fff !important}.button.is-link.is-outlined{background-color:transparent;border-color:#2e63b8;color:#2e63b8}.button.is-link.is-outlined:hover,.button.is-link.is-outlined.is-hovered,.button.is-link.is-outlined:focus,.button.is-link.is-outlined.is-focused{background-color:#2e63b8;border-color:#2e63b8;color:#fff}.button.is-link.is-outlined.is-loading::after{border-color:transparent transparent #2e63b8 #2e63b8 !important}.button.is-link.is-outlined.is-loading:hover::after,.button.is-link.is-outlined.is-loading.is-hovered::after,.button.is-link.is-outlined.is-loading:focus::after,.button.is-link.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #fff #fff !important}.button.is-link.is-outlined[disabled],fieldset[disabled] .button.is-link.is-outlined{background-color:transparent;border-color:#2e63b8;box-shadow:none;color:#2e63b8}.button.is-link.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-link.is-inverted.is-outlined:hover,.button.is-link.is-inverted.is-outlined.is-hovered,.button.is-link.is-inverted.is-outlined:focus,.button.is-link.is-inverted.is-outlined.is-focused{background-color:#fff;color:#2e63b8}.button.is-link.is-inverted.is-outlined.is-loading:hover::after,.button.is-link.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-link.is-inverted.is-outlined.is-loading:focus::after,.button.is-link.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #2e63b8 #2e63b8 !important}.button.is-link.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-link.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-link.is-light{background-color:#eff3fb;color:#3169c4}.button.is-link.is-light:hover,.button.is-link.is-light.is-hovered{background-color:#e4ecf8;border-color:transparent;color:#3169c4}.button.is-link.is-light:active,.button.is-link.is-light.is-active{background-color:#dae5f6;border-color:transparent;color:#3169c4}.button.is-info{background-color:#209cee;border-color:transparent;color:#fff}.button.is-info:hover,.button.is-info.is-hovered{background-color:#1497ed;border-color:transparent;color:#fff}.button.is-info:focus,.button.is-info.is-focused{border-color:transparent;color:#fff}.button.is-info:focus:not(:active),.button.is-info.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(32,156,238,0.25)}.button.is-info:active,.button.is-info.is-active{background-color:#1190e3;border-color:transparent;color:#fff}.button.is-info[disabled],fieldset[disabled] .button.is-info{background-color:#209cee;border-color:#209cee;box-shadow:none}.button.is-info.is-inverted{background-color:#fff;color:#209cee}.button.is-info.is-inverted:hover,.button.is-info.is-inverted.is-hovered{background-color:#f2f2f2}.button.is-info.is-inverted[disabled],fieldset[disabled] .button.is-info.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#209cee}.button.is-info.is-loading::after{border-color:transparent transparent #fff #fff !important}.button.is-info.is-outlined{background-color:transparent;border-color:#209cee;color:#209cee}.button.is-info.is-outlined:hover,.button.is-info.is-outlined.is-hovered,.button.is-info.is-outlined:focus,.button.is-info.is-outlined.is-focused{background-color:#209cee;border-color:#209cee;color:#fff}.button.is-info.is-outlined.is-loading::after{border-color:transparent transparent #209cee #209cee !important}.button.is-info.is-outlined.is-loading:hover::after,.button.is-info.is-outlined.is-loading.is-hovered::after,.button.is-info.is-outlined.is-loading:focus::after,.button.is-info.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #fff #fff !important}.button.is-info.is-outlined[disabled],fieldset[disabled] .button.is-info.is-outlined{background-color:transparent;border-color:#209cee;box-shadow:none;color:#209cee}.button.is-info.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-info.is-inverted.is-outlined:hover,.button.is-info.is-inverted.is-outlined.is-hovered,.button.is-info.is-inverted.is-outlined:focus,.button.is-info.is-inverted.is-outlined.is-focused{background-color:#fff;color:#209cee}.button.is-info.is-inverted.is-outlined.is-loading:hover::after,.button.is-info.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-info.is-inverted.is-outlined.is-loading:focus::after,.button.is-info.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #209cee #209cee !important}.button.is-info.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-info.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-info.is-light{background-color:#ecf7fe;color:#0e72b4}.button.is-info.is-light:hover,.button.is-info.is-light.is-hovered{background-color:#e0f1fd;border-color:transparent;color:#0e72b4}.button.is-info.is-light:active,.button.is-info.is-light.is-active{background-color:#d4ecfc;border-color:transparent;color:#0e72b4}.button.is-success{background-color:#22c35b;border-color:transparent;color:#fff}.button.is-success:hover,.button.is-success.is-hovered{background-color:#20b856;border-color:transparent;color:#fff}.button.is-success:focus,.button.is-success.is-focused{border-color:transparent;color:#fff}.button.is-success:focus:not(:active),.button.is-success.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(34,195,91,0.25)}.button.is-success:active,.button.is-success.is-active{background-color:#1ead51;border-color:transparent;color:#fff}.button.is-success[disabled],fieldset[disabled] .button.is-success{background-color:#22c35b;border-color:#22c35b;box-shadow:none}.button.is-success.is-inverted{background-color:#fff;color:#22c35b}.button.is-success.is-inverted:hover,.button.is-success.is-inverted.is-hovered{background-color:#f2f2f2}.button.is-success.is-inverted[disabled],fieldset[disabled] .button.is-success.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#22c35b}.button.is-success.is-loading::after{border-color:transparent transparent #fff #fff !important}.button.is-success.is-outlined{background-color:transparent;border-color:#22c35b;color:#22c35b}.button.is-success.is-outlined:hover,.button.is-success.is-outlined.is-hovered,.button.is-success.is-outlined:focus,.button.is-success.is-outlined.is-focused{background-color:#22c35b;border-color:#22c35b;color:#fff}.button.is-success.is-outlined.is-loading::after{border-color:transparent transparent #22c35b #22c35b !important}.button.is-success.is-outlined.is-loading:hover::after,.button.is-success.is-outlined.is-loading.is-hovered::after,.button.is-success.is-outlined.is-loading:focus::after,.button.is-success.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #fff #fff !important}.button.is-success.is-outlined[disabled],fieldset[disabled] .button.is-success.is-outlined{background-color:transparent;border-color:#22c35b;box-shadow:none;color:#22c35b}.button.is-success.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-success.is-inverted.is-outlined:hover,.button.is-success.is-inverted.is-outlined.is-hovered,.button.is-success.is-inverted.is-outlined:focus,.button.is-success.is-inverted.is-outlined.is-focused{background-color:#fff;color:#22c35b}.button.is-success.is-inverted.is-outlined.is-loading:hover::after,.button.is-success.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-success.is-inverted.is-outlined.is-loading:focus::after,.button.is-success.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #22c35b #22c35b !important}.button.is-success.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-success.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-success.is-light{background-color:#eefcf3;color:#198f43}.button.is-success.is-light:hover,.button.is-success.is-light.is-hovered{background-color:#e3faeb;border-color:transparent;color:#198f43}.button.is-success.is-light:active,.button.is-success.is-light.is-active{background-color:#d8f8e3;border-color:transparent;color:#198f43}.button.is-warning{background-color:#ffdd57;border-color:transparent;color:rgba(0,0,0,0.7)}.button.is-warning:hover,.button.is-warning.is-hovered{background-color:#ffda4a;border-color:transparent;color:rgba(0,0,0,0.7)}.button.is-warning:focus,.button.is-warning.is-focused{border-color:transparent;color:rgba(0,0,0,0.7)}.button.is-warning:focus:not(:active),.button.is-warning.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(255,221,87,0.25)}.button.is-warning:active,.button.is-warning.is-active{background-color:#ffd83e;border-color:transparent;color:rgba(0,0,0,0.7)}.button.is-warning[disabled],fieldset[disabled] .button.is-warning{background-color:#ffdd57;border-color:#ffdd57;box-shadow:none}.button.is-warning.is-inverted{background-color:rgba(0,0,0,0.7);color:#ffdd57}.button.is-warning.is-inverted:hover,.button.is-warning.is-inverted.is-hovered{background-color:rgba(0,0,0,0.7)}.button.is-warning.is-inverted[disabled],fieldset[disabled] .button.is-warning.is-inverted{background-color:rgba(0,0,0,0.7);border-color:transparent;box-shadow:none;color:#ffdd57}.button.is-warning.is-loading::after{border-color:transparent transparent rgba(0,0,0,0.7) rgba(0,0,0,0.7) !important}.button.is-warning.is-outlined{background-color:transparent;border-color:#ffdd57;color:#ffdd57}.button.is-warning.is-outlined:hover,.button.is-warning.is-outlined.is-hovered,.button.is-warning.is-outlined:focus,.button.is-warning.is-outlined.is-focused{background-color:#ffdd57;border-color:#ffdd57;color:rgba(0,0,0,0.7)}.button.is-warning.is-outlined.is-loading::after{border-color:transparent transparent #ffdd57 #ffdd57 !important}.button.is-warning.is-outlined.is-loading:hover::after,.button.is-warning.is-outlined.is-loading.is-hovered::after,.button.is-warning.is-outlined.is-loading:focus::after,.button.is-warning.is-outlined.is-loading.is-focused::after{border-color:transparent transparent rgba(0,0,0,0.7) rgba(0,0,0,0.7) !important}.button.is-warning.is-outlined[disabled],fieldset[disabled] .button.is-warning.is-outlined{background-color:transparent;border-color:#ffdd57;box-shadow:none;color:#ffdd57}.button.is-warning.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,0.7);color:rgba(0,0,0,0.7)}.button.is-warning.is-inverted.is-outlined:hover,.button.is-warning.is-inverted.is-outlined.is-hovered,.button.is-warning.is-inverted.is-outlined:focus,.button.is-warning.is-inverted.is-outlined.is-focused{background-color:rgba(0,0,0,0.7);color:#ffdd57}.button.is-warning.is-inverted.is-outlined.is-loading:hover::after,.button.is-warning.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-warning.is-inverted.is-outlined.is-loading:focus::after,.button.is-warning.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #ffdd57 #ffdd57 !important}.button.is-warning.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-warning.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,0.7);box-shadow:none;color:rgba(0,0,0,0.7)}.button.is-warning.is-light{background-color:#fffbeb;color:#947600}.button.is-warning.is-light:hover,.button.is-warning.is-light.is-hovered{background-color:#fff8de;border-color:transparent;color:#947600}.button.is-warning.is-light:active,.button.is-warning.is-light.is-active{background-color:#fff6d1;border-color:transparent;color:#947600}.button.is-danger{background-color:#da0b00;border-color:transparent;color:#fff}.button.is-danger:hover,.button.is-danger.is-hovered{background-color:#cd0a00;border-color:transparent;color:#fff}.button.is-danger:focus,.button.is-danger.is-focused{border-color:transparent;color:#fff}.button.is-danger:focus:not(:active),.button.is-danger.is-focused:not(:active){box-shadow:0 0 0 0.125em rgba(218,11,0,0.25)}.button.is-danger:active,.button.is-danger.is-active{background-color:#c10a00;border-color:transparent;color:#fff}.button.is-danger[disabled],fieldset[disabled] .button.is-danger{background-color:#da0b00;border-color:#da0b00;box-shadow:none}.button.is-danger.is-inverted{background-color:#fff;color:#da0b00}.button.is-danger.is-inverted:hover,.button.is-danger.is-inverted.is-hovered{background-color:#f2f2f2}.button.is-danger.is-inverted[disabled],fieldset[disabled] .button.is-danger.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#da0b00}.button.is-danger.is-loading::after{border-color:transparent transparent #fff #fff !important}.button.is-danger.is-outlined{background-color:transparent;border-color:#da0b00;color:#da0b00}.button.is-danger.is-outlined:hover,.button.is-danger.is-outlined.is-hovered,.button.is-danger.is-outlined:focus,.button.is-danger.is-outlined.is-focused{background-color:#da0b00;border-color:#da0b00;color:#fff}.button.is-danger.is-outlined.is-loading::after{border-color:transparent transparent #da0b00 #da0b00 !important}.button.is-danger.is-outlined.is-loading:hover::after,.button.is-danger.is-outlined.is-loading.is-hovered::after,.button.is-danger.is-outlined.is-loading:focus::after,.button.is-danger.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #fff #fff !important}.button.is-danger.is-outlined[disabled],fieldset[disabled] .button.is-danger.is-outlined{background-color:transparent;border-color:#da0b00;box-shadow:none;color:#da0b00}.button.is-danger.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-danger.is-inverted.is-outlined:hover,.button.is-danger.is-inverted.is-outlined.is-hovered,.button.is-danger.is-inverted.is-outlined:focus,.button.is-danger.is-inverted.is-outlined.is-focused{background-color:#fff;color:#da0b00}.button.is-danger.is-inverted.is-outlined.is-loading:hover::after,.button.is-danger.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-danger.is-inverted.is-outlined.is-loading:focus::after,.button.is-danger.is-inverted.is-outlined.is-loading.is-focused::after{border-color:transparent transparent #da0b00 #da0b00 !important}.button.is-danger.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-danger.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-danger.is-light{background-color:#ffeceb;color:#f50c00}.button.is-danger.is-light:hover,.button.is-danger.is-light.is-hovered{background-color:#ffe0de;border-color:transparent;color:#f50c00}.button.is-danger.is-light:active,.button.is-danger.is-light.is-active{background-color:#ffd3d1;border-color:transparent;color:#f50c00}.button.is-small,#documenter .docs-sidebar form.docs-search>input.button{font-size:.75rem}.button.is-small:not(.is-rounded),#documenter .docs-sidebar form.docs-search>input.button:not(.is-rounded){border-radius:2px}.button.is-normal{font-size:1rem}.button.is-medium{font-size:1.25rem}.button.is-large{font-size:1.5rem}.button[disabled],fieldset[disabled] .button{background-color:#fff;border-color:#dbdbdb;box-shadow:none;opacity:.5}.button.is-fullwidth{display:flex;width:100%}.button.is-loading{color:transparent !important;pointer-events:none}.button.is-loading::after{position:absolute;left:calc(50% - (1em * 0.5));top:calc(50% - (1em * 0.5));position:absolute !important}.button.is-static{background-color:#f5f5f5;border-color:#dbdbdb;color:#6b6b6b;box-shadow:none;pointer-events:none}.button.is-rounded,#documenter .docs-sidebar form.docs-search>input.button{border-radius:9999px;padding-left:calc(1em + 0.25em);padding-right:calc(1em + 0.25em)}.buttons{align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start}.buttons .button{margin-bottom:0.5rem}.buttons .button:not(:last-child):not(.is-fullwidth){margin-right:.5rem}.buttons:last-child{margin-bottom:-0.5rem}.buttons:not(:last-child){margin-bottom:1rem}.buttons.are-small .button:not(.is-normal):not(.is-medium):not(.is-large){font-size:.75rem}.buttons.are-small .button:not(.is-normal):not(.is-medium):not(.is-large):not(.is-rounded){border-radius:2px}.buttons.are-medium .button:not(.is-small):not(.is-normal):not(.is-large){font-size:1.25rem}.buttons.are-large .button:not(.is-small):not(.is-normal):not(.is-medium){font-size:1.5rem}.buttons.has-addons .button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.buttons.has-addons .button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0;margin-right:-1px}.buttons.has-addons .button:last-child{margin-right:0}.buttons.has-addons .button:hover,.buttons.has-addons .button.is-hovered{z-index:2}.buttons.has-addons .button:focus,.buttons.has-addons .button.is-focused,.buttons.has-addons .button:active,.buttons.has-addons .button.is-active,.buttons.has-addons .button.is-selected{z-index:3}.buttons.has-addons .button:focus:hover,.buttons.has-addons .button.is-focused:hover,.buttons.has-addons .button:active:hover,.buttons.has-addons .button.is-active:hover,.buttons.has-addons .button.is-selected:hover{z-index:4}.buttons.has-addons .button.is-expanded{flex-grow:1;flex-shrink:1}.buttons.is-centered{justify-content:center}.buttons.is-centered:not(.has-addons) .button:not(.is-fullwidth){margin-left:0.25rem;margin-right:0.25rem}.buttons.is-right{justify-content:flex-end}.buttons.is-right:not(.has-addons) .button:not(.is-fullwidth){margin-left:0.25rem;margin-right:0.25rem}@media screen and (max-width: 768px){.button.is-responsive.is-small,#documenter .docs-sidebar form.docs-search>input.is-responsive{font-size:.5625rem}.button.is-responsive,.button.is-responsive.is-normal{font-size:.65625rem}.button.is-responsive.is-medium{font-size:.75rem}.button.is-responsive.is-large{font-size:1rem}}@media screen and (min-width: 769px) and (max-width: 1055px){.button.is-responsive.is-small,#documenter .docs-sidebar form.docs-search>input.is-responsive{font-size:.65625rem}.button.is-responsive,.button.is-responsive.is-normal{font-size:.75rem}.button.is-responsive.is-medium{font-size:1rem}.button.is-responsive.is-large{font-size:1.25rem}}.container{flex-grow:1;margin:0 auto;position:relative;width:auto}.container.is-fluid{max-width:none !important;padding-left:32px;padding-right:32px;width:100%}@media screen and (min-width: 1056px){.container{max-width:992px}}@media screen and (max-width: 1215px){.container.is-widescreen:not(.is-max-desktop){max-width:1152px}}@media screen and (max-width: 1407px){.container.is-fullhd:not(.is-max-desktop):not(.is-max-widescreen){max-width:1344px}}@media screen and (min-width: 1216px){.container:not(.is-max-desktop){max-width:1152px}}@media screen and (min-width: 1408px){.container:not(.is-max-desktop):not(.is-max-widescreen){max-width:1344px}}.content li+li{margin-top:0.25em}.content p:not(:last-child),.content dl:not(:last-child),.content ol:not(:last-child),.content ul:not(:last-child),.content blockquote:not(:last-child),.content pre:not(:last-child),.content table:not(:last-child){margin-bottom:1em}.content h1,.content h2,.content h3,.content h4,.content h5,.content h6{color:#222;font-weight:600;line-height:1.125}.content h1{font-size:2em;margin-bottom:0.5em}.content h1:not(:first-child){margin-top:1em}.content h2{font-size:1.75em;margin-bottom:0.5714em}.content h2:not(:first-child){margin-top:1.1428em}.content h3{font-size:1.5em;margin-bottom:0.6666em}.content h3:not(:first-child){margin-top:1.3333em}.content h4{font-size:1.25em;margin-bottom:0.8em}.content h5{font-size:1.125em;margin-bottom:0.8888em}.content h6{font-size:1em;margin-bottom:1em}.content blockquote{background-color:#f5f5f5;border-left:5px solid #dbdbdb;padding:1.25em 1.5em}.content ol{list-style-position:outside;margin-left:2em;margin-top:1em}.content ol:not([type]){list-style-type:decimal}.content ol.is-lower-alpha:not([type]){list-style-type:lower-alpha}.content ol.is-lower-roman:not([type]){list-style-type:lower-roman}.content ol.is-upper-alpha:not([type]){list-style-type:upper-alpha}.content ol.is-upper-roman:not([type]){list-style-type:upper-roman}.content ul{list-style:disc outside;margin-left:2em;margin-top:1em}.content ul ul{list-style-type:circle;margin-top:0.5em}.content ul ul ul{list-style-type:square}.content dd{margin-left:2em}.content figure{margin-left:2em;margin-right:2em;text-align:center}.content figure:not(:first-child){margin-top:2em}.content figure:not(:last-child){margin-bottom:2em}.content figure img{display:inline-block}.content figure figcaption{font-style:italic}.content pre{-webkit-overflow-scrolling:touch;overflow-x:auto;padding:0;white-space:pre;word-wrap:normal}.content sup,.content sub{font-size:75%}.content table{width:100%}.content table td,.content table th{border:1px solid #dbdbdb;border-width:0 0 1px;padding:0.5em 0.75em;vertical-align:top}.content table th{color:#222}.content table th:not([align]){text-align:inherit}.content table thead td,.content table thead th{border-width:0 0 2px;color:#222}.content table tfoot td,.content table tfoot th{border-width:2px 0 0;color:#222}.content table tbody tr:last-child td,.content table tbody tr:last-child th{border-bottom-width:0}.content .tabs li+li{margin-top:0}.content.is-small,#documenter .docs-sidebar form.docs-search>input.content{font-size:.75rem}.content.is-normal{font-size:1rem}.content.is-medium{font-size:1.25rem}.content.is-large{font-size:1.5rem}.icon{align-items:center;display:inline-flex;justify-content:center;height:1.5rem;width:1.5rem}.icon.is-small,#documenter .docs-sidebar form.docs-search>input.icon{height:1rem;width:1rem}.icon.is-medium{height:2rem;width:2rem}.icon.is-large{height:3rem;width:3rem}.icon-text{align-items:flex-start;color:inherit;display:inline-flex;flex-wrap:wrap;line-height:1.5rem;vertical-align:top}.icon-text .icon{flex-grow:0;flex-shrink:0}.icon-text .icon:not(:last-child){margin-right:.25em}.icon-text .icon:not(:first-child){margin-left:.25em}div.icon-text{display:flex}.image,#documenter .docs-sidebar .docs-logo>img{display:block;position:relative}.image img,#documenter .docs-sidebar .docs-logo>img img{display:block;height:auto;width:100%}.image img.is-rounded,#documenter .docs-sidebar .docs-logo>img img.is-rounded{border-radius:9999px}.image.is-fullwidth,#documenter .docs-sidebar .docs-logo>img.is-fullwidth{width:100%}.image.is-square img,#documenter .docs-sidebar .docs-logo>img.is-square img,.image.is-square .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-square .has-ratio,.image.is-1by1 img,#documenter .docs-sidebar .docs-logo>img.is-1by1 img,.image.is-1by1 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-1by1 .has-ratio,.image.is-5by4 img,#documenter .docs-sidebar .docs-logo>img.is-5by4 img,.image.is-5by4 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-5by4 .has-ratio,.image.is-4by3 img,#documenter .docs-sidebar .docs-logo>img.is-4by3 img,.image.is-4by3 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-4by3 .has-ratio,.image.is-3by2 img,#documenter .docs-sidebar .docs-logo>img.is-3by2 img,.image.is-3by2 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-3by2 .has-ratio,.image.is-5by3 img,#documenter .docs-sidebar .docs-logo>img.is-5by3 img,.image.is-5by3 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-5by3 .has-ratio,.image.is-16by9 img,#documenter .docs-sidebar .docs-logo>img.is-16by9 img,.image.is-16by9 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-16by9 .has-ratio,.image.is-2by1 img,#documenter .docs-sidebar .docs-logo>img.is-2by1 img,.image.is-2by1 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-2by1 .has-ratio,.image.is-3by1 img,#documenter .docs-sidebar .docs-logo>img.is-3by1 img,.image.is-3by1 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-3by1 .has-ratio,.image.is-4by5 img,#documenter .docs-sidebar .docs-logo>img.is-4by5 img,.image.is-4by5 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-4by5 .has-ratio,.image.is-3by4 img,#documenter .docs-sidebar .docs-logo>img.is-3by4 img,.image.is-3by4 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-3by4 .has-ratio,.image.is-2by3 img,#documenter .docs-sidebar .docs-logo>img.is-2by3 img,.image.is-2by3 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-2by3 .has-ratio,.image.is-3by5 img,#documenter .docs-sidebar .docs-logo>img.is-3by5 img,.image.is-3by5 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-3by5 .has-ratio,.image.is-9by16 img,#documenter .docs-sidebar .docs-logo>img.is-9by16 img,.image.is-9by16 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-9by16 .has-ratio,.image.is-1by2 img,#documenter .docs-sidebar .docs-logo>img.is-1by2 img,.image.is-1by2 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-1by2 .has-ratio,.image.is-1by3 img,#documenter .docs-sidebar .docs-logo>img.is-1by3 img,.image.is-1by3 .has-ratio,#documenter .docs-sidebar .docs-logo>img.is-1by3 .has-ratio{height:100%;width:100%}.image.is-square,#documenter .docs-sidebar .docs-logo>img.is-square,.image.is-1by1,#documenter .docs-sidebar .docs-logo>img.is-1by1{padding-top:100%}.image.is-5by4,#documenter .docs-sidebar .docs-logo>img.is-5by4{padding-top:80%}.image.is-4by3,#documenter .docs-sidebar .docs-logo>img.is-4by3{padding-top:75%}.image.is-3by2,#documenter .docs-sidebar .docs-logo>img.is-3by2{padding-top:66.6666%}.image.is-5by3,#documenter .docs-sidebar .docs-logo>img.is-5by3{padding-top:60%}.image.is-16by9,#documenter .docs-sidebar .docs-logo>img.is-16by9{padding-top:56.25%}.image.is-2by1,#documenter .docs-sidebar .docs-logo>img.is-2by1{padding-top:50%}.image.is-3by1,#documenter .docs-sidebar .docs-logo>img.is-3by1{padding-top:33.3333%}.image.is-4by5,#documenter .docs-sidebar .docs-logo>img.is-4by5{padding-top:125%}.image.is-3by4,#documenter .docs-sidebar .docs-logo>img.is-3by4{padding-top:133.3333%}.image.is-2by3,#documenter .docs-sidebar .docs-logo>img.is-2by3{padding-top:150%}.image.is-3by5,#documenter .docs-sidebar .docs-logo>img.is-3by5{padding-top:166.6666%}.image.is-9by16,#documenter .docs-sidebar .docs-logo>img.is-9by16{padding-top:177.7777%}.image.is-1by2,#documenter .docs-sidebar .docs-logo>img.is-1by2{padding-top:200%}.image.is-1by3,#documenter .docs-sidebar .docs-logo>img.is-1by3{padding-top:300%}.image.is-16x16,#documenter .docs-sidebar .docs-logo>img.is-16x16{height:16px;width:16px}.image.is-24x24,#documenter .docs-sidebar .docs-logo>img.is-24x24{height:24px;width:24px}.image.is-32x32,#documenter .docs-sidebar .docs-logo>img.is-32x32{height:32px;width:32px}.image.is-48x48,#documenter .docs-sidebar .docs-logo>img.is-48x48{height:48px;width:48px}.image.is-64x64,#documenter .docs-sidebar .docs-logo>img.is-64x64{height:64px;width:64px}.image.is-96x96,#documenter .docs-sidebar .docs-logo>img.is-96x96{height:96px;width:96px}.image.is-128x128,#documenter .docs-sidebar .docs-logo>img.is-128x128{height:128px;width:128px}.notification{background-color:#f5f5f5;border-radius:4px;position:relative;padding:1.25rem 2.5rem 1.25rem 1.5rem}.notification a:not(.button):not(.dropdown-item){color:currentColor;text-decoration:underline}.notification strong{color:currentColor}.notification code,.notification pre{background:#fff}.notification pre code{background:transparent}.notification>.delete{right:.5rem;position:absolute;top:0.5rem}.notification .title,.notification .subtitle,.notification .content{color:currentColor}.notification.is-white{background-color:#fff;color:#0a0a0a}.notification.is-black{background-color:#0a0a0a;color:#fff}.notification.is-light{background-color:#f5f5f5;color:rgba(0,0,0,0.7)}.notification.is-dark,.content kbd.notification{background-color:#363636;color:#fff}.notification.is-primary,.docstring>section>a.notification.docs-sourcelink{background-color:#4eb5de;color:#fff}.notification.is-primary.is-light,.docstring>section>a.notification.is-light.docs-sourcelink{background-color:#eef8fc;color:#1a6d8e}.notification.is-link{background-color:#2e63b8;color:#fff}.notification.is-link.is-light{background-color:#eff3fb;color:#3169c4}.notification.is-info{background-color:#209cee;color:#fff}.notification.is-info.is-light{background-color:#ecf7fe;color:#0e72b4}.notification.is-success{background-color:#22c35b;color:#fff}.notification.is-success.is-light{background-color:#eefcf3;color:#198f43}.notification.is-warning{background-color:#ffdd57;color:rgba(0,0,0,0.7)}.notification.is-warning.is-light{background-color:#fffbeb;color:#947600}.notification.is-danger{background-color:#da0b00;color:#fff}.notification.is-danger.is-light{background-color:#ffeceb;color:#f50c00}.progress{-moz-appearance:none;-webkit-appearance:none;border:none;border-radius:9999px;display:block;height:1rem;overflow:hidden;padding:0;width:100%}.progress::-webkit-progress-bar{background-color:#ededed}.progress::-webkit-progress-value{background-color:#222}.progress::-moz-progress-bar{background-color:#222}.progress::-ms-fill{background-color:#222;border:none}.progress.is-white::-webkit-progress-value{background-color:#fff}.progress.is-white::-moz-progress-bar{background-color:#fff}.progress.is-white::-ms-fill{background-color:#fff}.progress.is-white:indeterminate{background-image:linear-gradient(to right, #fff 30%, #ededed 30%)}.progress.is-black::-webkit-progress-value{background-color:#0a0a0a}.progress.is-black::-moz-progress-bar{background-color:#0a0a0a}.progress.is-black::-ms-fill{background-color:#0a0a0a}.progress.is-black:indeterminate{background-image:linear-gradient(to right, #0a0a0a 30%, #ededed 30%)}.progress.is-light::-webkit-progress-value{background-color:#f5f5f5}.progress.is-light::-moz-progress-bar{background-color:#f5f5f5}.progress.is-light::-ms-fill{background-color:#f5f5f5}.progress.is-light:indeterminate{background-image:linear-gradient(to right, #f5f5f5 30%, #ededed 30%)}.progress.is-dark::-webkit-progress-value,.content kbd.progress::-webkit-progress-value{background-color:#363636}.progress.is-dark::-moz-progress-bar,.content kbd.progress::-moz-progress-bar{background-color:#363636}.progress.is-dark::-ms-fill,.content kbd.progress::-ms-fill{background-color:#363636}.progress.is-dark:indeterminate,.content kbd.progress:indeterminate{background-image:linear-gradient(to right, #363636 30%, #ededed 30%)}.progress.is-primary::-webkit-progress-value,.docstring>section>a.progress.docs-sourcelink::-webkit-progress-value{background-color:#4eb5de}.progress.is-primary::-moz-progress-bar,.docstring>section>a.progress.docs-sourcelink::-moz-progress-bar{background-color:#4eb5de}.progress.is-primary::-ms-fill,.docstring>section>a.progress.docs-sourcelink::-ms-fill{background-color:#4eb5de}.progress.is-primary:indeterminate,.docstring>section>a.progress.docs-sourcelink:indeterminate{background-image:linear-gradient(to right, #4eb5de 30%, #ededed 30%)}.progress.is-link::-webkit-progress-value{background-color:#2e63b8}.progress.is-link::-moz-progress-bar{background-color:#2e63b8}.progress.is-link::-ms-fill{background-color:#2e63b8}.progress.is-link:indeterminate{background-image:linear-gradient(to right, #2e63b8 30%, #ededed 30%)}.progress.is-info::-webkit-progress-value{background-color:#209cee}.progress.is-info::-moz-progress-bar{background-color:#209cee}.progress.is-info::-ms-fill{background-color:#209cee}.progress.is-info:indeterminate{background-image:linear-gradient(to right, #209cee 30%, #ededed 30%)}.progress.is-success::-webkit-progress-value{background-color:#22c35b}.progress.is-success::-moz-progress-bar{background-color:#22c35b}.progress.is-success::-ms-fill{background-color:#22c35b}.progress.is-success:indeterminate{background-image:linear-gradient(to right, #22c35b 30%, #ededed 30%)}.progress.is-warning::-webkit-progress-value{background-color:#ffdd57}.progress.is-warning::-moz-progress-bar{background-color:#ffdd57}.progress.is-warning::-ms-fill{background-color:#ffdd57}.progress.is-warning:indeterminate{background-image:linear-gradient(to right, #ffdd57 30%, #ededed 30%)}.progress.is-danger::-webkit-progress-value{background-color:#da0b00}.progress.is-danger::-moz-progress-bar{background-color:#da0b00}.progress.is-danger::-ms-fill{background-color:#da0b00}.progress.is-danger:indeterminate{background-image:linear-gradient(to right, #da0b00 30%, #ededed 30%)}.progress:indeterminate{animation-duration:1.5s;animation-iteration-count:infinite;animation-name:moveIndeterminate;animation-timing-function:linear;background-color:#ededed;background-image:linear-gradient(to right, #222 30%, #ededed 30%);background-position:top left;background-repeat:no-repeat;background-size:150% 150%}.progress:indeterminate::-webkit-progress-bar{background-color:transparent}.progress:indeterminate::-moz-progress-bar{background-color:transparent}.progress:indeterminate::-ms-fill{animation-name:none}.progress.is-small,#documenter .docs-sidebar form.docs-search>input.progress{height:.75rem}.progress.is-medium{height:1.25rem}.progress.is-large{height:1.5rem}@keyframes moveIndeterminate{from{background-position:200% 0}to{background-position:-200% 0}}.table{background-color:#fff;color:#222}.table td,.table th{border:1px solid #dbdbdb;border-width:0 0 1px;padding:0.5em 0.75em;vertical-align:top}.table td.is-white,.table th.is-white{background-color:#fff;border-color:#fff;color:#0a0a0a}.table td.is-black,.table th.is-black{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.table td.is-light,.table th.is-light{background-color:#f5f5f5;border-color:#f5f5f5;color:rgba(0,0,0,0.7)}.table td.is-dark,.table th.is-dark{background-color:#363636;border-color:#363636;color:#fff}.table td.is-primary,.table th.is-primary{background-color:#4eb5de;border-color:#4eb5de;color:#fff}.table td.is-link,.table th.is-link{background-color:#2e63b8;border-color:#2e63b8;color:#fff}.table td.is-info,.table th.is-info{background-color:#209cee;border-color:#209cee;color:#fff}.table td.is-success,.table th.is-success{background-color:#22c35b;border-color:#22c35b;color:#fff}.table td.is-warning,.table th.is-warning{background-color:#ffdd57;border-color:#ffdd57;color:rgba(0,0,0,0.7)}.table td.is-danger,.table th.is-danger{background-color:#da0b00;border-color:#da0b00;color:#fff}.table td.is-narrow,.table th.is-narrow{white-space:nowrap;width:1%}.table td.is-selected,.table th.is-selected{background-color:#4eb5de;color:#fff}.table td.is-selected a,.table td.is-selected strong,.table th.is-selected a,.table th.is-selected strong{color:currentColor}.table td.is-vcentered,.table th.is-vcentered{vertical-align:middle}.table th{color:#222}.table th:not([align]){text-align:left}.table tr.is-selected{background-color:#4eb5de;color:#fff}.table tr.is-selected a,.table tr.is-selected strong{color:currentColor}.table tr.is-selected td,.table tr.is-selected th{border-color:#fff;color:currentColor}.table thead{background-color:rgba(0,0,0,0)}.table thead td,.table thead th{border-width:0 0 2px;color:#222}.table tfoot{background-color:rgba(0,0,0,0)}.table tfoot td,.table tfoot th{border-width:2px 0 0;color:#222}.table tbody{background-color:rgba(0,0,0,0)}.table tbody tr:last-child td,.table tbody tr:last-child th{border-bottom-width:0}.table.is-bordered td,.table.is-bordered th{border-width:1px}.table.is-bordered tr:last-child td,.table.is-bordered tr:last-child th{border-bottom-width:1px}.table.is-fullwidth{width:100%}.table.is-hoverable tbody tr:not(.is-selected):hover{background-color:#fafafa}.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover{background-color:#fafafa}.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover:nth-child(even){background-color:#f5f5f5}.table.is-narrow td,.table.is-narrow th{padding:0.25em 0.5em}.table.is-striped tbody tr:not(.is-selected):nth-child(even){background-color:#fafafa}.table-container{-webkit-overflow-scrolling:touch;overflow:auto;overflow-y:hidden;max-width:100%}.tags{align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start}.tags .tag,.tags .content kbd,.content .tags kbd,.tags .docstring>section>a.docs-sourcelink{margin-bottom:0.5rem}.tags .tag:not(:last-child),.tags .content kbd:not(:last-child),.content .tags kbd:not(:last-child),.tags .docstring>section>a.docs-sourcelink:not(:last-child){margin-right:.5rem}.tags:last-child{margin-bottom:-0.5rem}.tags:not(:last-child){margin-bottom:1rem}.tags.are-medium .tag:not(.is-normal):not(.is-large),.tags.are-medium .content kbd:not(.is-normal):not(.is-large),.content .tags.are-medium kbd:not(.is-normal):not(.is-large),.tags.are-medium .docstring>section>a.docs-sourcelink:not(.is-normal):not(.is-large){font-size:1rem}.tags.are-large .tag:not(.is-normal):not(.is-medium),.tags.are-large .content kbd:not(.is-normal):not(.is-medium),.content .tags.are-large kbd:not(.is-normal):not(.is-medium),.tags.are-large .docstring>section>a.docs-sourcelink:not(.is-normal):not(.is-medium){font-size:1.25rem}.tags.is-centered{justify-content:center}.tags.is-centered .tag,.tags.is-centered .content kbd,.content .tags.is-centered kbd,.tags.is-centered .docstring>section>a.docs-sourcelink{margin-right:0.25rem;margin-left:0.25rem}.tags.is-right{justify-content:flex-end}.tags.is-right .tag:not(:first-child),.tags.is-right .content kbd:not(:first-child),.content .tags.is-right kbd:not(:first-child),.tags.is-right .docstring>section>a.docs-sourcelink:not(:first-child){margin-left:0.5rem}.tags.is-right .tag:not(:last-child),.tags.is-right .content kbd:not(:last-child),.content .tags.is-right kbd:not(:last-child),.tags.is-right .docstring>section>a.docs-sourcelink:not(:last-child){margin-right:0}.tags.has-addons .tag,.tags.has-addons .content kbd,.content .tags.has-addons kbd,.tags.has-addons .docstring>section>a.docs-sourcelink{margin-right:0}.tags.has-addons .tag:not(:first-child),.tags.has-addons .content kbd:not(:first-child),.content .tags.has-addons kbd:not(:first-child),.tags.has-addons .docstring>section>a.docs-sourcelink:not(:first-child){margin-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.tags.has-addons .tag:not(:last-child),.tags.has-addons .content kbd:not(:last-child),.content .tags.has-addons kbd:not(:last-child),.tags.has-addons .docstring>section>a.docs-sourcelink:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.tag:not(body),.content kbd:not(body),.docstring>section>a.docs-sourcelink:not(body){align-items:center;background-color:#f5f5f5;border-radius:4px;color:#222;display:inline-flex;font-size:.75rem;height:2em;justify-content:center;line-height:1.5;padding-left:0.75em;padding-right:0.75em;white-space:nowrap}.tag:not(body) .delete,.content kbd:not(body) .delete,.docstring>section>a.docs-sourcelink:not(body) .delete{margin-left:.25rem;margin-right:-.375rem}.tag.is-white:not(body),.content kbd.is-white:not(body),.docstring>section>a.docs-sourcelink.is-white:not(body){background-color:#fff;color:#0a0a0a}.tag.is-black:not(body),.content kbd.is-black:not(body),.docstring>section>a.docs-sourcelink.is-black:not(body){background-color:#0a0a0a;color:#fff}.tag.is-light:not(body),.content kbd.is-light:not(body),.docstring>section>a.docs-sourcelink.is-light:not(body){background-color:#f5f5f5;color:rgba(0,0,0,0.7)}.tag.is-dark:not(body),.content kbd:not(body),.docstring>section>a.docs-sourcelink.is-dark:not(body),.content .docstring>section>kbd:not(body){background-color:#363636;color:#fff}.tag.is-primary:not(body),.content kbd.is-primary:not(body),.docstring>section>a.docs-sourcelink:not(body){background-color:#4eb5de;color:#fff}.tag.is-primary.is-light:not(body),.content kbd.is-primary.is-light:not(body),.docstring>section>a.docs-sourcelink.is-light:not(body){background-color:#eef8fc;color:#1a6d8e}.tag.is-link:not(body),.content kbd.is-link:not(body),.docstring>section>a.docs-sourcelink.is-link:not(body){background-color:#2e63b8;color:#fff}.tag.is-link.is-light:not(body),.content kbd.is-link.is-light:not(body),.docstring>section>a.docs-sourcelink.is-link.is-light:not(body){background-color:#eff3fb;color:#3169c4}.tag.is-info:not(body),.content kbd.is-info:not(body),.docstring>section>a.docs-sourcelink.is-info:not(body){background-color:#209cee;color:#fff}.tag.is-info.is-light:not(body),.content kbd.is-info.is-light:not(body),.docstring>section>a.docs-sourcelink.is-info.is-light:not(body){background-color:#ecf7fe;color:#0e72b4}.tag.is-success:not(body),.content kbd.is-success:not(body),.docstring>section>a.docs-sourcelink.is-success:not(body){background-color:#22c35b;color:#fff}.tag.is-success.is-light:not(body),.content kbd.is-success.is-light:not(body),.docstring>section>a.docs-sourcelink.is-success.is-light:not(body){background-color:#eefcf3;color:#198f43}.tag.is-warning:not(body),.content kbd.is-warning:not(body),.docstring>section>a.docs-sourcelink.is-warning:not(body){background-color:#ffdd57;color:rgba(0,0,0,0.7)}.tag.is-warning.is-light:not(body),.content kbd.is-warning.is-light:not(body),.docstring>section>a.docs-sourcelink.is-warning.is-light:not(body){background-color:#fffbeb;color:#947600}.tag.is-danger:not(body),.content kbd.is-danger:not(body),.docstring>section>a.docs-sourcelink.is-danger:not(body){background-color:#da0b00;color:#fff}.tag.is-danger.is-light:not(body),.content kbd.is-danger.is-light:not(body),.docstring>section>a.docs-sourcelink.is-danger.is-light:not(body){background-color:#ffeceb;color:#f50c00}.tag.is-normal:not(body),.content kbd.is-normal:not(body),.docstring>section>a.docs-sourcelink.is-normal:not(body){font-size:.75rem}.tag.is-medium:not(body),.content kbd.is-medium:not(body),.docstring>section>a.docs-sourcelink.is-medium:not(body){font-size:1rem}.tag.is-large:not(body),.content kbd.is-large:not(body),.docstring>section>a.docs-sourcelink.is-large:not(body){font-size:1.25rem}.tag:not(body) .icon:first-child:not(:last-child),.content kbd:not(body) .icon:first-child:not(:last-child),.docstring>section>a.docs-sourcelink:not(body) .icon:first-child:not(:last-child){margin-left:-.375em;margin-right:.1875em}.tag:not(body) .icon:last-child:not(:first-child),.content kbd:not(body) .icon:last-child:not(:first-child),.docstring>section>a.docs-sourcelink:not(body) .icon:last-child:not(:first-child){margin-left:.1875em;margin-right:-.375em}.tag:not(body) .icon:first-child:last-child,.content kbd:not(body) .icon:first-child:last-child,.docstring>section>a.docs-sourcelink:not(body) .icon:first-child:last-child{margin-left:-.375em;margin-right:-.375em}.tag.is-delete:not(body),.content kbd.is-delete:not(body),.docstring>section>a.docs-sourcelink.is-delete:not(body){margin-left:1px;padding:0;position:relative;width:2em}.tag.is-delete:not(body)::before,.content kbd.is-delete:not(body)::before,.docstring>section>a.docs-sourcelink.is-delete:not(body)::before,.tag.is-delete:not(body)::after,.content kbd.is-delete:not(body)::after,.docstring>section>a.docs-sourcelink.is-delete:not(body)::after{background-color:currentColor;content:"";display:block;left:50%;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%) rotate(45deg);transform-origin:center center}.tag.is-delete:not(body)::before,.content kbd.is-delete:not(body)::before,.docstring>section>a.docs-sourcelink.is-delete:not(body)::before{height:1px;width:50%}.tag.is-delete:not(body)::after,.content kbd.is-delete:not(body)::after,.docstring>section>a.docs-sourcelink.is-delete:not(body)::after{height:50%;width:1px}.tag.is-delete:not(body):hover,.content kbd.is-delete:not(body):hover,.docstring>section>a.docs-sourcelink.is-delete:not(body):hover,.tag.is-delete:not(body):focus,.content kbd.is-delete:not(body):focus,.docstring>section>a.docs-sourcelink.is-delete:not(body):focus{background-color:#e8e8e8}.tag.is-delete:not(body):active,.content kbd.is-delete:not(body):active,.docstring>section>a.docs-sourcelink.is-delete:not(body):active{background-color:#dbdbdb}.tag.is-rounded:not(body),#documenter .docs-sidebar form.docs-search>input:not(body),.content kbd.is-rounded:not(body),#documenter .docs-sidebar .content form.docs-search>input:not(body),.docstring>section>a.docs-sourcelink.is-rounded:not(body){border-radius:9999px}a.tag:hover,.docstring>section>a.docs-sourcelink:hover{text-decoration:underline}.title,.subtitle{word-break:break-word}.title em,.title span,.subtitle em,.subtitle span{font-weight:inherit}.title sub,.subtitle sub{font-size:.75em}.title sup,.subtitle sup{font-size:.75em}.title .tag,.title .content kbd,.content .title kbd,.title .docstring>section>a.docs-sourcelink,.subtitle .tag,.subtitle .content kbd,.content .subtitle kbd,.subtitle .docstring>section>a.docs-sourcelink{vertical-align:middle}.title{color:#222;font-size:2rem;font-weight:600;line-height:1.125}.title strong{color:inherit;font-weight:inherit}.title:not(.is-spaced)+.subtitle{margin-top:-1.25rem}.title.is-1{font-size:3rem}.title.is-2{font-size:2.5rem}.title.is-3{font-size:2rem}.title.is-4{font-size:1.5rem}.title.is-5{font-size:1.25rem}.title.is-6{font-size:1rem}.title.is-7{font-size:.75rem}.subtitle{color:#222;font-size:1.25rem;font-weight:400;line-height:1.25}.subtitle strong{color:#222;font-weight:600}.subtitle:not(.is-spaced)+.title{margin-top:-1.25rem}.subtitle.is-1{font-size:3rem}.subtitle.is-2{font-size:2.5rem}.subtitle.is-3{font-size:2rem}.subtitle.is-4{font-size:1.5rem}.subtitle.is-5{font-size:1.25rem}.subtitle.is-6{font-size:1rem}.subtitle.is-7{font-size:.75rem}.heading{display:block;font-size:11px;letter-spacing:1px;margin-bottom:5px;text-transform:uppercase}.number{align-items:center;background-color:#f5f5f5;border-radius:9999px;display:inline-flex;font-size:1.25rem;height:2em;justify-content:center;margin-right:1.5rem;min-width:2.5em;padding:0.25rem 0.5rem;text-align:center;vertical-align:top}.select select,.textarea,.input,#documenter .docs-sidebar form.docs-search>input{background-color:#fff;border-color:#dbdbdb;border-radius:4px;color:#222}.select select::-moz-placeholder,.textarea::-moz-placeholder,.input::-moz-placeholder,#documenter .docs-sidebar form.docs-search>input::-moz-placeholder{color:#707070}.select select::-webkit-input-placeholder,.textarea::-webkit-input-placeholder,.input::-webkit-input-placeholder,#documenter .docs-sidebar form.docs-search>input::-webkit-input-placeholder{color:#707070}.select select:-moz-placeholder,.textarea:-moz-placeholder,.input:-moz-placeholder,#documenter .docs-sidebar form.docs-search>input:-moz-placeholder{color:#707070}.select select:-ms-input-placeholder,.textarea:-ms-input-placeholder,.input:-ms-input-placeholder,#documenter .docs-sidebar form.docs-search>input:-ms-input-placeholder{color:#707070}.select select:hover,.textarea:hover,.input:hover,#documenter .docs-sidebar form.docs-search>input:hover,.select select.is-hovered,.is-hovered.textarea,.is-hovered.input,#documenter .docs-sidebar form.docs-search>input.is-hovered{border-color:#b5b5b5}.select select:focus,.textarea:focus,.input:focus,#documenter .docs-sidebar form.docs-search>input:focus,.select select.is-focused,.is-focused.textarea,.is-focused.input,#documenter .docs-sidebar form.docs-search>input.is-focused,.select select:active,.textarea:active,.input:active,#documenter .docs-sidebar form.docs-search>input:active,.select select.is-active,.is-active.textarea,.is-active.input,#documenter .docs-sidebar form.docs-search>input.is-active{border-color:#2e63b8;box-shadow:0 0 0 0.125em rgba(46,99,184,0.25)}.select select[disabled],.textarea[disabled],.input[disabled],#documenter .docs-sidebar form.docs-search>input[disabled],fieldset[disabled] .select select,.select fieldset[disabled] select,fieldset[disabled] .textarea,fieldset[disabled] .input,fieldset[disabled] #documenter .docs-sidebar form.docs-search>input,#documenter .docs-sidebar fieldset[disabled] form.docs-search>input{background-color:#f5f5f5;border-color:#f5f5f5;box-shadow:none;color:#6b6b6b}.select select[disabled]::-moz-placeholder,.textarea[disabled]::-moz-placeholder,.input[disabled]::-moz-placeholder,#documenter .docs-sidebar form.docs-search>input[disabled]::-moz-placeholder,fieldset[disabled] .select select::-moz-placeholder,.select fieldset[disabled] select::-moz-placeholder,fieldset[disabled] .textarea::-moz-placeholder,fieldset[disabled] .input::-moz-placeholder,fieldset[disabled] #documenter .docs-sidebar form.docs-search>input::-moz-placeholder,#documenter .docs-sidebar fieldset[disabled] form.docs-search>input::-moz-placeholder{color:rgba(107,107,107,0.3)}.select select[disabled]::-webkit-input-placeholder,.textarea[disabled]::-webkit-input-placeholder,.input[disabled]::-webkit-input-placeholder,#documenter .docs-sidebar form.docs-search>input[disabled]::-webkit-input-placeholder,fieldset[disabled] .select select::-webkit-input-placeholder,.select fieldset[disabled] select::-webkit-input-placeholder,fieldset[disabled] .textarea::-webkit-input-placeholder,fieldset[disabled] .input::-webkit-input-placeholder,fieldset[disabled] #documenter .docs-sidebar form.docs-search>input::-webkit-input-placeholder,#documenter .docs-sidebar fieldset[disabled] form.docs-search>input::-webkit-input-placeholder{color:rgba(107,107,107,0.3)}.select select[disabled]:-moz-placeholder,.textarea[disabled]:-moz-placeholder,.input[disabled]:-moz-placeholder,#documenter .docs-sidebar form.docs-search>input[disabled]:-moz-placeholder,fieldset[disabled] .select select:-moz-placeholder,.select fieldset[disabled] select:-moz-placeholder,fieldset[disabled] .textarea:-moz-placeholder,fieldset[disabled] .input:-moz-placeholder,fieldset[disabled] #documenter .docs-sidebar form.docs-search>input:-moz-placeholder,#documenter .docs-sidebar fieldset[disabled] form.docs-search>input:-moz-placeholder{color:rgba(107,107,107,0.3)}.select select[disabled]:-ms-input-placeholder,.textarea[disabled]:-ms-input-placeholder,.input[disabled]:-ms-input-placeholder,#documenter .docs-sidebar form.docs-search>input[disabled]:-ms-input-placeholder,fieldset[disabled] .select select:-ms-input-placeholder,.select fieldset[disabled] select:-ms-input-placeholder,fieldset[disabled] .textarea:-ms-input-placeholder,fieldset[disabled] .input:-ms-input-placeholder,fieldset[disabled] #documenter .docs-sidebar form.docs-search>input:-ms-input-placeholder,#documenter .docs-sidebar fieldset[disabled] form.docs-search>input:-ms-input-placeholder{color:rgba(107,107,107,0.3)}.textarea,.input,#documenter .docs-sidebar form.docs-search>input{box-shadow:inset 0 0.0625em 0.125em rgba(10,10,10,0.05);max-width:100%;width:100%}.textarea[readonly],.input[readonly],#documenter .docs-sidebar form.docs-search>input[readonly]{box-shadow:none}.is-white.textarea,.is-white.input,#documenter .docs-sidebar form.docs-search>input.is-white{border-color:#fff}.is-white.textarea:focus,.is-white.input:focus,#documenter .docs-sidebar form.docs-search>input.is-white:focus,.is-white.is-focused.textarea,.is-white.is-focused.input,#documenter .docs-sidebar form.docs-search>input.is-focused,.is-white.textarea:active,.is-white.input:active,#documenter .docs-sidebar form.docs-search>input.is-white:active,.is-white.is-active.textarea,.is-white.is-active.input,#documenter .docs-sidebar form.docs-search>input.is-active{box-shadow:0 0 0 0.125em rgba(255,255,255,0.25)}.is-black.textarea,.is-black.input,#documenter .docs-sidebar form.docs-search>input.is-black{border-color:#0a0a0a}.is-black.textarea:focus,.is-black.input:focus,#documenter .docs-sidebar form.docs-search>input.is-black:focus,.is-black.is-focused.textarea,.is-black.is-focused.input,#documenter .docs-sidebar form.docs-search>input.is-focused,.is-black.textarea:active,.is-black.input:active,#documenter .docs-sidebar form.docs-search>input.is-black:active,.is-black.is-active.textarea,.is-black.is-active.input,#documenter .docs-sidebar form.docs-search>input.is-active{box-shadow:0 0 0 0.125em rgba(10,10,10,0.25)}.is-light.textarea,.is-light.input,#documenter .docs-sidebar form.docs-search>input.is-light{border-color:#f5f5f5}.is-light.textarea:focus,.is-light.input:focus,#documenter .docs-sidebar form.docs-search>input.is-light:focus,.is-light.is-focused.textarea,.is-light.is-focused.input,#documenter .docs-sidebar form.docs-search>input.is-focused,.is-light.textarea:active,.is-light.input:active,#documenter .docs-sidebar form.docs-search>input.is-light:active,.is-light.is-active.textarea,.is-light.is-active.input,#documenter .docs-sidebar form.docs-search>input.is-active{box-shadow:0 0 0 0.125em rgba(245,245,245,0.25)}.is-dark.textarea,.content kbd.textarea,.is-dark.input,#documenter .docs-sidebar form.docs-search>input.is-dark,.content kbd.input{border-color:#363636}.is-dark.textarea:focus,.content kbd.textarea:focus,.is-dark.input:focus,#documenter .docs-sidebar form.docs-search>input.is-dark:focus,.content kbd.input:focus,.is-dark.is-focused.textarea,.content kbd.is-focused.textarea,.is-dark.is-focused.input,#documenter .docs-sidebar form.docs-search>input.is-focused,.content kbd.is-focused.input,#documenter .docs-sidebar .content form.docs-search>input.is-focused,.is-dark.textarea:active,.content kbd.textarea:active,.is-dark.input:active,#documenter .docs-sidebar form.docs-search>input.is-dark:active,.content kbd.input:active,.is-dark.is-active.textarea,.content kbd.is-active.textarea,.is-dark.is-active.input,#documenter .docs-sidebar form.docs-search>input.is-active,.content kbd.is-active.input,#documenter .docs-sidebar .content form.docs-search>input.is-active{box-shadow:0 0 0 0.125em rgba(54,54,54,0.25)}.is-primary.textarea,.docstring>section>a.textarea.docs-sourcelink,.is-primary.input,#documenter .docs-sidebar form.docs-search>input.is-primary,.docstring>section>a.input.docs-sourcelink{border-color:#4eb5de}.is-primary.textarea:focus,.docstring>section>a.textarea.docs-sourcelink:focus,.is-primary.input:focus,#documenter .docs-sidebar form.docs-search>input.is-primary:focus,.docstring>section>a.input.docs-sourcelink:focus,.is-primary.is-focused.textarea,.docstring>section>a.is-focused.textarea.docs-sourcelink,.is-primary.is-focused.input,#documenter .docs-sidebar form.docs-search>input.is-focused,.docstring>section>a.is-focused.input.docs-sourcelink,.is-primary.textarea:active,.docstring>section>a.textarea.docs-sourcelink:active,.is-primary.input:active,#documenter .docs-sidebar form.docs-search>input.is-primary:active,.docstring>section>a.input.docs-sourcelink:active,.is-primary.is-active.textarea,.docstring>section>a.is-active.textarea.docs-sourcelink,.is-primary.is-active.input,#documenter .docs-sidebar form.docs-search>input.is-active,.docstring>section>a.is-active.input.docs-sourcelink{box-shadow:0 0 0 0.125em rgba(78,181,222,0.25)}.is-link.textarea,.is-link.input,#documenter .docs-sidebar form.docs-search>input.is-link{border-color:#2e63b8}.is-link.textarea:focus,.is-link.input:focus,#documenter .docs-sidebar form.docs-search>input.is-link:focus,.is-link.is-focused.textarea,.is-link.is-focused.input,#documenter .docs-sidebar form.docs-search>input.is-focused,.is-link.textarea:active,.is-link.input:active,#documenter .docs-sidebar form.docs-search>input.is-link:active,.is-link.is-active.textarea,.is-link.is-active.input,#documenter .docs-sidebar form.docs-search>input.is-active{box-shadow:0 0 0 0.125em rgba(46,99,184,0.25)}.is-info.textarea,.is-info.input,#documenter .docs-sidebar form.docs-search>input.is-info{border-color:#209cee}.is-info.textarea:focus,.is-info.input:focus,#documenter .docs-sidebar form.docs-search>input.is-info:focus,.is-info.is-focused.textarea,.is-info.is-focused.input,#documenter .docs-sidebar form.docs-search>input.is-focused,.is-info.textarea:active,.is-info.input:active,#documenter .docs-sidebar form.docs-search>input.is-info:active,.is-info.is-active.textarea,.is-info.is-active.input,#documenter .docs-sidebar form.docs-search>input.is-active{box-shadow:0 0 0 0.125em rgba(32,156,238,0.25)}.is-success.textarea,.is-success.input,#documenter .docs-sidebar form.docs-search>input.is-success{border-color:#22c35b}.is-success.textarea:focus,.is-success.input:focus,#documenter .docs-sidebar form.docs-search>input.is-success:focus,.is-success.is-focused.textarea,.is-success.is-focused.input,#documenter .docs-sidebar form.docs-search>input.is-focused,.is-success.textarea:active,.is-success.input:active,#documenter .docs-sidebar form.docs-search>input.is-success:active,.is-success.is-active.textarea,.is-success.is-active.input,#documenter .docs-sidebar form.docs-search>input.is-active{box-shadow:0 0 0 0.125em rgba(34,195,91,0.25)}.is-warning.textarea,.is-warning.input,#documenter .docs-sidebar form.docs-search>input.is-warning{border-color:#ffdd57}.is-warning.textarea:focus,.is-warning.input:focus,#documenter .docs-sidebar form.docs-search>input.is-warning:focus,.is-warning.is-focused.textarea,.is-warning.is-focused.input,#documenter .docs-sidebar form.docs-search>input.is-focused,.is-warning.textarea:active,.is-warning.input:active,#documenter .docs-sidebar form.docs-search>input.is-warning:active,.is-warning.is-active.textarea,.is-warning.is-active.input,#documenter .docs-sidebar form.docs-search>input.is-active{box-shadow:0 0 0 0.125em rgba(255,221,87,0.25)}.is-danger.textarea,.is-danger.input,#documenter .docs-sidebar form.docs-search>input.is-danger{border-color:#da0b00}.is-danger.textarea:focus,.is-danger.input:focus,#documenter .docs-sidebar form.docs-search>input.is-danger:focus,.is-danger.is-focused.textarea,.is-danger.is-focused.input,#documenter .docs-sidebar form.docs-search>input.is-focused,.is-danger.textarea:active,.is-danger.input:active,#documenter .docs-sidebar form.docs-search>input.is-danger:active,.is-danger.is-active.textarea,.is-danger.is-active.input,#documenter .docs-sidebar form.docs-search>input.is-active{box-shadow:0 0 0 0.125em rgba(218,11,0,0.25)}.is-small.textarea,.is-small.input,#documenter .docs-sidebar form.docs-search>input{border-radius:2px;font-size:.75rem}.is-medium.textarea,.is-medium.input,#documenter .docs-sidebar form.docs-search>input.is-medium{font-size:1.25rem}.is-large.textarea,.is-large.input,#documenter .docs-sidebar form.docs-search>input.is-large{font-size:1.5rem}.is-fullwidth.textarea,.is-fullwidth.input,#documenter .docs-sidebar form.docs-search>input.is-fullwidth{display:block;width:100%}.is-inline.textarea,.is-inline.input,#documenter .docs-sidebar form.docs-search>input.is-inline{display:inline;width:auto}.input.is-rounded,#documenter .docs-sidebar form.docs-search>input{border-radius:9999px;padding-left:calc(calc(0.75em - 1px) + 0.375em);padding-right:calc(calc(0.75em - 1px) + 0.375em)}.input.is-static,#documenter .docs-sidebar form.docs-search>input.is-static{background-color:transparent;border-color:transparent;box-shadow:none;padding-left:0;padding-right:0}.textarea{display:block;max-width:100%;min-width:100%;padding:calc(0.75em - 1px);resize:vertical}.textarea:not([rows]){max-height:40em;min-height:8em}.textarea[rows]{height:initial}.textarea.has-fixed-size{resize:none}.radio,.checkbox{cursor:pointer;display:inline-block;line-height:1.25;position:relative}.radio input,.checkbox input{cursor:pointer}.radio:hover,.checkbox:hover{color:#222}.radio[disabled],.checkbox[disabled],fieldset[disabled] .radio,fieldset[disabled] .checkbox,.radio input[disabled],.checkbox input[disabled]{color:#6b6b6b;cursor:not-allowed}.radio+.radio{margin-left:.5em}.select{display:inline-block;max-width:100%;position:relative;vertical-align:top}.select:not(.is-multiple){height:2.5em}.select:not(.is-multiple):not(.is-loading)::after{border-color:#2e63b8;right:1.125em;z-index:4}.select.is-rounded select,#documenter .docs-sidebar form.docs-search>input.select select{border-radius:9999px;padding-left:1em}.select select{cursor:pointer;display:block;font-size:1em;max-width:100%;outline:none}.select select::-ms-expand{display:none}.select select[disabled]:hover,fieldset[disabled] .select select:hover{border-color:#f5f5f5}.select select:not([multiple]){padding-right:2.5em}.select select[multiple]{height:auto;padding:0}.select select[multiple] option{padding:0.5em 1em}.select:not(.is-multiple):not(.is-loading):hover::after{border-color:#222}.select.is-white:not(:hover)::after{border-color:#fff}.select.is-white select{border-color:#fff}.select.is-white select:hover,.select.is-white select.is-hovered{border-color:#f2f2f2}.select.is-white select:focus,.select.is-white select.is-focused,.select.is-white select:active,.select.is-white select.is-active{box-shadow:0 0 0 0.125em rgba(255,255,255,0.25)}.select.is-black:not(:hover)::after{border-color:#0a0a0a}.select.is-black select{border-color:#0a0a0a}.select.is-black select:hover,.select.is-black select.is-hovered{border-color:#000}.select.is-black select:focus,.select.is-black select.is-focused,.select.is-black select:active,.select.is-black select.is-active{box-shadow:0 0 0 0.125em rgba(10,10,10,0.25)}.select.is-light:not(:hover)::after{border-color:#f5f5f5}.select.is-light select{border-color:#f5f5f5}.select.is-light select:hover,.select.is-light select.is-hovered{border-color:#e8e8e8}.select.is-light select:focus,.select.is-light select.is-focused,.select.is-light select:active,.select.is-light select.is-active{box-shadow:0 0 0 0.125em rgba(245,245,245,0.25)}.select.is-dark:not(:hover)::after,.content kbd.select:not(:hover)::after{border-color:#363636}.select.is-dark select,.content kbd.select select{border-color:#363636}.select.is-dark select:hover,.content kbd.select select:hover,.select.is-dark select.is-hovered,.content kbd.select select.is-hovered{border-color:#292929}.select.is-dark select:focus,.content kbd.select select:focus,.select.is-dark select.is-focused,.content kbd.select select.is-focused,.select.is-dark select:active,.content kbd.select select:active,.select.is-dark select.is-active,.content kbd.select select.is-active{box-shadow:0 0 0 0.125em rgba(54,54,54,0.25)}.select.is-primary:not(:hover)::after,.docstring>section>a.select.docs-sourcelink:not(:hover)::after{border-color:#4eb5de}.select.is-primary select,.docstring>section>a.select.docs-sourcelink select{border-color:#4eb5de}.select.is-primary select:hover,.docstring>section>a.select.docs-sourcelink select:hover,.select.is-primary select.is-hovered,.docstring>section>a.select.docs-sourcelink select.is-hovered{border-color:#39acda}.select.is-primary select:focus,.docstring>section>a.select.docs-sourcelink select:focus,.select.is-primary select.is-focused,.docstring>section>a.select.docs-sourcelink select.is-focused,.select.is-primary select:active,.docstring>section>a.select.docs-sourcelink select:active,.select.is-primary select.is-active,.docstring>section>a.select.docs-sourcelink select.is-active{box-shadow:0 0 0 0.125em rgba(78,181,222,0.25)}.select.is-link:not(:hover)::after{border-color:#2e63b8}.select.is-link select{border-color:#2e63b8}.select.is-link select:hover,.select.is-link select.is-hovered{border-color:#2958a4}.select.is-link select:focus,.select.is-link select.is-focused,.select.is-link select:active,.select.is-link select.is-active{box-shadow:0 0 0 0.125em rgba(46,99,184,0.25)}.select.is-info:not(:hover)::after{border-color:#209cee}.select.is-info select{border-color:#209cee}.select.is-info select:hover,.select.is-info select.is-hovered{border-color:#1190e3}.select.is-info select:focus,.select.is-info select.is-focused,.select.is-info select:active,.select.is-info select.is-active{box-shadow:0 0 0 0.125em rgba(32,156,238,0.25)}.select.is-success:not(:hover)::after{border-color:#22c35b}.select.is-success select{border-color:#22c35b}.select.is-success select:hover,.select.is-success select.is-hovered{border-color:#1ead51}.select.is-success select:focus,.select.is-success select.is-focused,.select.is-success select:active,.select.is-success select.is-active{box-shadow:0 0 0 0.125em rgba(34,195,91,0.25)}.select.is-warning:not(:hover)::after{border-color:#ffdd57}.select.is-warning select{border-color:#ffdd57}.select.is-warning select:hover,.select.is-warning select.is-hovered{border-color:#ffd83e}.select.is-warning select:focus,.select.is-warning select.is-focused,.select.is-warning select:active,.select.is-warning select.is-active{box-shadow:0 0 0 0.125em rgba(255,221,87,0.25)}.select.is-danger:not(:hover)::after{border-color:#da0b00}.select.is-danger select{border-color:#da0b00}.select.is-danger select:hover,.select.is-danger select.is-hovered{border-color:#c10a00}.select.is-danger select:focus,.select.is-danger select.is-focused,.select.is-danger select:active,.select.is-danger select.is-active{box-shadow:0 0 0 0.125em rgba(218,11,0,0.25)}.select.is-small,#documenter .docs-sidebar form.docs-search>input.select{border-radius:2px;font-size:.75rem}.select.is-medium{font-size:1.25rem}.select.is-large{font-size:1.5rem}.select.is-disabled::after{border-color:#6b6b6b !important;opacity:0.5}.select.is-fullwidth{width:100%}.select.is-fullwidth select{width:100%}.select.is-loading::after{margin-top:0;position:absolute;right:.625em;top:0.625em;transform:none}.select.is-loading.is-small:after,#documenter .docs-sidebar form.docs-search>input.is-loading:after{font-size:.75rem}.select.is-loading.is-medium:after{font-size:1.25rem}.select.is-loading.is-large:after{font-size:1.5rem}.file{align-items:stretch;display:flex;justify-content:flex-start;position:relative}.file.is-white .file-cta{background-color:#fff;border-color:transparent;color:#0a0a0a}.file.is-white:hover .file-cta,.file.is-white.is-hovered .file-cta{background-color:#f9f9f9;border-color:transparent;color:#0a0a0a}.file.is-white:focus .file-cta,.file.is-white.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(255,255,255,0.25);color:#0a0a0a}.file.is-white:active .file-cta,.file.is-white.is-active .file-cta{background-color:#f2f2f2;border-color:transparent;color:#0a0a0a}.file.is-black .file-cta{background-color:#0a0a0a;border-color:transparent;color:#fff}.file.is-black:hover .file-cta,.file.is-black.is-hovered .file-cta{background-color:#040404;border-color:transparent;color:#fff}.file.is-black:focus .file-cta,.file.is-black.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(10,10,10,0.25);color:#fff}.file.is-black:active .file-cta,.file.is-black.is-active .file-cta{background-color:#000;border-color:transparent;color:#fff}.file.is-light .file-cta{background-color:#f5f5f5;border-color:transparent;color:rgba(0,0,0,0.7)}.file.is-light:hover .file-cta,.file.is-light.is-hovered .file-cta{background-color:#eee;border-color:transparent;color:rgba(0,0,0,0.7)}.file.is-light:focus .file-cta,.file.is-light.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(245,245,245,0.25);color:rgba(0,0,0,0.7)}.file.is-light:active .file-cta,.file.is-light.is-active .file-cta{background-color:#e8e8e8;border-color:transparent;color:rgba(0,0,0,0.7)}.file.is-dark .file-cta,.content kbd.file .file-cta{background-color:#363636;border-color:transparent;color:#fff}.file.is-dark:hover .file-cta,.content kbd.file:hover .file-cta,.file.is-dark.is-hovered .file-cta,.content kbd.file.is-hovered .file-cta{background-color:#2f2f2f;border-color:transparent;color:#fff}.file.is-dark:focus .file-cta,.content kbd.file:focus .file-cta,.file.is-dark.is-focused .file-cta,.content kbd.file.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(54,54,54,0.25);color:#fff}.file.is-dark:active .file-cta,.content kbd.file:active .file-cta,.file.is-dark.is-active .file-cta,.content kbd.file.is-active .file-cta{background-color:#292929;border-color:transparent;color:#fff}.file.is-primary .file-cta,.docstring>section>a.file.docs-sourcelink .file-cta{background-color:#4eb5de;border-color:transparent;color:#fff}.file.is-primary:hover .file-cta,.docstring>section>a.file.docs-sourcelink:hover .file-cta,.file.is-primary.is-hovered .file-cta,.docstring>section>a.file.is-hovered.docs-sourcelink .file-cta{background-color:#43b1dc;border-color:transparent;color:#fff}.file.is-primary:focus .file-cta,.docstring>section>a.file.docs-sourcelink:focus .file-cta,.file.is-primary.is-focused .file-cta,.docstring>section>a.file.is-focused.docs-sourcelink .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(78,181,222,0.25);color:#fff}.file.is-primary:active .file-cta,.docstring>section>a.file.docs-sourcelink:active .file-cta,.file.is-primary.is-active .file-cta,.docstring>section>a.file.is-active.docs-sourcelink .file-cta{background-color:#39acda;border-color:transparent;color:#fff}.file.is-link .file-cta{background-color:#2e63b8;border-color:transparent;color:#fff}.file.is-link:hover .file-cta,.file.is-link.is-hovered .file-cta{background-color:#2b5eae;border-color:transparent;color:#fff}.file.is-link:focus .file-cta,.file.is-link.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(46,99,184,0.25);color:#fff}.file.is-link:active .file-cta,.file.is-link.is-active .file-cta{background-color:#2958a4;border-color:transparent;color:#fff}.file.is-info .file-cta{background-color:#209cee;border-color:transparent;color:#fff}.file.is-info:hover .file-cta,.file.is-info.is-hovered .file-cta{background-color:#1497ed;border-color:transparent;color:#fff}.file.is-info:focus .file-cta,.file.is-info.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(32,156,238,0.25);color:#fff}.file.is-info:active .file-cta,.file.is-info.is-active .file-cta{background-color:#1190e3;border-color:transparent;color:#fff}.file.is-success .file-cta{background-color:#22c35b;border-color:transparent;color:#fff}.file.is-success:hover .file-cta,.file.is-success.is-hovered .file-cta{background-color:#20b856;border-color:transparent;color:#fff}.file.is-success:focus .file-cta,.file.is-success.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(34,195,91,0.25);color:#fff}.file.is-success:active .file-cta,.file.is-success.is-active .file-cta{background-color:#1ead51;border-color:transparent;color:#fff}.file.is-warning .file-cta{background-color:#ffdd57;border-color:transparent;color:rgba(0,0,0,0.7)}.file.is-warning:hover .file-cta,.file.is-warning.is-hovered .file-cta{background-color:#ffda4a;border-color:transparent;color:rgba(0,0,0,0.7)}.file.is-warning:focus .file-cta,.file.is-warning.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(255,221,87,0.25);color:rgba(0,0,0,0.7)}.file.is-warning:active .file-cta,.file.is-warning.is-active .file-cta{background-color:#ffd83e;border-color:transparent;color:rgba(0,0,0,0.7)}.file.is-danger .file-cta{background-color:#da0b00;border-color:transparent;color:#fff}.file.is-danger:hover .file-cta,.file.is-danger.is-hovered .file-cta{background-color:#cd0a00;border-color:transparent;color:#fff}.file.is-danger:focus .file-cta,.file.is-danger.is-focused .file-cta{border-color:transparent;box-shadow:0 0 0.5em rgba(218,11,0,0.25);color:#fff}.file.is-danger:active .file-cta,.file.is-danger.is-active .file-cta{background-color:#c10a00;border-color:transparent;color:#fff}.file.is-small,#documenter .docs-sidebar form.docs-search>input.file{font-size:.75rem}.file.is-normal{font-size:1rem}.file.is-medium{font-size:1.25rem}.file.is-medium .file-icon .fa{font-size:21px}.file.is-large{font-size:1.5rem}.file.is-large .file-icon .fa{font-size:28px}.file.has-name .file-cta{border-bottom-right-radius:0;border-top-right-radius:0}.file.has-name .file-name{border-bottom-left-radius:0;border-top-left-radius:0}.file.has-name.is-empty .file-cta{border-radius:4px}.file.has-name.is-empty .file-name{display:none}.file.is-boxed .file-label{flex-direction:column}.file.is-boxed .file-cta{flex-direction:column;height:auto;padding:1em 3em}.file.is-boxed .file-name{border-width:0 1px 1px}.file.is-boxed .file-icon{height:1.5em;width:1.5em}.file.is-boxed .file-icon .fa{font-size:21px}.file.is-boxed.is-small .file-icon .fa,#documenter .docs-sidebar form.docs-search>input.is-boxed .file-icon .fa{font-size:14px}.file.is-boxed.is-medium .file-icon .fa{font-size:28px}.file.is-boxed.is-large .file-icon .fa{font-size:35px}.file.is-boxed.has-name .file-cta{border-radius:4px 4px 0 0}.file.is-boxed.has-name .file-name{border-radius:0 0 4px 4px;border-width:0 1px 1px}.file.is-centered{justify-content:center}.file.is-fullwidth .file-label{width:100%}.file.is-fullwidth .file-name{flex-grow:1;max-width:none}.file.is-right{justify-content:flex-end}.file.is-right .file-cta{border-radius:0 4px 4px 0}.file.is-right .file-name{border-radius:4px 0 0 4px;border-width:1px 0 1px 1px;order:-1}.file-label{align-items:stretch;display:flex;cursor:pointer;justify-content:flex-start;overflow:hidden;position:relative}.file-label:hover .file-cta{background-color:#eee;color:#222}.file-label:hover .file-name{border-color:#d5d5d5}.file-label:active .file-cta{background-color:#e8e8e8;color:#222}.file-label:active .file-name{border-color:#cfcfcf}.file-input{height:100%;left:0;opacity:0;outline:none;position:absolute;top:0;width:100%}.file-cta,.file-name{border-color:#dbdbdb;border-radius:4px;font-size:1em;padding-left:1em;padding-right:1em;white-space:nowrap}.file-cta{background-color:#f5f5f5;color:#222}.file-name{border-color:#dbdbdb;border-style:solid;border-width:1px 1px 1px 0;display:block;max-width:16em;overflow:hidden;text-align:inherit;text-overflow:ellipsis}.file-icon{align-items:center;display:flex;height:1em;justify-content:center;margin-right:.5em;width:1em}.file-icon .fa{font-size:14px}.label{color:#222;display:block;font-size:1rem;font-weight:700}.label:not(:last-child){margin-bottom:0.5em}.label.is-small,#documenter .docs-sidebar form.docs-search>input.label{font-size:.75rem}.label.is-medium{font-size:1.25rem}.label.is-large{font-size:1.5rem}.help{display:block;font-size:.75rem;margin-top:0.25rem}.help.is-white{color:#fff}.help.is-black{color:#0a0a0a}.help.is-light{color:#f5f5f5}.help.is-dark,.content kbd.help{color:#363636}.help.is-primary,.docstring>section>a.help.docs-sourcelink{color:#4eb5de}.help.is-link{color:#2e63b8}.help.is-info{color:#209cee}.help.is-success{color:#22c35b}.help.is-warning{color:#ffdd57}.help.is-danger{color:#da0b00}.field:not(:last-child){margin-bottom:0.75rem}.field.has-addons{display:flex;justify-content:flex-start}.field.has-addons .control:not(:last-child){margin-right:-1px}.field.has-addons .control:not(:first-child):not(:last-child) .button,.field.has-addons .control:not(:first-child):not(:last-child) .input,.field.has-addons .control:not(:first-child):not(:last-child) #documenter .docs-sidebar form.docs-search>input,#documenter .docs-sidebar .field.has-addons .control:not(:first-child):not(:last-child) form.docs-search>input,.field.has-addons .control:not(:first-child):not(:last-child) .select select{border-radius:0}.field.has-addons .control:first-child:not(:only-child) .button,.field.has-addons .control:first-child:not(:only-child) .input,.field.has-addons .control:first-child:not(:only-child) #documenter .docs-sidebar form.docs-search>input,#documenter .docs-sidebar .field.has-addons .control:first-child:not(:only-child) form.docs-search>input,.field.has-addons .control:first-child:not(:only-child) .select select{border-bottom-right-radius:0;border-top-right-radius:0}.field.has-addons .control:last-child:not(:only-child) .button,.field.has-addons .control:last-child:not(:only-child) .input,.field.has-addons .control:last-child:not(:only-child) #documenter .docs-sidebar form.docs-search>input,#documenter .docs-sidebar .field.has-addons .control:last-child:not(:only-child) form.docs-search>input,.field.has-addons .control:last-child:not(:only-child) .select select{border-bottom-left-radius:0;border-top-left-radius:0}.field.has-addons .control .button:not([disabled]):hover,.field.has-addons .control .button.is-hovered:not([disabled]),.field.has-addons .control .input:not([disabled]):hover,.field.has-addons .control #documenter .docs-sidebar form.docs-search>input:not([disabled]):hover,#documenter .docs-sidebar .field.has-addons .control form.docs-search>input:not([disabled]):hover,.field.has-addons .control .input.is-hovered:not([disabled]),.field.has-addons .control #documenter .docs-sidebar form.docs-search>input.is-hovered:not([disabled]),#documenter .docs-sidebar .field.has-addons .control form.docs-search>input.is-hovered:not([disabled]),.field.has-addons .control .select select:not([disabled]):hover,.field.has-addons .control .select select.is-hovered:not([disabled]){z-index:2}.field.has-addons .control .button:not([disabled]):focus,.field.has-addons .control .button.is-focused:not([disabled]),.field.has-addons .control .button:not([disabled]):active,.field.has-addons .control .button.is-active:not([disabled]),.field.has-addons .control .input:not([disabled]):focus,.field.has-addons .control #documenter .docs-sidebar form.docs-search>input:not([disabled]):focus,#documenter .docs-sidebar .field.has-addons .control form.docs-search>input:not([disabled]):focus,.field.has-addons .control .input.is-focused:not([disabled]),.field.has-addons .control #documenter .docs-sidebar form.docs-search>input.is-focused:not([disabled]),#documenter .docs-sidebar .field.has-addons .control form.docs-search>input.is-focused:not([disabled]),.field.has-addons .control .input:not([disabled]):active,.field.has-addons .control #documenter .docs-sidebar form.docs-search>input:not([disabled]):active,#documenter .docs-sidebar .field.has-addons .control form.docs-search>input:not([disabled]):active,.field.has-addons .control .input.is-active:not([disabled]),.field.has-addons .control #documenter .docs-sidebar form.docs-search>input.is-active:not([disabled]),#documenter .docs-sidebar .field.has-addons .control form.docs-search>input.is-active:not([disabled]),.field.has-addons .control .select select:not([disabled]):focus,.field.has-addons .control .select select.is-focused:not([disabled]),.field.has-addons .control .select select:not([disabled]):active,.field.has-addons .control .select select.is-active:not([disabled]){z-index:3}.field.has-addons .control .button:not([disabled]):focus:hover,.field.has-addons .control .button.is-focused:not([disabled]):hover,.field.has-addons .control .button:not([disabled]):active:hover,.field.has-addons .control .button.is-active:not([disabled]):hover,.field.has-addons .control .input:not([disabled]):focus:hover,.field.has-addons .control #documenter .docs-sidebar form.docs-search>input:not([disabled]):focus:hover,#documenter .docs-sidebar .field.has-addons .control form.docs-search>input:not([disabled]):focus:hover,.field.has-addons .control .input.is-focused:not([disabled]):hover,.field.has-addons .control #documenter .docs-sidebar form.docs-search>input.is-focused:not([disabled]):hover,#documenter .docs-sidebar .field.has-addons .control form.docs-search>input.is-focused:not([disabled]):hover,.field.has-addons .control .input:not([disabled]):active:hover,.field.has-addons .control #documenter .docs-sidebar form.docs-search>input:not([disabled]):active:hover,#documenter .docs-sidebar .field.has-addons .control form.docs-search>input:not([disabled]):active:hover,.field.has-addons .control .input.is-active:not([disabled]):hover,.field.has-addons .control #documenter .docs-sidebar form.docs-search>input.is-active:not([disabled]):hover,#documenter .docs-sidebar .field.has-addons .control form.docs-search>input.is-active:not([disabled]):hover,.field.has-addons .control .select select:not([disabled]):focus:hover,.field.has-addons .control .select select.is-focused:not([disabled]):hover,.field.has-addons .control .select select:not([disabled]):active:hover,.field.has-addons .control .select select.is-active:not([disabled]):hover{z-index:4}.field.has-addons .control.is-expanded{flex-grow:1;flex-shrink:1}.field.has-addons.has-addons-centered{justify-content:center}.field.has-addons.has-addons-right{justify-content:flex-end}.field.has-addons.has-addons-fullwidth .control{flex-grow:1;flex-shrink:0}.field.is-grouped{display:flex;justify-content:flex-start}.field.is-grouped>.control{flex-shrink:0}.field.is-grouped>.control:not(:last-child){margin-bottom:0;margin-right:.75rem}.field.is-grouped>.control.is-expanded{flex-grow:1;flex-shrink:1}.field.is-grouped.is-grouped-centered{justify-content:center}.field.is-grouped.is-grouped-right{justify-content:flex-end}.field.is-grouped.is-grouped-multiline{flex-wrap:wrap}.field.is-grouped.is-grouped-multiline>.control:last-child,.field.is-grouped.is-grouped-multiline>.control:not(:last-child){margin-bottom:0.75rem}.field.is-grouped.is-grouped-multiline:last-child{margin-bottom:-0.75rem}.field.is-grouped.is-grouped-multiline:not(:last-child){margin-bottom:0}@media screen and (min-width: 769px),print{.field.is-horizontal{display:flex}}.field-label .label{font-size:inherit}@media screen and (max-width: 768px){.field-label{margin-bottom:0.5rem}}@media screen and (min-width: 769px),print{.field-label{flex-basis:0;flex-grow:1;flex-shrink:0;margin-right:1.5rem;text-align:right}.field-label.is-small,#documenter .docs-sidebar form.docs-search>input.field-label{font-size:.75rem;padding-top:0.375em}.field-label.is-normal{padding-top:0.375em}.field-label.is-medium{font-size:1.25rem;padding-top:0.375em}.field-label.is-large{font-size:1.5rem;padding-top:0.375em}}.field-body .field .field{margin-bottom:0}@media screen and (min-width: 769px),print{.field-body{display:flex;flex-basis:0;flex-grow:5;flex-shrink:1}.field-body .field{margin-bottom:0}.field-body>.field{flex-shrink:1}.field-body>.field:not(.is-narrow){flex-grow:1}.field-body>.field:not(:last-child){margin-right:.75rem}}.control{box-sizing:border-box;clear:both;font-size:1rem;position:relative;text-align:inherit}.control.has-icons-left .input:focus~.icon,.control.has-icons-left #documenter .docs-sidebar form.docs-search>input:focus~.icon,#documenter .docs-sidebar .control.has-icons-left form.docs-search>input:focus~.icon,.control.has-icons-left .select:focus~.icon,.control.has-icons-right .input:focus~.icon,.control.has-icons-right #documenter .docs-sidebar form.docs-search>input:focus~.icon,#documenter .docs-sidebar .control.has-icons-right form.docs-search>input:focus~.icon,.control.has-icons-right .select:focus~.icon{color:#222}.control.has-icons-left .input.is-small~.icon,.control.has-icons-left #documenter .docs-sidebar form.docs-search>input~.icon,#documenter .docs-sidebar .control.has-icons-left form.docs-search>input~.icon,.control.has-icons-left .select.is-small~.icon,.control.has-icons-right .input.is-small~.icon,.control.has-icons-right #documenter .docs-sidebar form.docs-search>input~.icon,#documenter .docs-sidebar .control.has-icons-right form.docs-search>input~.icon,.control.has-icons-right .select.is-small~.icon{font-size:.75rem}.control.has-icons-left .input.is-medium~.icon,.control.has-icons-left #documenter .docs-sidebar form.docs-search>input.is-medium~.icon,#documenter .docs-sidebar .control.has-icons-left form.docs-search>input.is-medium~.icon,.control.has-icons-left .select.is-medium~.icon,.control.has-icons-right .input.is-medium~.icon,.control.has-icons-right #documenter .docs-sidebar form.docs-search>input.is-medium~.icon,#documenter .docs-sidebar .control.has-icons-right form.docs-search>input.is-medium~.icon,.control.has-icons-right .select.is-medium~.icon{font-size:1.25rem}.control.has-icons-left .input.is-large~.icon,.control.has-icons-left #documenter .docs-sidebar form.docs-search>input.is-large~.icon,#documenter .docs-sidebar .control.has-icons-left form.docs-search>input.is-large~.icon,.control.has-icons-left .select.is-large~.icon,.control.has-icons-right .input.is-large~.icon,.control.has-icons-right #documenter .docs-sidebar form.docs-search>input.is-large~.icon,#documenter .docs-sidebar .control.has-icons-right form.docs-search>input.is-large~.icon,.control.has-icons-right .select.is-large~.icon{font-size:1.5rem}.control.has-icons-left .icon,.control.has-icons-right .icon{color:#dbdbdb;height:2.5em;pointer-events:none;position:absolute;top:0;width:2.5em;z-index:4}.control.has-icons-left .input,.control.has-icons-left #documenter .docs-sidebar form.docs-search>input,#documenter .docs-sidebar .control.has-icons-left form.docs-search>input,.control.has-icons-left .select select{padding-left:2.5em}.control.has-icons-left .icon.is-left{left:0}.control.has-icons-right .input,.control.has-icons-right #documenter .docs-sidebar form.docs-search>input,#documenter .docs-sidebar .control.has-icons-right form.docs-search>input,.control.has-icons-right .select select{padding-right:2.5em}.control.has-icons-right .icon.is-right{right:0}.control.is-loading::after{position:absolute !important;right:.625em;top:0.625em;z-index:4}.control.is-loading.is-small:after,#documenter .docs-sidebar form.docs-search>input.is-loading:after{font-size:.75rem}.control.is-loading.is-medium:after{font-size:1.25rem}.control.is-loading.is-large:after{font-size:1.5rem}.breadcrumb{font-size:1rem;white-space:nowrap}.breadcrumb a{align-items:center;color:#2e63b8;display:flex;justify-content:center;padding:0 .75em}.breadcrumb a:hover{color:#363636}.breadcrumb li{align-items:center;display:flex}.breadcrumb li:first-child a{padding-left:0}.breadcrumb li.is-active a{color:#222;cursor:default;pointer-events:none}.breadcrumb li+li::before{color:#b5b5b5;content:"\0002f"}.breadcrumb ul,.breadcrumb ol{align-items:flex-start;display:flex;flex-wrap:wrap;justify-content:flex-start}.breadcrumb .icon:first-child{margin-right:.5em}.breadcrumb .icon:last-child{margin-left:.5em}.breadcrumb.is-centered ol,.breadcrumb.is-centered ul{justify-content:center}.breadcrumb.is-right ol,.breadcrumb.is-right ul{justify-content:flex-end}.breadcrumb.is-small,#documenter .docs-sidebar form.docs-search>input.breadcrumb{font-size:.75rem}.breadcrumb.is-medium{font-size:1.25rem}.breadcrumb.is-large{font-size:1.5rem}.breadcrumb.has-arrow-separator li+li::before{content:"\02192"}.breadcrumb.has-bullet-separator li+li::before{content:"\02022"}.breadcrumb.has-dot-separator li+li::before{content:"\000b7"}.breadcrumb.has-succeeds-separator li+li::before{content:"\0227B"}.card{background-color:#fff;border-radius:.25rem;box-shadow:#bbb;color:#222;max-width:100%;position:relative}.card-footer:first-child,.card-content:first-child,.card-header:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card-footer:last-child,.card-content:last-child,.card-header:last-child{border-bottom-left-radius:.25rem;border-bottom-right-radius:.25rem}.card-header{background-color:rgba(0,0,0,0);align-items:stretch;box-shadow:0 0.125em 0.25em rgba(10,10,10,0.1);display:flex}.card-header-title{align-items:center;color:#222;display:flex;flex-grow:1;font-weight:700;padding:0.75rem 1rem}.card-header-title.is-centered{justify-content:center}.card-header-icon{-moz-appearance:none;-webkit-appearance:none;appearance:none;background:none;border:none;color:currentColor;font-family:inherit;font-size:1em;margin:0;padding:0;align-items:center;cursor:pointer;display:flex;justify-content:center;padding:0.75rem 1rem}.card-image{display:block;position:relative}.card-image:first-child img{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card-image:last-child img{border-bottom-left-radius:.25rem;border-bottom-right-radius:.25rem}.card-content{background-color:rgba(0,0,0,0);padding:1.5rem}.card-footer{background-color:rgba(0,0,0,0);border-top:1px solid #ededed;align-items:stretch;display:flex}.card-footer-item{align-items:center;display:flex;flex-basis:0;flex-grow:1;flex-shrink:0;justify-content:center;padding:.75rem}.card-footer-item:not(:last-child){border-right:1px solid #ededed}.card .media:not(:last-child){margin-bottom:1.5rem}.dropdown{display:inline-flex;position:relative;vertical-align:top}.dropdown.is-active .dropdown-menu,.dropdown.is-hoverable:hover .dropdown-menu{display:block}.dropdown.is-right .dropdown-menu{left:auto;right:0}.dropdown.is-up .dropdown-menu{bottom:100%;padding-bottom:4px;padding-top:initial;top:auto}.dropdown-menu{display:none;left:0;min-width:12rem;padding-top:4px;position:absolute;top:100%;z-index:20}.dropdown-content{background-color:#fff;border-radius:4px;box-shadow:#bbb;padding-bottom:.5rem;padding-top:.5rem}.dropdown-item{color:#222;display:block;font-size:0.875rem;line-height:1.5;padding:0.375rem 1rem;position:relative}a.dropdown-item,button.dropdown-item{padding-right:3rem;text-align:inherit;white-space:nowrap;width:100%}a.dropdown-item:hover,button.dropdown-item:hover{background-color:#f5f5f5;color:#0a0a0a}a.dropdown-item.is-active,button.dropdown-item.is-active{background-color:#2e63b8;color:#fff}.dropdown-divider{background-color:#ededed;border:none;display:block;height:1px;margin:0.5rem 0}.level{align-items:center;justify-content:space-between}.level code{border-radius:4px}.level img{display:inline-block;vertical-align:top}.level.is-mobile{display:flex}.level.is-mobile .level-left,.level.is-mobile .level-right{display:flex}.level.is-mobile .level-left+.level-right{margin-top:0}.level.is-mobile .level-item:not(:last-child){margin-bottom:0;margin-right:.75rem}.level.is-mobile .level-item:not(.is-narrow){flex-grow:1}@media screen and (min-width: 769px),print{.level{display:flex}.level>.level-item:not(.is-narrow){flex-grow:1}}.level-item{align-items:center;display:flex;flex-basis:auto;flex-grow:0;flex-shrink:0;justify-content:center}.level-item .title,.level-item .subtitle{margin-bottom:0}@media screen and (max-width: 768px){.level-item:not(:last-child){margin-bottom:.75rem}}.level-left,.level-right{flex-basis:auto;flex-grow:0;flex-shrink:0}.level-left .level-item.is-flexible,.level-right .level-item.is-flexible{flex-grow:1}@media screen and (min-width: 769px),print{.level-left .level-item:not(:last-child),.level-right .level-item:not(:last-child){margin-right:.75rem}}.level-left{align-items:center;justify-content:flex-start}@media screen and (max-width: 768px){.level-left+.level-right{margin-top:1.5rem}}@media screen and (min-width: 769px),print{.level-left{display:flex}}.level-right{align-items:center;justify-content:flex-end}@media screen and (min-width: 769px),print{.level-right{display:flex}}.media{align-items:flex-start;display:flex;text-align:inherit}.media .content:not(:last-child){margin-bottom:.75rem}.media .media{border-top:1px solid rgba(219,219,219,0.5);display:flex;padding-top:.75rem}.media .media .content:not(:last-child),.media .media .control:not(:last-child){margin-bottom:.5rem}.media .media .media{padding-top:.5rem}.media .media .media+.media{margin-top:.5rem}.media+.media{border-top:1px solid rgba(219,219,219,0.5);margin-top:1rem;padding-top:1rem}.media.is-large+.media{margin-top:1.5rem;padding-top:1.5rem}.media-left,.media-right{flex-basis:auto;flex-grow:0;flex-shrink:0}.media-left{margin-right:1rem}.media-right{margin-left:1rem}.media-content{flex-basis:auto;flex-grow:1;flex-shrink:1;text-align:inherit}@media screen and (max-width: 768px){.media-content{overflow-x:auto}}.menu{font-size:1rem}.menu.is-small,#documenter .docs-sidebar form.docs-search>input.menu{font-size:.75rem}.menu.is-medium{font-size:1.25rem}.menu.is-large{font-size:1.5rem}.menu-list{line-height:1.25}.menu-list a{border-radius:2px;color:#222;display:block;padding:0.5em 0.75em}.menu-list a:hover{background-color:#f5f5f5;color:#222}.menu-list a.is-active{background-color:#2e63b8;color:#fff}.menu-list li ul{border-left:1px solid #dbdbdb;margin:.75em;padding-left:.75em}.menu-label{color:#6b6b6b;font-size:.75em;letter-spacing:.1em;text-transform:uppercase}.menu-label:not(:first-child){margin-top:1em}.menu-label:not(:last-child){margin-bottom:1em}.message{background-color:#f5f5f5;border-radius:4px;font-size:1rem}.message strong{color:currentColor}.message a:not(.button):not(.tag):not(.dropdown-item){color:currentColor;text-decoration:underline}.message.is-small,#documenter .docs-sidebar form.docs-search>input.message{font-size:.75rem}.message.is-medium{font-size:1.25rem}.message.is-large{font-size:1.5rem}.message.is-white{background-color:#fff}.message.is-white .message-header{background-color:#fff;color:#0a0a0a}.message.is-white .message-body{border-color:#fff}.message.is-black{background-color:#fafafa}.message.is-black .message-header{background-color:#0a0a0a;color:#fff}.message.is-black .message-body{border-color:#0a0a0a}.message.is-light{background-color:#fafafa}.message.is-light .message-header{background-color:#f5f5f5;color:rgba(0,0,0,0.7)}.message.is-light .message-body{border-color:#f5f5f5}.message.is-dark,.content kbd.message{background-color:#fafafa}.message.is-dark .message-header,.content kbd.message .message-header{background-color:#363636;color:#fff}.message.is-dark .message-body,.content kbd.message .message-body{border-color:#363636}.message.is-primary,.docstring>section>a.message.docs-sourcelink{background-color:#eef8fc}.message.is-primary .message-header,.docstring>section>a.message.docs-sourcelink .message-header{background-color:#4eb5de;color:#fff}.message.is-primary .message-body,.docstring>section>a.message.docs-sourcelink .message-body{border-color:#4eb5de;color:#1a6d8e}.message.is-link{background-color:#eff3fb}.message.is-link .message-header{background-color:#2e63b8;color:#fff}.message.is-link .message-body{border-color:#2e63b8;color:#3169c4}.message.is-info{background-color:#ecf7fe}.message.is-info .message-header{background-color:#209cee;color:#fff}.message.is-info .message-body{border-color:#209cee;color:#0e72b4}.message.is-success{background-color:#eefcf3}.message.is-success .message-header{background-color:#22c35b;color:#fff}.message.is-success .message-body{border-color:#22c35b;color:#198f43}.message.is-warning{background-color:#fffbeb}.message.is-warning .message-header{background-color:#ffdd57;color:rgba(0,0,0,0.7)}.message.is-warning .message-body{border-color:#ffdd57;color:#947600}.message.is-danger{background-color:#ffeceb}.message.is-danger .message-header{background-color:#da0b00;color:#fff}.message.is-danger .message-body{border-color:#da0b00;color:#f50c00}.message-header{align-items:center;background-color:#222;border-radius:4px 4px 0 0;color:#fff;display:flex;font-weight:700;justify-content:space-between;line-height:1.25;padding:0.75em 1em;position:relative}.message-header .delete{flex-grow:0;flex-shrink:0;margin-left:.75em}.message-header+.message-body{border-width:0;border-top-left-radius:0;border-top-right-radius:0}.message-body{border-color:#dbdbdb;border-radius:4px;border-style:solid;border-width:0 0 0 4px;color:#222;padding:1.25em 1.5em}.message-body code,.message-body pre{background-color:#fff}.message-body pre code{background-color:rgba(0,0,0,0)}.modal{align-items:center;display:none;flex-direction:column;justify-content:center;overflow:hidden;position:fixed;z-index:40}.modal.is-active{display:flex}.modal-background{background-color:rgba(10,10,10,0.86)}.modal-content,.modal-card{margin:0 20px;max-height:calc(100vh - 160px);overflow:auto;position:relative;width:100%}@media screen and (min-width: 769px){.modal-content,.modal-card{margin:0 auto;max-height:calc(100vh - 40px);width:640px}}.modal-close{background:none;height:40px;position:fixed;right:20px;top:20px;width:40px}.modal-card{display:flex;flex-direction:column;max-height:calc(100vh - 40px);overflow:hidden;-ms-overflow-y:visible}.modal-card-head,.modal-card-foot{align-items:center;background-color:#f5f5f5;display:flex;flex-shrink:0;justify-content:flex-start;padding:20px;position:relative}.modal-card-head{border-bottom:1px solid #dbdbdb;border-top-left-radius:6px;border-top-right-radius:6px}.modal-card-title{color:#222;flex-grow:1;flex-shrink:0;font-size:1.5rem;line-height:1}.modal-card-foot{border-bottom-left-radius:6px;border-bottom-right-radius:6px;border-top:1px solid #dbdbdb}.modal-card-foot .button:not(:last-child){margin-right:.5em}.modal-card-body{-webkit-overflow-scrolling:touch;background-color:#fff;flex-grow:1;flex-shrink:1;overflow:auto;padding:20px}.navbar{background-color:#fff;min-height:3.25rem;position:relative;z-index:30}.navbar.is-white{background-color:#fff;color:#0a0a0a}.navbar.is-white .navbar-brand>.navbar-item,.navbar.is-white .navbar-brand .navbar-link{color:#0a0a0a}.navbar.is-white .navbar-brand>a.navbar-item:focus,.navbar.is-white .navbar-brand>a.navbar-item:hover,.navbar.is-white .navbar-brand>a.navbar-item.is-active,.navbar.is-white .navbar-brand .navbar-link:focus,.navbar.is-white .navbar-brand .navbar-link:hover,.navbar.is-white .navbar-brand .navbar-link.is-active{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link::after{border-color:#0a0a0a}.navbar.is-white .navbar-burger{color:#0a0a0a}@media screen and (min-width: 1056px){.navbar.is-white .navbar-start>.navbar-item,.navbar.is-white .navbar-start .navbar-link,.navbar.is-white .navbar-end>.navbar-item,.navbar.is-white .navbar-end .navbar-link{color:#0a0a0a}.navbar.is-white .navbar-start>a.navbar-item:focus,.navbar.is-white .navbar-start>a.navbar-item:hover,.navbar.is-white .navbar-start>a.navbar-item.is-active,.navbar.is-white .navbar-start .navbar-link:focus,.navbar.is-white .navbar-start .navbar-link:hover,.navbar.is-white .navbar-start .navbar-link.is-active,.navbar.is-white .navbar-end>a.navbar-item:focus,.navbar.is-white .navbar-end>a.navbar-item:hover,.navbar.is-white .navbar-end>a.navbar-item.is-active,.navbar.is-white .navbar-end .navbar-link:focus,.navbar.is-white .navbar-end .navbar-link:hover,.navbar.is-white .navbar-end .navbar-link.is-active{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-start .navbar-link::after,.navbar.is-white .navbar-end .navbar-link::after{border-color:#0a0a0a}.navbar.is-white .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-white .navbar-item.has-dropdown:hover .navbar-link,.navbar.is-white .navbar-item.has-dropdown.is-active .navbar-link{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-dropdown a.navbar-item.is-active{background-color:#fff;color:#0a0a0a}}.navbar.is-black{background-color:#0a0a0a;color:#fff}.navbar.is-black .navbar-brand>.navbar-item,.navbar.is-black .navbar-brand .navbar-link{color:#fff}.navbar.is-black .navbar-brand>a.navbar-item:focus,.navbar.is-black .navbar-brand>a.navbar-item:hover,.navbar.is-black .navbar-brand>a.navbar-item.is-active,.navbar.is-black .navbar-brand .navbar-link:focus,.navbar.is-black .navbar-brand .navbar-link:hover,.navbar.is-black .navbar-brand .navbar-link.is-active{background-color:#000;color:#fff}.navbar.is-black .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-black .navbar-burger{color:#fff}@media screen and (min-width: 1056px){.navbar.is-black .navbar-start>.navbar-item,.navbar.is-black .navbar-start .navbar-link,.navbar.is-black .navbar-end>.navbar-item,.navbar.is-black .navbar-end .navbar-link{color:#fff}.navbar.is-black .navbar-start>a.navbar-item:focus,.navbar.is-black .navbar-start>a.navbar-item:hover,.navbar.is-black .navbar-start>a.navbar-item.is-active,.navbar.is-black .navbar-start .navbar-link:focus,.navbar.is-black .navbar-start .navbar-link:hover,.navbar.is-black .navbar-start .navbar-link.is-active,.navbar.is-black .navbar-end>a.navbar-item:focus,.navbar.is-black .navbar-end>a.navbar-item:hover,.navbar.is-black .navbar-end>a.navbar-item.is-active,.navbar.is-black .navbar-end .navbar-link:focus,.navbar.is-black .navbar-end .navbar-link:hover,.navbar.is-black .navbar-end .navbar-link.is-active{background-color:#000;color:#fff}.navbar.is-black .navbar-start .navbar-link::after,.navbar.is-black .navbar-end .navbar-link::after{border-color:#fff}.navbar.is-black .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-black .navbar-item.has-dropdown:hover .navbar-link,.navbar.is-black .navbar-item.has-dropdown.is-active .navbar-link{background-color:#000;color:#fff}.navbar.is-black .navbar-dropdown a.navbar-item.is-active{background-color:#0a0a0a;color:#fff}}.navbar.is-light{background-color:#f5f5f5;color:rgba(0,0,0,0.7)}.navbar.is-light .navbar-brand>.navbar-item,.navbar.is-light .navbar-brand .navbar-link{color:rgba(0,0,0,0.7)}.navbar.is-light .navbar-brand>a.navbar-item:focus,.navbar.is-light .navbar-brand>a.navbar-item:hover,.navbar.is-light .navbar-brand>a.navbar-item.is-active,.navbar.is-light .navbar-brand .navbar-link:focus,.navbar.is-light .navbar-brand .navbar-link:hover,.navbar.is-light .navbar-brand .navbar-link.is-active{background-color:#e8e8e8;color:rgba(0,0,0,0.7)}.navbar.is-light .navbar-brand .navbar-link::after{border-color:rgba(0,0,0,0.7)}.navbar.is-light .navbar-burger{color:rgba(0,0,0,0.7)}@media screen and (min-width: 1056px){.navbar.is-light .navbar-start>.navbar-item,.navbar.is-light .navbar-start .navbar-link,.navbar.is-light .navbar-end>.navbar-item,.navbar.is-light .navbar-end .navbar-link{color:rgba(0,0,0,0.7)}.navbar.is-light .navbar-start>a.navbar-item:focus,.navbar.is-light .navbar-start>a.navbar-item:hover,.navbar.is-light .navbar-start>a.navbar-item.is-active,.navbar.is-light .navbar-start .navbar-link:focus,.navbar.is-light .navbar-start .navbar-link:hover,.navbar.is-light .navbar-start .navbar-link.is-active,.navbar.is-light .navbar-end>a.navbar-item:focus,.navbar.is-light .navbar-end>a.navbar-item:hover,.navbar.is-light .navbar-end>a.navbar-item.is-active,.navbar.is-light .navbar-end .navbar-link:focus,.navbar.is-light .navbar-end .navbar-link:hover,.navbar.is-light .navbar-end .navbar-link.is-active{background-color:#e8e8e8;color:rgba(0,0,0,0.7)}.navbar.is-light .navbar-start .navbar-link::after,.navbar.is-light .navbar-end .navbar-link::after{border-color:rgba(0,0,0,0.7)}.navbar.is-light .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-light .navbar-item.has-dropdown:hover .navbar-link,.navbar.is-light .navbar-item.has-dropdown.is-active .navbar-link{background-color:#e8e8e8;color:rgba(0,0,0,0.7)}.navbar.is-light .navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:rgba(0,0,0,0.7)}}.navbar.is-dark,.content kbd.navbar{background-color:#363636;color:#fff}.navbar.is-dark .navbar-brand>.navbar-item,.content kbd.navbar .navbar-brand>.navbar-item,.navbar.is-dark .navbar-brand .navbar-link,.content kbd.navbar .navbar-brand .navbar-link{color:#fff}.navbar.is-dark .navbar-brand>a.navbar-item:focus,.content kbd.navbar .navbar-brand>a.navbar-item:focus,.navbar.is-dark .navbar-brand>a.navbar-item:hover,.content kbd.navbar .navbar-brand>a.navbar-item:hover,.navbar.is-dark .navbar-brand>a.navbar-item.is-active,.content kbd.navbar .navbar-brand>a.navbar-item.is-active,.navbar.is-dark .navbar-brand .navbar-link:focus,.content kbd.navbar .navbar-brand .navbar-link:focus,.navbar.is-dark .navbar-brand .navbar-link:hover,.content kbd.navbar .navbar-brand .navbar-link:hover,.navbar.is-dark .navbar-brand .navbar-link.is-active,.content kbd.navbar .navbar-brand .navbar-link.is-active{background-color:#292929;color:#fff}.navbar.is-dark .navbar-brand .navbar-link::after,.content kbd.navbar .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-dark .navbar-burger,.content kbd.navbar .navbar-burger{color:#fff}@media screen and (min-width: 1056px){.navbar.is-dark .navbar-start>.navbar-item,.content kbd.navbar .navbar-start>.navbar-item,.navbar.is-dark .navbar-start .navbar-link,.content kbd.navbar .navbar-start .navbar-link,.navbar.is-dark .navbar-end>.navbar-item,.content kbd.navbar .navbar-end>.navbar-item,.navbar.is-dark .navbar-end .navbar-link,.content kbd.navbar .navbar-end .navbar-link{color:#fff}.navbar.is-dark .navbar-start>a.navbar-item:focus,.content kbd.navbar .navbar-start>a.navbar-item:focus,.navbar.is-dark .navbar-start>a.navbar-item:hover,.content kbd.navbar .navbar-start>a.navbar-item:hover,.navbar.is-dark .navbar-start>a.navbar-item.is-active,.content kbd.navbar .navbar-start>a.navbar-item.is-active,.navbar.is-dark .navbar-start .navbar-link:focus,.content kbd.navbar .navbar-start .navbar-link:focus,.navbar.is-dark .navbar-start .navbar-link:hover,.content kbd.navbar .navbar-start .navbar-link:hover,.navbar.is-dark .navbar-start .navbar-link.is-active,.content kbd.navbar .navbar-start .navbar-link.is-active,.navbar.is-dark .navbar-end>a.navbar-item:focus,.content kbd.navbar .navbar-end>a.navbar-item:focus,.navbar.is-dark .navbar-end>a.navbar-item:hover,.content kbd.navbar .navbar-end>a.navbar-item:hover,.navbar.is-dark .navbar-end>a.navbar-item.is-active,.content kbd.navbar .navbar-end>a.navbar-item.is-active,.navbar.is-dark .navbar-end .navbar-link:focus,.content kbd.navbar .navbar-end .navbar-link:focus,.navbar.is-dark .navbar-end .navbar-link:hover,.content kbd.navbar .navbar-end .navbar-link:hover,.navbar.is-dark .navbar-end .navbar-link.is-active,.content kbd.navbar .navbar-end .navbar-link.is-active{background-color:#292929;color:#fff}.navbar.is-dark .navbar-start .navbar-link::after,.content kbd.navbar .navbar-start .navbar-link::after,.navbar.is-dark .navbar-end .navbar-link::after,.content kbd.navbar .navbar-end .navbar-link::after{border-color:#fff}.navbar.is-dark .navbar-item.has-dropdown:focus .navbar-link,.content kbd.navbar .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-dark .navbar-item.has-dropdown:hover .navbar-link,.content kbd.navbar .navbar-item.has-dropdown:hover .navbar-link,.navbar.is-dark .navbar-item.has-dropdown.is-active .navbar-link,.content kbd.navbar .navbar-item.has-dropdown.is-active .navbar-link{background-color:#292929;color:#fff}.navbar.is-dark .navbar-dropdown a.navbar-item.is-active,.content kbd.navbar .navbar-dropdown a.navbar-item.is-active{background-color:#363636;color:#fff}}.navbar.is-primary,.docstring>section>a.navbar.docs-sourcelink{background-color:#4eb5de;color:#fff}.navbar.is-primary .navbar-brand>.navbar-item,.docstring>section>a.navbar.docs-sourcelink .navbar-brand>.navbar-item,.navbar.is-primary .navbar-brand .navbar-link,.docstring>section>a.navbar.docs-sourcelink .navbar-brand .navbar-link{color:#fff}.navbar.is-primary .navbar-brand>a.navbar-item:focus,.docstring>section>a.navbar.docs-sourcelink .navbar-brand>a.navbar-item:focus,.navbar.is-primary .navbar-brand>a.navbar-item:hover,.docstring>section>a.navbar.docs-sourcelink .navbar-brand>a.navbar-item:hover,.navbar.is-primary .navbar-brand>a.navbar-item.is-active,.docstring>section>a.navbar.docs-sourcelink .navbar-brand>a.navbar-item.is-active,.navbar.is-primary .navbar-brand .navbar-link:focus,.docstring>section>a.navbar.docs-sourcelink .navbar-brand .navbar-link:focus,.navbar.is-primary .navbar-brand .navbar-link:hover,.docstring>section>a.navbar.docs-sourcelink .navbar-brand .navbar-link:hover,.navbar.is-primary .navbar-brand .navbar-link.is-active,.docstring>section>a.navbar.docs-sourcelink .navbar-brand .navbar-link.is-active{background-color:#39acda;color:#fff}.navbar.is-primary .navbar-brand .navbar-link::after,.docstring>section>a.navbar.docs-sourcelink .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-primary .navbar-burger,.docstring>section>a.navbar.docs-sourcelink .navbar-burger{color:#fff}@media screen and (min-width: 1056px){.navbar.is-primary .navbar-start>.navbar-item,.docstring>section>a.navbar.docs-sourcelink .navbar-start>.navbar-item,.navbar.is-primary .navbar-start .navbar-link,.docstring>section>a.navbar.docs-sourcelink .navbar-start .navbar-link,.navbar.is-primary .navbar-end>.navbar-item,.docstring>section>a.navbar.docs-sourcelink .navbar-end>.navbar-item,.navbar.is-primary .navbar-end .navbar-link,.docstring>section>a.navbar.docs-sourcelink .navbar-end .navbar-link{color:#fff}.navbar.is-primary .navbar-start>a.navbar-item:focus,.docstring>section>a.navbar.docs-sourcelink .navbar-start>a.navbar-item:focus,.navbar.is-primary .navbar-start>a.navbar-item:hover,.docstring>section>a.navbar.docs-sourcelink .navbar-start>a.navbar-item:hover,.navbar.is-primary .navbar-start>a.navbar-item.is-active,.docstring>section>a.navbar.docs-sourcelink .navbar-start>a.navbar-item.is-active,.navbar.is-primary .navbar-start .navbar-link:focus,.docstring>section>a.navbar.docs-sourcelink .navbar-start .navbar-link:focus,.navbar.is-primary .navbar-start .navbar-link:hover,.docstring>section>a.navbar.docs-sourcelink .navbar-start .navbar-link:hover,.navbar.is-primary .navbar-start .navbar-link.is-active,.docstring>section>a.navbar.docs-sourcelink .navbar-start .navbar-link.is-active,.navbar.is-primary .navbar-end>a.navbar-item:focus,.docstring>section>a.navbar.docs-sourcelink .navbar-end>a.navbar-item:focus,.navbar.is-primary .navbar-end>a.navbar-item:hover,.docstring>section>a.navbar.docs-sourcelink .navbar-end>a.navbar-item:hover,.navbar.is-primary .navbar-end>a.navbar-item.is-active,.docstring>section>a.navbar.docs-sourcelink .navbar-end>a.navbar-item.is-active,.navbar.is-primary .navbar-end .navbar-link:focus,.docstring>section>a.navbar.docs-sourcelink .navbar-end .navbar-link:focus,.navbar.is-primary .navbar-end .navbar-link:hover,.docstring>section>a.navbar.docs-sourcelink .navbar-end .navbar-link:hover,.navbar.is-primary .navbar-end .navbar-link.is-active,.docstring>section>a.navbar.docs-sourcelink .navbar-end .navbar-link.is-active{background-color:#39acda;color:#fff}.navbar.is-primary .navbar-start .navbar-link::after,.docstring>section>a.navbar.docs-sourcelink .navbar-start .navbar-link::after,.navbar.is-primary .navbar-end .navbar-link::after,.docstring>section>a.navbar.docs-sourcelink .navbar-end .navbar-link::after{border-color:#fff}.navbar.is-primary .navbar-item.has-dropdown:focus .navbar-link,.docstring>section>a.navbar.docs-sourcelink .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-primary .navbar-item.has-dropdown:hover .navbar-link,.docstring>section>a.navbar.docs-sourcelink .navbar-item.has-dropdown:hover .navbar-link,.navbar.is-primary .navbar-item.has-dropdown.is-active .navbar-link,.docstring>section>a.navbar.docs-sourcelink .navbar-item.has-dropdown.is-active .navbar-link{background-color:#39acda;color:#fff}.navbar.is-primary .navbar-dropdown a.navbar-item.is-active,.docstring>section>a.navbar.docs-sourcelink .navbar-dropdown a.navbar-item.is-active{background-color:#4eb5de;color:#fff}}.navbar.is-link{background-color:#2e63b8;color:#fff}.navbar.is-link .navbar-brand>.navbar-item,.navbar.is-link .navbar-brand .navbar-link{color:#fff}.navbar.is-link .navbar-brand>a.navbar-item:focus,.navbar.is-link .navbar-brand>a.navbar-item:hover,.navbar.is-link .navbar-brand>a.navbar-item.is-active,.navbar.is-link .navbar-brand .navbar-link:focus,.navbar.is-link .navbar-brand .navbar-link:hover,.navbar.is-link .navbar-brand .navbar-link.is-active{background-color:#2958a4;color:#fff}.navbar.is-link .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-link .navbar-burger{color:#fff}@media screen and (min-width: 1056px){.navbar.is-link .navbar-start>.navbar-item,.navbar.is-link .navbar-start .navbar-link,.navbar.is-link .navbar-end>.navbar-item,.navbar.is-link .navbar-end .navbar-link{color:#fff}.navbar.is-link .navbar-start>a.navbar-item:focus,.navbar.is-link .navbar-start>a.navbar-item:hover,.navbar.is-link .navbar-start>a.navbar-item.is-active,.navbar.is-link .navbar-start .navbar-link:focus,.navbar.is-link .navbar-start .navbar-link:hover,.navbar.is-link .navbar-start .navbar-link.is-active,.navbar.is-link .navbar-end>a.navbar-item:focus,.navbar.is-link .navbar-end>a.navbar-item:hover,.navbar.is-link .navbar-end>a.navbar-item.is-active,.navbar.is-link .navbar-end .navbar-link:focus,.navbar.is-link .navbar-end .navbar-link:hover,.navbar.is-link .navbar-end .navbar-link.is-active{background-color:#2958a4;color:#fff}.navbar.is-link .navbar-start .navbar-link::after,.navbar.is-link .navbar-end .navbar-link::after{border-color:#fff}.navbar.is-link .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-link .navbar-item.has-dropdown:hover .navbar-link,.navbar.is-link .navbar-item.has-dropdown.is-active .navbar-link{background-color:#2958a4;color:#fff}.navbar.is-link .navbar-dropdown a.navbar-item.is-active{background-color:#2e63b8;color:#fff}}.navbar.is-info{background-color:#209cee;color:#fff}.navbar.is-info .navbar-brand>.navbar-item,.navbar.is-info .navbar-brand .navbar-link{color:#fff}.navbar.is-info .navbar-brand>a.navbar-item:focus,.navbar.is-info .navbar-brand>a.navbar-item:hover,.navbar.is-info .navbar-brand>a.navbar-item.is-active,.navbar.is-info .navbar-brand .navbar-link:focus,.navbar.is-info .navbar-brand .navbar-link:hover,.navbar.is-info .navbar-brand .navbar-link.is-active{background-color:#1190e3;color:#fff}.navbar.is-info .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-info .navbar-burger{color:#fff}@media screen and (min-width: 1056px){.navbar.is-info .navbar-start>.navbar-item,.navbar.is-info .navbar-start .navbar-link,.navbar.is-info .navbar-end>.navbar-item,.navbar.is-info .navbar-end .navbar-link{color:#fff}.navbar.is-info .navbar-start>a.navbar-item:focus,.navbar.is-info .navbar-start>a.navbar-item:hover,.navbar.is-info .navbar-start>a.navbar-item.is-active,.navbar.is-info .navbar-start .navbar-link:focus,.navbar.is-info .navbar-start .navbar-link:hover,.navbar.is-info .navbar-start .navbar-link.is-active,.navbar.is-info .navbar-end>a.navbar-item:focus,.navbar.is-info .navbar-end>a.navbar-item:hover,.navbar.is-info .navbar-end>a.navbar-item.is-active,.navbar.is-info .navbar-end .navbar-link:focus,.navbar.is-info .navbar-end .navbar-link:hover,.navbar.is-info .navbar-end .navbar-link.is-active{background-color:#1190e3;color:#fff}.navbar.is-info .navbar-start .navbar-link::after,.navbar.is-info .navbar-end .navbar-link::after{border-color:#fff}.navbar.is-info .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-info .navbar-item.has-dropdown:hover .navbar-link,.navbar.is-info .navbar-item.has-dropdown.is-active .navbar-link{background-color:#1190e3;color:#fff}.navbar.is-info .navbar-dropdown a.navbar-item.is-active{background-color:#209cee;color:#fff}}.navbar.is-success{background-color:#22c35b;color:#fff}.navbar.is-success .navbar-brand>.navbar-item,.navbar.is-success .navbar-brand .navbar-link{color:#fff}.navbar.is-success .navbar-brand>a.navbar-item:focus,.navbar.is-success .navbar-brand>a.navbar-item:hover,.navbar.is-success .navbar-brand>a.navbar-item.is-active,.navbar.is-success .navbar-brand .navbar-link:focus,.navbar.is-success .navbar-brand .navbar-link:hover,.navbar.is-success .navbar-brand .navbar-link.is-active{background-color:#1ead51;color:#fff}.navbar.is-success .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-success .navbar-burger{color:#fff}@media screen and (min-width: 1056px){.navbar.is-success .navbar-start>.navbar-item,.navbar.is-success .navbar-start .navbar-link,.navbar.is-success .navbar-end>.navbar-item,.navbar.is-success .navbar-end .navbar-link{color:#fff}.navbar.is-success .navbar-start>a.navbar-item:focus,.navbar.is-success .navbar-start>a.navbar-item:hover,.navbar.is-success .navbar-start>a.navbar-item.is-active,.navbar.is-success .navbar-start .navbar-link:focus,.navbar.is-success .navbar-start .navbar-link:hover,.navbar.is-success .navbar-start .navbar-link.is-active,.navbar.is-success .navbar-end>a.navbar-item:focus,.navbar.is-success .navbar-end>a.navbar-item:hover,.navbar.is-success .navbar-end>a.navbar-item.is-active,.navbar.is-success .navbar-end .navbar-link:focus,.navbar.is-success .navbar-end .navbar-link:hover,.navbar.is-success .navbar-end .navbar-link.is-active{background-color:#1ead51;color:#fff}.navbar.is-success .navbar-start .navbar-link::after,.navbar.is-success .navbar-end .navbar-link::after{border-color:#fff}.navbar.is-success .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-success .navbar-item.has-dropdown:hover .navbar-link,.navbar.is-success .navbar-item.has-dropdown.is-active .navbar-link{background-color:#1ead51;color:#fff}.navbar.is-success .navbar-dropdown a.navbar-item.is-active{background-color:#22c35b;color:#fff}}.navbar.is-warning{background-color:#ffdd57;color:rgba(0,0,0,0.7)}.navbar.is-warning .navbar-brand>.navbar-item,.navbar.is-warning .navbar-brand .navbar-link{color:rgba(0,0,0,0.7)}.navbar.is-warning .navbar-brand>a.navbar-item:focus,.navbar.is-warning .navbar-brand>a.navbar-item:hover,.navbar.is-warning .navbar-brand>a.navbar-item.is-active,.navbar.is-warning .navbar-brand .navbar-link:focus,.navbar.is-warning .navbar-brand .navbar-link:hover,.navbar.is-warning .navbar-brand .navbar-link.is-active{background-color:#ffd83e;color:rgba(0,0,0,0.7)}.navbar.is-warning .navbar-brand .navbar-link::after{border-color:rgba(0,0,0,0.7)}.navbar.is-warning .navbar-burger{color:rgba(0,0,0,0.7)}@media screen and (min-width: 1056px){.navbar.is-warning .navbar-start>.navbar-item,.navbar.is-warning .navbar-start .navbar-link,.navbar.is-warning .navbar-end>.navbar-item,.navbar.is-warning .navbar-end .navbar-link{color:rgba(0,0,0,0.7)}.navbar.is-warning .navbar-start>a.navbar-item:focus,.navbar.is-warning .navbar-start>a.navbar-item:hover,.navbar.is-warning .navbar-start>a.navbar-item.is-active,.navbar.is-warning .navbar-start .navbar-link:focus,.navbar.is-warning .navbar-start .navbar-link:hover,.navbar.is-warning .navbar-start .navbar-link.is-active,.navbar.is-warning .navbar-end>a.navbar-item:focus,.navbar.is-warning .navbar-end>a.navbar-item:hover,.navbar.is-warning .navbar-end>a.navbar-item.is-active,.navbar.is-warning .navbar-end .navbar-link:focus,.navbar.is-warning .navbar-end .navbar-link:hover,.navbar.is-warning .navbar-end .navbar-link.is-active{background-color:#ffd83e;color:rgba(0,0,0,0.7)}.navbar.is-warning .navbar-start .navbar-link::after,.navbar.is-warning .navbar-end .navbar-link::after{border-color:rgba(0,0,0,0.7)}.navbar.is-warning .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-warning .navbar-item.has-dropdown:hover .navbar-link,.navbar.is-warning .navbar-item.has-dropdown.is-active .navbar-link{background-color:#ffd83e;color:rgba(0,0,0,0.7)}.navbar.is-warning .navbar-dropdown a.navbar-item.is-active{background-color:#ffdd57;color:rgba(0,0,0,0.7)}}.navbar.is-danger{background-color:#da0b00;color:#fff}.navbar.is-danger .navbar-brand>.navbar-item,.navbar.is-danger .navbar-brand .navbar-link{color:#fff}.navbar.is-danger .navbar-brand>a.navbar-item:focus,.navbar.is-danger .navbar-brand>a.navbar-item:hover,.navbar.is-danger .navbar-brand>a.navbar-item.is-active,.navbar.is-danger .navbar-brand .navbar-link:focus,.navbar.is-danger .navbar-brand .navbar-link:hover,.navbar.is-danger .navbar-brand .navbar-link.is-active{background-color:#c10a00;color:#fff}.navbar.is-danger .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-danger .navbar-burger{color:#fff}@media screen and (min-width: 1056px){.navbar.is-danger .navbar-start>.navbar-item,.navbar.is-danger .navbar-start .navbar-link,.navbar.is-danger .navbar-end>.navbar-item,.navbar.is-danger .navbar-end .navbar-link{color:#fff}.navbar.is-danger .navbar-start>a.navbar-item:focus,.navbar.is-danger .navbar-start>a.navbar-item:hover,.navbar.is-danger .navbar-start>a.navbar-item.is-active,.navbar.is-danger .navbar-start .navbar-link:focus,.navbar.is-danger .navbar-start .navbar-link:hover,.navbar.is-danger .navbar-start .navbar-link.is-active,.navbar.is-danger .navbar-end>a.navbar-item:focus,.navbar.is-danger .navbar-end>a.navbar-item:hover,.navbar.is-danger .navbar-end>a.navbar-item.is-active,.navbar.is-danger .navbar-end .navbar-link:focus,.navbar.is-danger .navbar-end .navbar-link:hover,.navbar.is-danger .navbar-end .navbar-link.is-active{background-color:#c10a00;color:#fff}.navbar.is-danger .navbar-start .navbar-link::after,.navbar.is-danger .navbar-end .navbar-link::after{border-color:#fff}.navbar.is-danger .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-danger .navbar-item.has-dropdown:hover .navbar-link,.navbar.is-danger .navbar-item.has-dropdown.is-active .navbar-link{background-color:#c10a00;color:#fff}.navbar.is-danger .navbar-dropdown a.navbar-item.is-active{background-color:#da0b00;color:#fff}}.navbar>.container{align-items:stretch;display:flex;min-height:3.25rem;width:100%}.navbar.has-shadow{box-shadow:0 2px 0 0 #f5f5f5}.navbar.is-fixed-bottom,.navbar.is-fixed-top{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom{bottom:0}.navbar.is-fixed-bottom.has-shadow{box-shadow:0 -2px 0 0 #f5f5f5}.navbar.is-fixed-top{top:0}html.has-navbar-fixed-top,body.has-navbar-fixed-top{padding-top:3.25rem}html.has-navbar-fixed-bottom,body.has-navbar-fixed-bottom{padding-bottom:3.25rem}.navbar-brand,.navbar-tabs{align-items:stretch;display:flex;flex-shrink:0;min-height:3.25rem}.navbar-brand a.navbar-item:focus,.navbar-brand a.navbar-item:hover{background-color:transparent}.navbar-tabs{-webkit-overflow-scrolling:touch;max-width:100vw;overflow-x:auto;overflow-y:hidden}.navbar-burger{color:#222;-moz-appearance:none;-webkit-appearance:none;appearance:none;background:none;border:none;cursor:pointer;display:block;height:3.25rem;position:relative;width:3.25rem;margin-left:auto}.navbar-burger span{background-color:currentColor;display:block;height:1px;left:calc(50% - 8px);position:absolute;transform-origin:center;transition-duration:86ms;transition-property:background-color, opacity, transform;transition-timing-function:ease-out;width:16px}.navbar-burger span:nth-child(1){top:calc(50% - 6px)}.navbar-burger span:nth-child(2){top:calc(50% - 1px)}.navbar-burger span:nth-child(3){top:calc(50% + 4px)}.navbar-burger:hover{background-color:rgba(0,0,0,0.05)}.navbar-burger.is-active span:nth-child(1){transform:translateY(5px) rotate(45deg)}.navbar-burger.is-active span:nth-child(2){opacity:0}.navbar-burger.is-active span:nth-child(3){transform:translateY(-5px) rotate(-45deg)}.navbar-menu{display:none}.navbar-item,.navbar-link{color:#222;display:block;line-height:1.5;padding:0.5rem 0.75rem;position:relative}.navbar-item .icon:only-child,.navbar-link .icon:only-child{margin-left:-0.25rem;margin-right:-0.25rem}a.navbar-item,.navbar-link{cursor:pointer}a.navbar-item:focus,a.navbar-item:focus-within,a.navbar-item:hover,a.navbar-item.is-active,.navbar-link:focus,.navbar-link:focus-within,.navbar-link:hover,.navbar-link.is-active{background-color:#fafafa;color:#2e63b8}.navbar-item{flex-grow:0;flex-shrink:0}.navbar-item img{max-height:1.75rem}.navbar-item.has-dropdown{padding:0}.navbar-item.is-expanded{flex-grow:1;flex-shrink:1}.navbar-item.is-tab{border-bottom:1px solid transparent;min-height:3.25rem;padding-bottom:calc(0.5rem - 1px)}.navbar-item.is-tab:focus,.navbar-item.is-tab:hover{background-color:rgba(0,0,0,0);border-bottom-color:#2e63b8}.navbar-item.is-tab.is-active{background-color:rgba(0,0,0,0);border-bottom-color:#2e63b8;border-bottom-style:solid;border-bottom-width:3px;color:#2e63b8;padding-bottom:calc(0.5rem - 3px)}.navbar-content{flex-grow:1;flex-shrink:1}.navbar-link:not(.is-arrowless){padding-right:2.5em}.navbar-link:not(.is-arrowless)::after{border-color:#2e63b8;margin-top:-0.375em;right:1.125em}.navbar-dropdown{font-size:0.875rem;padding-bottom:0.5rem;padding-top:0.5rem}.navbar-dropdown .navbar-item{padding-left:1.5rem;padding-right:1.5rem}.navbar-divider{background-color:#f5f5f5;border:none;display:none;height:2px;margin:0.5rem 0}@media screen and (max-width: 1055px){.navbar>.container{display:block}.navbar-brand .navbar-item,.navbar-tabs .navbar-item{align-items:center;display:flex}.navbar-link::after{display:none}.navbar-menu{background-color:#fff;box-shadow:0 8px 16px rgba(10,10,10,0.1);padding:0.5rem 0}.navbar-menu.is-active{display:block}.navbar.is-fixed-bottom-touch,.navbar.is-fixed-top-touch{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom-touch{bottom:0}.navbar.is-fixed-bottom-touch.has-shadow{box-shadow:0 -2px 3px rgba(10,10,10,0.1)}.navbar.is-fixed-top-touch{top:0}.navbar.is-fixed-top .navbar-menu,.navbar.is-fixed-top-touch .navbar-menu{-webkit-overflow-scrolling:touch;max-height:calc(100vh - 3.25rem);overflow:auto}html.has-navbar-fixed-top-touch,body.has-navbar-fixed-top-touch{padding-top:3.25rem}html.has-navbar-fixed-bottom-touch,body.has-navbar-fixed-bottom-touch{padding-bottom:3.25rem}}@media screen and (min-width: 1056px){.navbar,.navbar-menu,.navbar-start,.navbar-end{align-items:stretch;display:flex}.navbar{min-height:3.25rem}.navbar.is-spaced{padding:1rem 2rem}.navbar.is-spaced .navbar-start,.navbar.is-spaced .navbar-end{align-items:center}.navbar.is-spaced a.navbar-item,.navbar.is-spaced .navbar-link{border-radius:4px}.navbar.is-transparent a.navbar-item:focus,.navbar.is-transparent a.navbar-item:hover,.navbar.is-transparent a.navbar-item.is-active,.navbar.is-transparent .navbar-link:focus,.navbar.is-transparent .navbar-link:hover,.navbar.is-transparent .navbar-link.is-active{background-color:transparent !important}.navbar.is-transparent .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:focus .navbar-link,.navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:focus-within .navbar-link,.navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:hover .navbar-link{background-color:transparent !important}.navbar.is-transparent .navbar-dropdown a.navbar-item:focus,.navbar.is-transparent .navbar-dropdown a.navbar-item:hover{background-color:#f5f5f5;color:#0a0a0a}.navbar.is-transparent .navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#2e63b8}.navbar-burger{display:none}.navbar-item,.navbar-link{align-items:center;display:flex}.navbar-item.has-dropdown{align-items:stretch}.navbar-item.has-dropdown-up .navbar-link::after{transform:rotate(135deg) translate(0.25em, -0.25em)}.navbar-item.has-dropdown-up .navbar-dropdown{border-bottom:2px solid #dbdbdb;border-radius:6px 6px 0 0;border-top:none;bottom:100%;box-shadow:0 -8px 8px rgba(10,10,10,0.1);top:auto}.navbar-item.is-active .navbar-dropdown,.navbar-item.is-hoverable:focus .navbar-dropdown,.navbar-item.is-hoverable:focus-within .navbar-dropdown,.navbar-item.is-hoverable:hover .navbar-dropdown{display:block}.navbar.is-spaced .navbar-item.is-active .navbar-dropdown,.navbar-item.is-active .navbar-dropdown.is-boxed,.navbar.is-spaced .navbar-item.is-hoverable:focus .navbar-dropdown,.navbar-item.is-hoverable:focus .navbar-dropdown.is-boxed,.navbar.is-spaced .navbar-item.is-hoverable:focus-within .navbar-dropdown,.navbar-item.is-hoverable:focus-within .navbar-dropdown.is-boxed,.navbar.is-spaced .navbar-item.is-hoverable:hover .navbar-dropdown,.navbar-item.is-hoverable:hover .navbar-dropdown.is-boxed{opacity:1;pointer-events:auto;transform:translateY(0)}.navbar-menu{flex-grow:1;flex-shrink:0}.navbar-start{justify-content:flex-start;margin-right:auto}.navbar-end{justify-content:flex-end;margin-left:auto}.navbar-dropdown{background-color:#fff;border-bottom-left-radius:6px;border-bottom-right-radius:6px;border-top:2px solid #dbdbdb;box-shadow:0 8px 8px rgba(10,10,10,0.1);display:none;font-size:0.875rem;left:0;min-width:100%;position:absolute;top:100%;z-index:20}.navbar-dropdown .navbar-item{padding:0.375rem 1rem;white-space:nowrap}.navbar-dropdown a.navbar-item{padding-right:3rem}.navbar-dropdown a.navbar-item:focus,.navbar-dropdown a.navbar-item:hover{background-color:#f5f5f5;color:#0a0a0a}.navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#2e63b8}.navbar.is-spaced .navbar-dropdown,.navbar-dropdown.is-boxed{border-radius:6px;border-top:none;box-shadow:0 8px 8px rgba(10,10,10,0.1), 0 0 0 1px rgba(10,10,10,0.1);display:block;opacity:0;pointer-events:none;top:calc(100% + (-4px));transform:translateY(-5px);transition-duration:86ms;transition-property:opacity, transform}.navbar-dropdown.is-right{left:auto;right:0}.navbar-divider{display:block}.navbar>.container .navbar-brand,.container>.navbar .navbar-brand{margin-left:-.75rem}.navbar>.container .navbar-menu,.container>.navbar .navbar-menu{margin-right:-.75rem}.navbar.is-fixed-bottom-desktop,.navbar.is-fixed-top-desktop{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom-desktop{bottom:0}.navbar.is-fixed-bottom-desktop.has-shadow{box-shadow:0 -2px 3px rgba(10,10,10,0.1)}.navbar.is-fixed-top-desktop{top:0}html.has-navbar-fixed-top-desktop,body.has-navbar-fixed-top-desktop{padding-top:3.25rem}html.has-navbar-fixed-bottom-desktop,body.has-navbar-fixed-bottom-desktop{padding-bottom:3.25rem}html.has-spaced-navbar-fixed-top,body.has-spaced-navbar-fixed-top{padding-top:5.25rem}html.has-spaced-navbar-fixed-bottom,body.has-spaced-navbar-fixed-bottom{padding-bottom:5.25rem}a.navbar-item.is-active,.navbar-link.is-active{color:#0a0a0a}a.navbar-item.is-active:not(:focus):not(:hover),.navbar-link.is-active:not(:focus):not(:hover){background-color:rgba(0,0,0,0)}.navbar-item.has-dropdown:focus .navbar-link,.navbar-item.has-dropdown:hover .navbar-link,.navbar-item.has-dropdown.is-active .navbar-link{background-color:#fafafa}}.hero.is-fullheight-with-navbar{min-height:calc(100vh - 3.25rem)}.pagination{font-size:1rem;margin:-.25rem}.pagination.is-small,#documenter .docs-sidebar form.docs-search>input.pagination{font-size:.75rem}.pagination.is-medium{font-size:1.25rem}.pagination.is-large{font-size:1.5rem}.pagination.is-rounded .pagination-previous,#documenter .docs-sidebar form.docs-search>input.pagination .pagination-previous,.pagination.is-rounded .pagination-next,#documenter .docs-sidebar form.docs-search>input.pagination .pagination-next{padding-left:1em;padding-right:1em;border-radius:9999px}.pagination.is-rounded .pagination-link,#documenter .docs-sidebar form.docs-search>input.pagination .pagination-link{border-radius:9999px}.pagination,.pagination-list{align-items:center;display:flex;justify-content:center;text-align:center}.pagination-previous,.pagination-next,.pagination-link,.pagination-ellipsis{font-size:1em;justify-content:center;margin:.25rem;padding-left:.5em;padding-right:.5em;text-align:center}.pagination-previous,.pagination-next,.pagination-link{border-color:#dbdbdb;color:#222;min-width:2.5em}.pagination-previous:hover,.pagination-next:hover,.pagination-link:hover{border-color:#b5b5b5;color:#363636}.pagination-previous:focus,.pagination-next:focus,.pagination-link:focus{border-color:#3c5dcd}.pagination-previous:active,.pagination-next:active,.pagination-link:active{box-shadow:inset 0 1px 2px rgba(10,10,10,0.2)}.pagination-previous[disabled],.pagination-previous.is-disabled,.pagination-next[disabled],.pagination-next.is-disabled,.pagination-link[disabled],.pagination-link.is-disabled{background-color:#dbdbdb;border-color:#dbdbdb;box-shadow:none;color:#6b6b6b;opacity:0.5}.pagination-previous,.pagination-next{padding-left:.75em;padding-right:.75em;white-space:nowrap}.pagination-link.is-current{background-color:#2e63b8;border-color:#2e63b8;color:#fff}.pagination-ellipsis{color:#b5b5b5;pointer-events:none}.pagination-list{flex-wrap:wrap}.pagination-list li{list-style:none}@media screen and (max-width: 768px){.pagination{flex-wrap:wrap}.pagination-previous,.pagination-next{flex-grow:1;flex-shrink:1}.pagination-list li{flex-grow:1;flex-shrink:1}}@media screen and (min-width: 769px),print{.pagination-list{flex-grow:1;flex-shrink:1;justify-content:flex-start;order:1}.pagination-previous,.pagination-next,.pagination-link,.pagination-ellipsis{margin-bottom:0;margin-top:0}.pagination-previous{order:2}.pagination-next{order:3}.pagination{justify-content:space-between;margin-bottom:0;margin-top:0}.pagination.is-centered .pagination-previous{order:1}.pagination.is-centered .pagination-list{justify-content:center;order:2}.pagination.is-centered .pagination-next{order:3}.pagination.is-right .pagination-previous{order:1}.pagination.is-right .pagination-next{order:2}.pagination.is-right .pagination-list{justify-content:flex-end;order:3}}.panel{border-radius:6px;box-shadow:#bbb;font-size:1rem}.panel:not(:last-child){margin-bottom:1.5rem}.panel.is-white .panel-heading{background-color:#fff;color:#0a0a0a}.panel.is-white .panel-tabs a.is-active{border-bottom-color:#fff}.panel.is-white .panel-block.is-active .panel-icon{color:#fff}.panel.is-black .panel-heading{background-color:#0a0a0a;color:#fff}.panel.is-black .panel-tabs a.is-active{border-bottom-color:#0a0a0a}.panel.is-black .panel-block.is-active .panel-icon{color:#0a0a0a}.panel.is-light .panel-heading{background-color:#f5f5f5;color:rgba(0,0,0,0.7)}.panel.is-light .panel-tabs a.is-active{border-bottom-color:#f5f5f5}.panel.is-light .panel-block.is-active .panel-icon{color:#f5f5f5}.panel.is-dark .panel-heading,.content kbd.panel .panel-heading{background-color:#363636;color:#fff}.panel.is-dark .panel-tabs a.is-active,.content kbd.panel .panel-tabs a.is-active{border-bottom-color:#363636}.panel.is-dark .panel-block.is-active .panel-icon,.content kbd.panel .panel-block.is-active .panel-icon{color:#363636}.panel.is-primary .panel-heading,.docstring>section>a.panel.docs-sourcelink .panel-heading{background-color:#4eb5de;color:#fff}.panel.is-primary .panel-tabs a.is-active,.docstring>section>a.panel.docs-sourcelink .panel-tabs a.is-active{border-bottom-color:#4eb5de}.panel.is-primary .panel-block.is-active .panel-icon,.docstring>section>a.panel.docs-sourcelink .panel-block.is-active .panel-icon{color:#4eb5de}.panel.is-link .panel-heading{background-color:#2e63b8;color:#fff}.panel.is-link .panel-tabs a.is-active{border-bottom-color:#2e63b8}.panel.is-link .panel-block.is-active .panel-icon{color:#2e63b8}.panel.is-info .panel-heading{background-color:#209cee;color:#fff}.panel.is-info .panel-tabs a.is-active{border-bottom-color:#209cee}.panel.is-info .panel-block.is-active .panel-icon{color:#209cee}.panel.is-success .panel-heading{background-color:#22c35b;color:#fff}.panel.is-success .panel-tabs a.is-active{border-bottom-color:#22c35b}.panel.is-success .panel-block.is-active .panel-icon{color:#22c35b}.panel.is-warning .panel-heading{background-color:#ffdd57;color:rgba(0,0,0,0.7)}.panel.is-warning .panel-tabs a.is-active{border-bottom-color:#ffdd57}.panel.is-warning .panel-block.is-active .panel-icon{color:#ffdd57}.panel.is-danger .panel-heading{background-color:#da0b00;color:#fff}.panel.is-danger .panel-tabs a.is-active{border-bottom-color:#da0b00}.panel.is-danger .panel-block.is-active .panel-icon{color:#da0b00}.panel-tabs:not(:last-child),.panel-block:not(:last-child){border-bottom:1px solid #ededed}.panel-heading{background-color:#ededed;border-radius:6px 6px 0 0;color:#222;font-size:1.25em;font-weight:700;line-height:1.25;padding:0.75em 1em}.panel-tabs{align-items:flex-end;display:flex;font-size:.875em;justify-content:center}.panel-tabs a{border-bottom:1px solid #dbdbdb;margin-bottom:-1px;padding:0.5em}.panel-tabs a.is-active{border-bottom-color:#4a4a4a;color:#363636}.panel-list a{color:#222}.panel-list a:hover{color:#2e63b8}.panel-block{align-items:center;color:#222;display:flex;justify-content:flex-start;padding:0.5em 0.75em}.panel-block input[type="checkbox"]{margin-right:.75em}.panel-block>.control{flex-grow:1;flex-shrink:1;width:100%}.panel-block.is-wrapped{flex-wrap:wrap}.panel-block.is-active{border-left-color:#2e63b8;color:#363636}.panel-block.is-active .panel-icon{color:#2e63b8}.panel-block:last-child{border-bottom-left-radius:6px;border-bottom-right-radius:6px}a.panel-block,label.panel-block{cursor:pointer}a.panel-block:hover,label.panel-block:hover{background-color:#f5f5f5}.panel-icon{display:inline-block;font-size:14px;height:1em;line-height:1em;text-align:center;vertical-align:top;width:1em;color:#6b6b6b;margin-right:.75em}.panel-icon .fa{font-size:inherit;line-height:inherit}.tabs{-webkit-overflow-scrolling:touch;align-items:stretch;display:flex;font-size:1rem;justify-content:space-between;overflow:hidden;overflow-x:auto;white-space:nowrap}.tabs a{align-items:center;border-bottom-color:#dbdbdb;border-bottom-style:solid;border-bottom-width:1px;color:#222;display:flex;justify-content:center;margin-bottom:-1px;padding:0.5em 1em;vertical-align:top}.tabs a:hover{border-bottom-color:#222;color:#222}.tabs li{display:block}.tabs li.is-active a{border-bottom-color:#2e63b8;color:#2e63b8}.tabs ul{align-items:center;border-bottom-color:#dbdbdb;border-bottom-style:solid;border-bottom-width:1px;display:flex;flex-grow:1;flex-shrink:0;justify-content:flex-start}.tabs ul.is-left{padding-right:0.75em}.tabs ul.is-center{flex:none;justify-content:center;padding-left:0.75em;padding-right:0.75em}.tabs ul.is-right{justify-content:flex-end;padding-left:0.75em}.tabs .icon:first-child{margin-right:.5em}.tabs .icon:last-child{margin-left:.5em}.tabs.is-centered ul{justify-content:center}.tabs.is-right ul{justify-content:flex-end}.tabs.is-boxed a{border:1px solid transparent;border-radius:4px 4px 0 0}.tabs.is-boxed a:hover{background-color:#f5f5f5;border-bottom-color:#dbdbdb}.tabs.is-boxed li.is-active a{background-color:#fff;border-color:#dbdbdb;border-bottom-color:rgba(0,0,0,0) !important}.tabs.is-fullwidth li{flex-grow:1;flex-shrink:0}.tabs.is-toggle a{border-color:#dbdbdb;border-style:solid;border-width:1px;margin-bottom:0;position:relative}.tabs.is-toggle a:hover{background-color:#f5f5f5;border-color:#b5b5b5;z-index:2}.tabs.is-toggle li+li{margin-left:-1px}.tabs.is-toggle li:first-child a{border-top-left-radius:4px;border-bottom-left-radius:4px}.tabs.is-toggle li:last-child a{border-top-right-radius:4px;border-bottom-right-radius:4px}.tabs.is-toggle li.is-active a{background-color:#2e63b8;border-color:#2e63b8;color:#fff;z-index:1}.tabs.is-toggle ul{border-bottom:none}.tabs.is-toggle.is-toggle-rounded li:first-child a{border-bottom-left-radius:9999px;border-top-left-radius:9999px;padding-left:1.25em}.tabs.is-toggle.is-toggle-rounded li:last-child a{border-bottom-right-radius:9999px;border-top-right-radius:9999px;padding-right:1.25em}.tabs.is-small,#documenter .docs-sidebar form.docs-search>input.tabs{font-size:.75rem}.tabs.is-medium{font-size:1.25rem}.tabs.is-large{font-size:1.5rem}.column{display:block;flex-basis:0;flex-grow:1;flex-shrink:1;padding:.75rem}.columns.is-mobile>.column.is-narrow{flex:none;width:unset}.columns.is-mobile>.column.is-full{flex:none;width:100%}.columns.is-mobile>.column.is-three-quarters{flex:none;width:75%}.columns.is-mobile>.column.is-two-thirds{flex:none;width:66.6666%}.columns.is-mobile>.column.is-half{flex:none;width:50%}.columns.is-mobile>.column.is-one-third{flex:none;width:33.3333%}.columns.is-mobile>.column.is-one-quarter{flex:none;width:25%}.columns.is-mobile>.column.is-one-fifth{flex:none;width:20%}.columns.is-mobile>.column.is-two-fifths{flex:none;width:40%}.columns.is-mobile>.column.is-three-fifths{flex:none;width:60%}.columns.is-mobile>.column.is-four-fifths{flex:none;width:80%}.columns.is-mobile>.column.is-offset-three-quarters{margin-left:75%}.columns.is-mobile>.column.is-offset-two-thirds{margin-left:66.6666%}.columns.is-mobile>.column.is-offset-half{margin-left:50%}.columns.is-mobile>.column.is-offset-one-third{margin-left:33.3333%}.columns.is-mobile>.column.is-offset-one-quarter{margin-left:25%}.columns.is-mobile>.column.is-offset-one-fifth{margin-left:20%}.columns.is-mobile>.column.is-offset-two-fifths{margin-left:40%}.columns.is-mobile>.column.is-offset-three-fifths{margin-left:60%}.columns.is-mobile>.column.is-offset-four-fifths{margin-left:80%}.columns.is-mobile>.column.is-0{flex:none;width:0%}.columns.is-mobile>.column.is-offset-0{margin-left:0%}.columns.is-mobile>.column.is-1{flex:none;width:8.33333337%}.columns.is-mobile>.column.is-offset-1{margin-left:8.33333337%}.columns.is-mobile>.column.is-2{flex:none;width:16.66666674%}.columns.is-mobile>.column.is-offset-2{margin-left:16.66666674%}.columns.is-mobile>.column.is-3{flex:none;width:25%}.columns.is-mobile>.column.is-offset-3{margin-left:25%}.columns.is-mobile>.column.is-4{flex:none;width:33.33333337%}.columns.is-mobile>.column.is-offset-4{margin-left:33.33333337%}.columns.is-mobile>.column.is-5{flex:none;width:41.66666674%}.columns.is-mobile>.column.is-offset-5{margin-left:41.66666674%}.columns.is-mobile>.column.is-6{flex:none;width:50%}.columns.is-mobile>.column.is-offset-6{margin-left:50%}.columns.is-mobile>.column.is-7{flex:none;width:58.33333337%}.columns.is-mobile>.column.is-offset-7{margin-left:58.33333337%}.columns.is-mobile>.column.is-8{flex:none;width:66.66666674%}.columns.is-mobile>.column.is-offset-8{margin-left:66.66666674%}.columns.is-mobile>.column.is-9{flex:none;width:75%}.columns.is-mobile>.column.is-offset-9{margin-left:75%}.columns.is-mobile>.column.is-10{flex:none;width:83.33333337%}.columns.is-mobile>.column.is-offset-10{margin-left:83.33333337%}.columns.is-mobile>.column.is-11{flex:none;width:91.66666674%}.columns.is-mobile>.column.is-offset-11{margin-left:91.66666674%}.columns.is-mobile>.column.is-12{flex:none;width:100%}.columns.is-mobile>.column.is-offset-12{margin-left:100%}@media screen and (max-width: 768px){.column.is-narrow-mobile{flex:none;width:unset}.column.is-full-mobile{flex:none;width:100%}.column.is-three-quarters-mobile{flex:none;width:75%}.column.is-two-thirds-mobile{flex:none;width:66.6666%}.column.is-half-mobile{flex:none;width:50%}.column.is-one-third-mobile{flex:none;width:33.3333%}.column.is-one-quarter-mobile{flex:none;width:25%}.column.is-one-fifth-mobile{flex:none;width:20%}.column.is-two-fifths-mobile{flex:none;width:40%}.column.is-three-fifths-mobile{flex:none;width:60%}.column.is-four-fifths-mobile{flex:none;width:80%}.column.is-offset-three-quarters-mobile{margin-left:75%}.column.is-offset-two-thirds-mobile{margin-left:66.6666%}.column.is-offset-half-mobile{margin-left:50%}.column.is-offset-one-third-mobile{margin-left:33.3333%}.column.is-offset-one-quarter-mobile{margin-left:25%}.column.is-offset-one-fifth-mobile{margin-left:20%}.column.is-offset-two-fifths-mobile{margin-left:40%}.column.is-offset-three-fifths-mobile{margin-left:60%}.column.is-offset-four-fifths-mobile{margin-left:80%}.column.is-0-mobile{flex:none;width:0%}.column.is-offset-0-mobile{margin-left:0%}.column.is-1-mobile{flex:none;width:8.33333337%}.column.is-offset-1-mobile{margin-left:8.33333337%}.column.is-2-mobile{flex:none;width:16.66666674%}.column.is-offset-2-mobile{margin-left:16.66666674%}.column.is-3-mobile{flex:none;width:25%}.column.is-offset-3-mobile{margin-left:25%}.column.is-4-mobile{flex:none;width:33.33333337%}.column.is-offset-4-mobile{margin-left:33.33333337%}.column.is-5-mobile{flex:none;width:41.66666674%}.column.is-offset-5-mobile{margin-left:41.66666674%}.column.is-6-mobile{flex:none;width:50%}.column.is-offset-6-mobile{margin-left:50%}.column.is-7-mobile{flex:none;width:58.33333337%}.column.is-offset-7-mobile{margin-left:58.33333337%}.column.is-8-mobile{flex:none;width:66.66666674%}.column.is-offset-8-mobile{margin-left:66.66666674%}.column.is-9-mobile{flex:none;width:75%}.column.is-offset-9-mobile{margin-left:75%}.column.is-10-mobile{flex:none;width:83.33333337%}.column.is-offset-10-mobile{margin-left:83.33333337%}.column.is-11-mobile{flex:none;width:91.66666674%}.column.is-offset-11-mobile{margin-left:91.66666674%}.column.is-12-mobile{flex:none;width:100%}.column.is-offset-12-mobile{margin-left:100%}}@media screen and (min-width: 769px),print{.column.is-narrow,.column.is-narrow-tablet{flex:none;width:unset}.column.is-full,.column.is-full-tablet{flex:none;width:100%}.column.is-three-quarters,.column.is-three-quarters-tablet{flex:none;width:75%}.column.is-two-thirds,.column.is-two-thirds-tablet{flex:none;width:66.6666%}.column.is-half,.column.is-half-tablet{flex:none;width:50%}.column.is-one-third,.column.is-one-third-tablet{flex:none;width:33.3333%}.column.is-one-quarter,.column.is-one-quarter-tablet{flex:none;width:25%}.column.is-one-fifth,.column.is-one-fifth-tablet{flex:none;width:20%}.column.is-two-fifths,.column.is-two-fifths-tablet{flex:none;width:40%}.column.is-three-fifths,.column.is-three-fifths-tablet{flex:none;width:60%}.column.is-four-fifths,.column.is-four-fifths-tablet{flex:none;width:80%}.column.is-offset-three-quarters,.column.is-offset-three-quarters-tablet{margin-left:75%}.column.is-offset-two-thirds,.column.is-offset-two-thirds-tablet{margin-left:66.6666%}.column.is-offset-half,.column.is-offset-half-tablet{margin-left:50%}.column.is-offset-one-third,.column.is-offset-one-third-tablet{margin-left:33.3333%}.column.is-offset-one-quarter,.column.is-offset-one-quarter-tablet{margin-left:25%}.column.is-offset-one-fifth,.column.is-offset-one-fifth-tablet{margin-left:20%}.column.is-offset-two-fifths,.column.is-offset-two-fifths-tablet{margin-left:40%}.column.is-offset-three-fifths,.column.is-offset-three-fifths-tablet{margin-left:60%}.column.is-offset-four-fifths,.column.is-offset-four-fifths-tablet{margin-left:80%}.column.is-0,.column.is-0-tablet{flex:none;width:0%}.column.is-offset-0,.column.is-offset-0-tablet{margin-left:0%}.column.is-1,.column.is-1-tablet{flex:none;width:8.33333337%}.column.is-offset-1,.column.is-offset-1-tablet{margin-left:8.33333337%}.column.is-2,.column.is-2-tablet{flex:none;width:16.66666674%}.column.is-offset-2,.column.is-offset-2-tablet{margin-left:16.66666674%}.column.is-3,.column.is-3-tablet{flex:none;width:25%}.column.is-offset-3,.column.is-offset-3-tablet{margin-left:25%}.column.is-4,.column.is-4-tablet{flex:none;width:33.33333337%}.column.is-offset-4,.column.is-offset-4-tablet{margin-left:33.33333337%}.column.is-5,.column.is-5-tablet{flex:none;width:41.66666674%}.column.is-offset-5,.column.is-offset-5-tablet{margin-left:41.66666674%}.column.is-6,.column.is-6-tablet{flex:none;width:50%}.column.is-offset-6,.column.is-offset-6-tablet{margin-left:50%}.column.is-7,.column.is-7-tablet{flex:none;width:58.33333337%}.column.is-offset-7,.column.is-offset-7-tablet{margin-left:58.33333337%}.column.is-8,.column.is-8-tablet{flex:none;width:66.66666674%}.column.is-offset-8,.column.is-offset-8-tablet{margin-left:66.66666674%}.column.is-9,.column.is-9-tablet{flex:none;width:75%}.column.is-offset-9,.column.is-offset-9-tablet{margin-left:75%}.column.is-10,.column.is-10-tablet{flex:none;width:83.33333337%}.column.is-offset-10,.column.is-offset-10-tablet{margin-left:83.33333337%}.column.is-11,.column.is-11-tablet{flex:none;width:91.66666674%}.column.is-offset-11,.column.is-offset-11-tablet{margin-left:91.66666674%}.column.is-12,.column.is-12-tablet{flex:none;width:100%}.column.is-offset-12,.column.is-offset-12-tablet{margin-left:100%}}@media screen and (max-width: 1055px){.column.is-narrow-touch{flex:none;width:unset}.column.is-full-touch{flex:none;width:100%}.column.is-three-quarters-touch{flex:none;width:75%}.column.is-two-thirds-touch{flex:none;width:66.6666%}.column.is-half-touch{flex:none;width:50%}.column.is-one-third-touch{flex:none;width:33.3333%}.column.is-one-quarter-touch{flex:none;width:25%}.column.is-one-fifth-touch{flex:none;width:20%}.column.is-two-fifths-touch{flex:none;width:40%}.column.is-three-fifths-touch{flex:none;width:60%}.column.is-four-fifths-touch{flex:none;width:80%}.column.is-offset-three-quarters-touch{margin-left:75%}.column.is-offset-two-thirds-touch{margin-left:66.6666%}.column.is-offset-half-touch{margin-left:50%}.column.is-offset-one-third-touch{margin-left:33.3333%}.column.is-offset-one-quarter-touch{margin-left:25%}.column.is-offset-one-fifth-touch{margin-left:20%}.column.is-offset-two-fifths-touch{margin-left:40%}.column.is-offset-three-fifths-touch{margin-left:60%}.column.is-offset-four-fifths-touch{margin-left:80%}.column.is-0-touch{flex:none;width:0%}.column.is-offset-0-touch{margin-left:0%}.column.is-1-touch{flex:none;width:8.33333337%}.column.is-offset-1-touch{margin-left:8.33333337%}.column.is-2-touch{flex:none;width:16.66666674%}.column.is-offset-2-touch{margin-left:16.66666674%}.column.is-3-touch{flex:none;width:25%}.column.is-offset-3-touch{margin-left:25%}.column.is-4-touch{flex:none;width:33.33333337%}.column.is-offset-4-touch{margin-left:33.33333337%}.column.is-5-touch{flex:none;width:41.66666674%}.column.is-offset-5-touch{margin-left:41.66666674%}.column.is-6-touch{flex:none;width:50%}.column.is-offset-6-touch{margin-left:50%}.column.is-7-touch{flex:none;width:58.33333337%}.column.is-offset-7-touch{margin-left:58.33333337%}.column.is-8-touch{flex:none;width:66.66666674%}.column.is-offset-8-touch{margin-left:66.66666674%}.column.is-9-touch{flex:none;width:75%}.column.is-offset-9-touch{margin-left:75%}.column.is-10-touch{flex:none;width:83.33333337%}.column.is-offset-10-touch{margin-left:83.33333337%}.column.is-11-touch{flex:none;width:91.66666674%}.column.is-offset-11-touch{margin-left:91.66666674%}.column.is-12-touch{flex:none;width:100%}.column.is-offset-12-touch{margin-left:100%}}@media screen and (min-width: 1056px){.column.is-narrow-desktop{flex:none;width:unset}.column.is-full-desktop{flex:none;width:100%}.column.is-three-quarters-desktop{flex:none;width:75%}.column.is-two-thirds-desktop{flex:none;width:66.6666%}.column.is-half-desktop{flex:none;width:50%}.column.is-one-third-desktop{flex:none;width:33.3333%}.column.is-one-quarter-desktop{flex:none;width:25%}.column.is-one-fifth-desktop{flex:none;width:20%}.column.is-two-fifths-desktop{flex:none;width:40%}.column.is-three-fifths-desktop{flex:none;width:60%}.column.is-four-fifths-desktop{flex:none;width:80%}.column.is-offset-three-quarters-desktop{margin-left:75%}.column.is-offset-two-thirds-desktop{margin-left:66.6666%}.column.is-offset-half-desktop{margin-left:50%}.column.is-offset-one-third-desktop{margin-left:33.3333%}.column.is-offset-one-quarter-desktop{margin-left:25%}.column.is-offset-one-fifth-desktop{margin-left:20%}.column.is-offset-two-fifths-desktop{margin-left:40%}.column.is-offset-three-fifths-desktop{margin-left:60%}.column.is-offset-four-fifths-desktop{margin-left:80%}.column.is-0-desktop{flex:none;width:0%}.column.is-offset-0-desktop{margin-left:0%}.column.is-1-desktop{flex:none;width:8.33333337%}.column.is-offset-1-desktop{margin-left:8.33333337%}.column.is-2-desktop{flex:none;width:16.66666674%}.column.is-offset-2-desktop{margin-left:16.66666674%}.column.is-3-desktop{flex:none;width:25%}.column.is-offset-3-desktop{margin-left:25%}.column.is-4-desktop{flex:none;width:33.33333337%}.column.is-offset-4-desktop{margin-left:33.33333337%}.column.is-5-desktop{flex:none;width:41.66666674%}.column.is-offset-5-desktop{margin-left:41.66666674%}.column.is-6-desktop{flex:none;width:50%}.column.is-offset-6-desktop{margin-left:50%}.column.is-7-desktop{flex:none;width:58.33333337%}.column.is-offset-7-desktop{margin-left:58.33333337%}.column.is-8-desktop{flex:none;width:66.66666674%}.column.is-offset-8-desktop{margin-left:66.66666674%}.column.is-9-desktop{flex:none;width:75%}.column.is-offset-9-desktop{margin-left:75%}.column.is-10-desktop{flex:none;width:83.33333337%}.column.is-offset-10-desktop{margin-left:83.33333337%}.column.is-11-desktop{flex:none;width:91.66666674%}.column.is-offset-11-desktop{margin-left:91.66666674%}.column.is-12-desktop{flex:none;width:100%}.column.is-offset-12-desktop{margin-left:100%}}@media screen and (min-width: 1216px){.column.is-narrow-widescreen{flex:none;width:unset}.column.is-full-widescreen{flex:none;width:100%}.column.is-three-quarters-widescreen{flex:none;width:75%}.column.is-two-thirds-widescreen{flex:none;width:66.6666%}.column.is-half-widescreen{flex:none;width:50%}.column.is-one-third-widescreen{flex:none;width:33.3333%}.column.is-one-quarter-widescreen{flex:none;width:25%}.column.is-one-fifth-widescreen{flex:none;width:20%}.column.is-two-fifths-widescreen{flex:none;width:40%}.column.is-three-fifths-widescreen{flex:none;width:60%}.column.is-four-fifths-widescreen{flex:none;width:80%}.column.is-offset-three-quarters-widescreen{margin-left:75%}.column.is-offset-two-thirds-widescreen{margin-left:66.6666%}.column.is-offset-half-widescreen{margin-left:50%}.column.is-offset-one-third-widescreen{margin-left:33.3333%}.column.is-offset-one-quarter-widescreen{margin-left:25%}.column.is-offset-one-fifth-widescreen{margin-left:20%}.column.is-offset-two-fifths-widescreen{margin-left:40%}.column.is-offset-three-fifths-widescreen{margin-left:60%}.column.is-offset-four-fifths-widescreen{margin-left:80%}.column.is-0-widescreen{flex:none;width:0%}.column.is-offset-0-widescreen{margin-left:0%}.column.is-1-widescreen{flex:none;width:8.33333337%}.column.is-offset-1-widescreen{margin-left:8.33333337%}.column.is-2-widescreen{flex:none;width:16.66666674%}.column.is-offset-2-widescreen{margin-left:16.66666674%}.column.is-3-widescreen{flex:none;width:25%}.column.is-offset-3-widescreen{margin-left:25%}.column.is-4-widescreen{flex:none;width:33.33333337%}.column.is-offset-4-widescreen{margin-left:33.33333337%}.column.is-5-widescreen{flex:none;width:41.66666674%}.column.is-offset-5-widescreen{margin-left:41.66666674%}.column.is-6-widescreen{flex:none;width:50%}.column.is-offset-6-widescreen{margin-left:50%}.column.is-7-widescreen{flex:none;width:58.33333337%}.column.is-offset-7-widescreen{margin-left:58.33333337%}.column.is-8-widescreen{flex:none;width:66.66666674%}.column.is-offset-8-widescreen{margin-left:66.66666674%}.column.is-9-widescreen{flex:none;width:75%}.column.is-offset-9-widescreen{margin-left:75%}.column.is-10-widescreen{flex:none;width:83.33333337%}.column.is-offset-10-widescreen{margin-left:83.33333337%}.column.is-11-widescreen{flex:none;width:91.66666674%}.column.is-offset-11-widescreen{margin-left:91.66666674%}.column.is-12-widescreen{flex:none;width:100%}.column.is-offset-12-widescreen{margin-left:100%}}@media screen and (min-width: 1408px){.column.is-narrow-fullhd{flex:none;width:unset}.column.is-full-fullhd{flex:none;width:100%}.column.is-three-quarters-fullhd{flex:none;width:75%}.column.is-two-thirds-fullhd{flex:none;width:66.6666%}.column.is-half-fullhd{flex:none;width:50%}.column.is-one-third-fullhd{flex:none;width:33.3333%}.column.is-one-quarter-fullhd{flex:none;width:25%}.column.is-one-fifth-fullhd{flex:none;width:20%}.column.is-two-fifths-fullhd{flex:none;width:40%}.column.is-three-fifths-fullhd{flex:none;width:60%}.column.is-four-fifths-fullhd{flex:none;width:80%}.column.is-offset-three-quarters-fullhd{margin-left:75%}.column.is-offset-two-thirds-fullhd{margin-left:66.6666%}.column.is-offset-half-fullhd{margin-left:50%}.column.is-offset-one-third-fullhd{margin-left:33.3333%}.column.is-offset-one-quarter-fullhd{margin-left:25%}.column.is-offset-one-fifth-fullhd{margin-left:20%}.column.is-offset-two-fifths-fullhd{margin-left:40%}.column.is-offset-three-fifths-fullhd{margin-left:60%}.column.is-offset-four-fifths-fullhd{margin-left:80%}.column.is-0-fullhd{flex:none;width:0%}.column.is-offset-0-fullhd{margin-left:0%}.column.is-1-fullhd{flex:none;width:8.33333337%}.column.is-offset-1-fullhd{margin-left:8.33333337%}.column.is-2-fullhd{flex:none;width:16.66666674%}.column.is-offset-2-fullhd{margin-left:16.66666674%}.column.is-3-fullhd{flex:none;width:25%}.column.is-offset-3-fullhd{margin-left:25%}.column.is-4-fullhd{flex:none;width:33.33333337%}.column.is-offset-4-fullhd{margin-left:33.33333337%}.column.is-5-fullhd{flex:none;width:41.66666674%}.column.is-offset-5-fullhd{margin-left:41.66666674%}.column.is-6-fullhd{flex:none;width:50%}.column.is-offset-6-fullhd{margin-left:50%}.column.is-7-fullhd{flex:none;width:58.33333337%}.column.is-offset-7-fullhd{margin-left:58.33333337%}.column.is-8-fullhd{flex:none;width:66.66666674%}.column.is-offset-8-fullhd{margin-left:66.66666674%}.column.is-9-fullhd{flex:none;width:75%}.column.is-offset-9-fullhd{margin-left:75%}.column.is-10-fullhd{flex:none;width:83.33333337%}.column.is-offset-10-fullhd{margin-left:83.33333337%}.column.is-11-fullhd{flex:none;width:91.66666674%}.column.is-offset-11-fullhd{margin-left:91.66666674%}.column.is-12-fullhd{flex:none;width:100%}.column.is-offset-12-fullhd{margin-left:100%}}.columns{margin-left:-.75rem;margin-right:-.75rem;margin-top:-.75rem}.columns:last-child{margin-bottom:-.75rem}.columns:not(:last-child){margin-bottom:calc(1.5rem - .75rem)}.columns.is-centered{justify-content:center}.columns.is-gapless{margin-left:0;margin-right:0;margin-top:0}.columns.is-gapless>.column{margin:0;padding:0 !important}.columns.is-gapless:not(:last-child){margin-bottom:1.5rem}.columns.is-gapless:last-child{margin-bottom:0}.columns.is-mobile{display:flex}.columns.is-multiline{flex-wrap:wrap}.columns.is-vcentered{align-items:center}@media screen and (min-width: 769px),print{.columns:not(.is-desktop){display:flex}}@media screen and (min-width: 1056px){.columns.is-desktop{display:flex}}.columns.is-variable{--columnGap: 0.75rem;margin-left:calc(-1 * var(--columnGap));margin-right:calc(-1 * var(--columnGap))}.columns.is-variable>.column{padding-left:var(--columnGap);padding-right:var(--columnGap)}.columns.is-variable.is-0{--columnGap: 0rem}@media screen and (max-width: 768px){.columns.is-variable.is-0-mobile{--columnGap: 0rem}}@media screen and (min-width: 769px),print{.columns.is-variable.is-0-tablet{--columnGap: 0rem}}@media screen and (min-width: 769px) and (max-width: 1055px){.columns.is-variable.is-0-tablet-only{--columnGap: 0rem}}@media screen and (max-width: 1055px){.columns.is-variable.is-0-touch{--columnGap: 0rem}}@media screen and (min-width: 1056px){.columns.is-variable.is-0-desktop{--columnGap: 0rem}}@media screen and (min-width: 1056px) and (max-width: 1215px){.columns.is-variable.is-0-desktop-only{--columnGap: 0rem}}@media screen and (min-width: 1216px){.columns.is-variable.is-0-widescreen{--columnGap: 0rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){.columns.is-variable.is-0-widescreen-only{--columnGap: 0rem}}@media screen and (min-width: 1408px){.columns.is-variable.is-0-fullhd{--columnGap: 0rem}}.columns.is-variable.is-1{--columnGap: .25rem}@media screen and (max-width: 768px){.columns.is-variable.is-1-mobile{--columnGap: .25rem}}@media screen and (min-width: 769px),print{.columns.is-variable.is-1-tablet{--columnGap: .25rem}}@media screen and (min-width: 769px) and (max-width: 1055px){.columns.is-variable.is-1-tablet-only{--columnGap: .25rem}}@media screen and (max-width: 1055px){.columns.is-variable.is-1-touch{--columnGap: .25rem}}@media screen and (min-width: 1056px){.columns.is-variable.is-1-desktop{--columnGap: .25rem}}@media screen and (min-width: 1056px) and (max-width: 1215px){.columns.is-variable.is-1-desktop-only{--columnGap: .25rem}}@media screen and (min-width: 1216px){.columns.is-variable.is-1-widescreen{--columnGap: .25rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){.columns.is-variable.is-1-widescreen-only{--columnGap: .25rem}}@media screen and (min-width: 1408px){.columns.is-variable.is-1-fullhd{--columnGap: .25rem}}.columns.is-variable.is-2{--columnGap: .5rem}@media screen and (max-width: 768px){.columns.is-variable.is-2-mobile{--columnGap: .5rem}}@media screen and (min-width: 769px),print{.columns.is-variable.is-2-tablet{--columnGap: .5rem}}@media screen and (min-width: 769px) and (max-width: 1055px){.columns.is-variable.is-2-tablet-only{--columnGap: .5rem}}@media screen and (max-width: 1055px){.columns.is-variable.is-2-touch{--columnGap: .5rem}}@media screen and (min-width: 1056px){.columns.is-variable.is-2-desktop{--columnGap: .5rem}}@media screen and (min-width: 1056px) and (max-width: 1215px){.columns.is-variable.is-2-desktop-only{--columnGap: .5rem}}@media screen and (min-width: 1216px){.columns.is-variable.is-2-widescreen{--columnGap: .5rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){.columns.is-variable.is-2-widescreen-only{--columnGap: .5rem}}@media screen and (min-width: 1408px){.columns.is-variable.is-2-fullhd{--columnGap: .5rem}}.columns.is-variable.is-3{--columnGap: .75rem}@media screen and (max-width: 768px){.columns.is-variable.is-3-mobile{--columnGap: .75rem}}@media screen and (min-width: 769px),print{.columns.is-variable.is-3-tablet{--columnGap: .75rem}}@media screen and (min-width: 769px) and (max-width: 1055px){.columns.is-variable.is-3-tablet-only{--columnGap: .75rem}}@media screen and (max-width: 1055px){.columns.is-variable.is-3-touch{--columnGap: .75rem}}@media screen and (min-width: 1056px){.columns.is-variable.is-3-desktop{--columnGap: .75rem}}@media screen and (min-width: 1056px) and (max-width: 1215px){.columns.is-variable.is-3-desktop-only{--columnGap: .75rem}}@media screen and (min-width: 1216px){.columns.is-variable.is-3-widescreen{--columnGap: .75rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){.columns.is-variable.is-3-widescreen-only{--columnGap: .75rem}}@media screen and (min-width: 1408px){.columns.is-variable.is-3-fullhd{--columnGap: .75rem}}.columns.is-variable.is-4{--columnGap: 1rem}@media screen and (max-width: 768px){.columns.is-variable.is-4-mobile{--columnGap: 1rem}}@media screen and (min-width: 769px),print{.columns.is-variable.is-4-tablet{--columnGap: 1rem}}@media screen and (min-width: 769px) and (max-width: 1055px){.columns.is-variable.is-4-tablet-only{--columnGap: 1rem}}@media screen and (max-width: 1055px){.columns.is-variable.is-4-touch{--columnGap: 1rem}}@media screen and (min-width: 1056px){.columns.is-variable.is-4-desktop{--columnGap: 1rem}}@media screen and (min-width: 1056px) and (max-width: 1215px){.columns.is-variable.is-4-desktop-only{--columnGap: 1rem}}@media screen and (min-width: 1216px){.columns.is-variable.is-4-widescreen{--columnGap: 1rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){.columns.is-variable.is-4-widescreen-only{--columnGap: 1rem}}@media screen and (min-width: 1408px){.columns.is-variable.is-4-fullhd{--columnGap: 1rem}}.columns.is-variable.is-5{--columnGap: 1.25rem}@media screen and (max-width: 768px){.columns.is-variable.is-5-mobile{--columnGap: 1.25rem}}@media screen and (min-width: 769px),print{.columns.is-variable.is-5-tablet{--columnGap: 1.25rem}}@media screen and (min-width: 769px) and (max-width: 1055px){.columns.is-variable.is-5-tablet-only{--columnGap: 1.25rem}}@media screen and (max-width: 1055px){.columns.is-variable.is-5-touch{--columnGap: 1.25rem}}@media screen and (min-width: 1056px){.columns.is-variable.is-5-desktop{--columnGap: 1.25rem}}@media screen and (min-width: 1056px) and (max-width: 1215px){.columns.is-variable.is-5-desktop-only{--columnGap: 1.25rem}}@media screen and (min-width: 1216px){.columns.is-variable.is-5-widescreen{--columnGap: 1.25rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){.columns.is-variable.is-5-widescreen-only{--columnGap: 1.25rem}}@media screen and (min-width: 1408px){.columns.is-variable.is-5-fullhd{--columnGap: 1.25rem}}.columns.is-variable.is-6{--columnGap: 1.5rem}@media screen and (max-width: 768px){.columns.is-variable.is-6-mobile{--columnGap: 1.5rem}}@media screen and (min-width: 769px),print{.columns.is-variable.is-6-tablet{--columnGap: 1.5rem}}@media screen and (min-width: 769px) and (max-width: 1055px){.columns.is-variable.is-6-tablet-only{--columnGap: 1.5rem}}@media screen and (max-width: 1055px){.columns.is-variable.is-6-touch{--columnGap: 1.5rem}}@media screen and (min-width: 1056px){.columns.is-variable.is-6-desktop{--columnGap: 1.5rem}}@media screen and (min-width: 1056px) and (max-width: 1215px){.columns.is-variable.is-6-desktop-only{--columnGap: 1.5rem}}@media screen and (min-width: 1216px){.columns.is-variable.is-6-widescreen{--columnGap: 1.5rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){.columns.is-variable.is-6-widescreen-only{--columnGap: 1.5rem}}@media screen and (min-width: 1408px){.columns.is-variable.is-6-fullhd{--columnGap: 1.5rem}}.columns.is-variable.is-7{--columnGap: 1.75rem}@media screen and (max-width: 768px){.columns.is-variable.is-7-mobile{--columnGap: 1.75rem}}@media screen and (min-width: 769px),print{.columns.is-variable.is-7-tablet{--columnGap: 1.75rem}}@media screen and (min-width: 769px) and (max-width: 1055px){.columns.is-variable.is-7-tablet-only{--columnGap: 1.75rem}}@media screen and (max-width: 1055px){.columns.is-variable.is-7-touch{--columnGap: 1.75rem}}@media screen and (min-width: 1056px){.columns.is-variable.is-7-desktop{--columnGap: 1.75rem}}@media screen and (min-width: 1056px) and (max-width: 1215px){.columns.is-variable.is-7-desktop-only{--columnGap: 1.75rem}}@media screen and (min-width: 1216px){.columns.is-variable.is-7-widescreen{--columnGap: 1.75rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){.columns.is-variable.is-7-widescreen-only{--columnGap: 1.75rem}}@media screen and (min-width: 1408px){.columns.is-variable.is-7-fullhd{--columnGap: 1.75rem}}.columns.is-variable.is-8{--columnGap: 2rem}@media screen and (max-width: 768px){.columns.is-variable.is-8-mobile{--columnGap: 2rem}}@media screen and (min-width: 769px),print{.columns.is-variable.is-8-tablet{--columnGap: 2rem}}@media screen and (min-width: 769px) and (max-width: 1055px){.columns.is-variable.is-8-tablet-only{--columnGap: 2rem}}@media screen and (max-width: 1055px){.columns.is-variable.is-8-touch{--columnGap: 2rem}}@media screen and (min-width: 1056px){.columns.is-variable.is-8-desktop{--columnGap: 2rem}}@media screen and (min-width: 1056px) and (max-width: 1215px){.columns.is-variable.is-8-desktop-only{--columnGap: 2rem}}@media screen and (min-width: 1216px){.columns.is-variable.is-8-widescreen{--columnGap: 2rem}}@media screen and (min-width: 1216px) and (max-width: 1407px){.columns.is-variable.is-8-widescreen-only{--columnGap: 2rem}}@media screen and (min-width: 1408px){.columns.is-variable.is-8-fullhd{--columnGap: 2rem}}.tile{align-items:stretch;display:block;flex-basis:0;flex-grow:1;flex-shrink:1;min-height:min-content}.tile.is-ancestor{margin-left:-.75rem;margin-right:-.75rem;margin-top:-.75rem}.tile.is-ancestor:last-child{margin-bottom:-.75rem}.tile.is-ancestor:not(:last-child){margin-bottom:.75rem}.tile.is-child{margin:0 !important}.tile.is-parent{padding:.75rem}.tile.is-vertical{flex-direction:column}.tile.is-vertical>.tile.is-child:not(:last-child){margin-bottom:1.5rem !important}@media screen and (min-width: 769px),print{.tile:not(.is-child){display:flex}.tile.is-1{flex:none;width:8.33333337%}.tile.is-2{flex:none;width:16.66666674%}.tile.is-3{flex:none;width:25%}.tile.is-4{flex:none;width:33.33333337%}.tile.is-5{flex:none;width:41.66666674%}.tile.is-6{flex:none;width:50%}.tile.is-7{flex:none;width:58.33333337%}.tile.is-8{flex:none;width:66.66666674%}.tile.is-9{flex:none;width:75%}.tile.is-10{flex:none;width:83.33333337%}.tile.is-11{flex:none;width:91.66666674%}.tile.is-12{flex:none;width:100%}}.hero{align-items:stretch;display:flex;flex-direction:column;justify-content:space-between}.hero .navbar{background:none}.hero .tabs ul{border-bottom:none}.hero.is-white{background-color:#fff;color:#0a0a0a}.hero.is-white a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-white strong{color:inherit}.hero.is-white .title{color:#0a0a0a}.hero.is-white .subtitle{color:rgba(10,10,10,0.9)}.hero.is-white .subtitle a:not(.button),.hero.is-white .subtitle strong{color:#0a0a0a}@media screen and (max-width: 1055px){.hero.is-white .navbar-menu{background-color:#fff}}.hero.is-white .navbar-item,.hero.is-white .navbar-link{color:rgba(10,10,10,0.7)}.hero.is-white a.navbar-item:hover,.hero.is-white a.navbar-item.is-active,.hero.is-white .navbar-link:hover,.hero.is-white .navbar-link.is-active{background-color:#f2f2f2;color:#0a0a0a}.hero.is-white .tabs a{color:#0a0a0a;opacity:0.9}.hero.is-white .tabs a:hover{opacity:1}.hero.is-white .tabs li.is-active a{color:#fff !important;opacity:1}.hero.is-white .tabs.is-boxed a,.hero.is-white .tabs.is-toggle a{color:#0a0a0a}.hero.is-white .tabs.is-boxed a:hover,.hero.is-white .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}.hero.is-white .tabs.is-boxed li.is-active a,.hero.is-white .tabs.is-boxed li.is-active a:hover,.hero.is-white .tabs.is-toggle li.is-active a,.hero.is-white .tabs.is-toggle li.is-active a:hover{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.hero.is-white.is-bold{background-image:linear-gradient(141deg, #e8e3e4 0%, #fff 71%, #fff 100%)}@media screen and (max-width: 768px){.hero.is-white.is-bold .navbar-menu{background-image:linear-gradient(141deg, #e8e3e4 0%, #fff 71%, #fff 100%)}}.hero.is-black{background-color:#0a0a0a;color:#fff}.hero.is-black a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-black strong{color:inherit}.hero.is-black .title{color:#fff}.hero.is-black .subtitle{color:rgba(255,255,255,0.9)}.hero.is-black .subtitle a:not(.button),.hero.is-black .subtitle strong{color:#fff}@media screen and (max-width: 1055px){.hero.is-black .navbar-menu{background-color:#0a0a0a}}.hero.is-black .navbar-item,.hero.is-black .navbar-link{color:rgba(255,255,255,0.7)}.hero.is-black a.navbar-item:hover,.hero.is-black a.navbar-item.is-active,.hero.is-black .navbar-link:hover,.hero.is-black .navbar-link.is-active{background-color:#000;color:#fff}.hero.is-black .tabs a{color:#fff;opacity:0.9}.hero.is-black .tabs a:hover{opacity:1}.hero.is-black .tabs li.is-active a{color:#0a0a0a !important;opacity:1}.hero.is-black .tabs.is-boxed a,.hero.is-black .tabs.is-toggle a{color:#fff}.hero.is-black .tabs.is-boxed a:hover,.hero.is-black .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}.hero.is-black .tabs.is-boxed li.is-active a,.hero.is-black .tabs.is-boxed li.is-active a:hover,.hero.is-black .tabs.is-toggle li.is-active a,.hero.is-black .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#0a0a0a}.hero.is-black.is-bold{background-image:linear-gradient(141deg, #000 0%, #0a0a0a 71%, #181616 100%)}@media screen and (max-width: 768px){.hero.is-black.is-bold .navbar-menu{background-image:linear-gradient(141deg, #000 0%, #0a0a0a 71%, #181616 100%)}}.hero.is-light{background-color:#f5f5f5;color:rgba(0,0,0,0.7)}.hero.is-light a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-light strong{color:inherit}.hero.is-light .title{color:rgba(0,0,0,0.7)}.hero.is-light .subtitle{color:rgba(0,0,0,0.9)}.hero.is-light .subtitle a:not(.button),.hero.is-light .subtitle strong{color:rgba(0,0,0,0.7)}@media screen and (max-width: 1055px){.hero.is-light .navbar-menu{background-color:#f5f5f5}}.hero.is-light .navbar-item,.hero.is-light .navbar-link{color:rgba(0,0,0,0.7)}.hero.is-light a.navbar-item:hover,.hero.is-light a.navbar-item.is-active,.hero.is-light .navbar-link:hover,.hero.is-light .navbar-link.is-active{background-color:#e8e8e8;color:rgba(0,0,0,0.7)}.hero.is-light .tabs a{color:rgba(0,0,0,0.7);opacity:0.9}.hero.is-light .tabs a:hover{opacity:1}.hero.is-light .tabs li.is-active a{color:#f5f5f5 !important;opacity:1}.hero.is-light .tabs.is-boxed a,.hero.is-light .tabs.is-toggle a{color:rgba(0,0,0,0.7)}.hero.is-light .tabs.is-boxed a:hover,.hero.is-light .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}.hero.is-light .tabs.is-boxed li.is-active a,.hero.is-light .tabs.is-boxed li.is-active a:hover,.hero.is-light .tabs.is-toggle li.is-active a,.hero.is-light .tabs.is-toggle li.is-active a:hover{background-color:rgba(0,0,0,0.7);border-color:rgba(0,0,0,0.7);color:#f5f5f5}.hero.is-light.is-bold{background-image:linear-gradient(141deg, #dfd8d9 0%, #f5f5f5 71%, #fff 100%)}@media screen and (max-width: 768px){.hero.is-light.is-bold .navbar-menu{background-image:linear-gradient(141deg, #dfd8d9 0%, #f5f5f5 71%, #fff 100%)}}.hero.is-dark,.content kbd.hero{background-color:#363636;color:#fff}.hero.is-dark a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.content kbd.hero a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-dark strong,.content kbd.hero strong{color:inherit}.hero.is-dark .title,.content kbd.hero .title{color:#fff}.hero.is-dark .subtitle,.content kbd.hero .subtitle{color:rgba(255,255,255,0.9)}.hero.is-dark .subtitle a:not(.button),.content kbd.hero .subtitle a:not(.button),.hero.is-dark .subtitle strong,.content kbd.hero .subtitle strong{color:#fff}@media screen and (max-width: 1055px){.hero.is-dark .navbar-menu,.content kbd.hero .navbar-menu{background-color:#363636}}.hero.is-dark .navbar-item,.content kbd.hero .navbar-item,.hero.is-dark .navbar-link,.content kbd.hero .navbar-link{color:rgba(255,255,255,0.7)}.hero.is-dark a.navbar-item:hover,.content kbd.hero a.navbar-item:hover,.hero.is-dark a.navbar-item.is-active,.content kbd.hero a.navbar-item.is-active,.hero.is-dark .navbar-link:hover,.content kbd.hero .navbar-link:hover,.hero.is-dark .navbar-link.is-active,.content kbd.hero .navbar-link.is-active{background-color:#292929;color:#fff}.hero.is-dark .tabs a,.content kbd.hero .tabs a{color:#fff;opacity:0.9}.hero.is-dark .tabs a:hover,.content kbd.hero .tabs a:hover{opacity:1}.hero.is-dark .tabs li.is-active a,.content kbd.hero .tabs li.is-active a{color:#363636 !important;opacity:1}.hero.is-dark .tabs.is-boxed a,.content kbd.hero .tabs.is-boxed a,.hero.is-dark .tabs.is-toggle a,.content kbd.hero .tabs.is-toggle a{color:#fff}.hero.is-dark .tabs.is-boxed a:hover,.content kbd.hero .tabs.is-boxed a:hover,.hero.is-dark .tabs.is-toggle a:hover,.content kbd.hero .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}.hero.is-dark .tabs.is-boxed li.is-active a,.content kbd.hero .tabs.is-boxed li.is-active a,.hero.is-dark .tabs.is-boxed li.is-active a:hover,.hero.is-dark .tabs.is-toggle li.is-active a,.content kbd.hero .tabs.is-toggle li.is-active a,.hero.is-dark .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#363636}.hero.is-dark.is-bold,.content kbd.hero.is-bold{background-image:linear-gradient(141deg, #1f191a 0%, #363636 71%, #46403f 100%)}@media screen and (max-width: 768px){.hero.is-dark.is-bold .navbar-menu,.content kbd.hero.is-bold .navbar-menu{background-image:linear-gradient(141deg, #1f191a 0%, #363636 71%, #46403f 100%)}}.hero.is-primary,.docstring>section>a.hero.docs-sourcelink{background-color:#4eb5de;color:#fff}.hero.is-primary a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.docstring>section>a.hero.docs-sourcelink a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-primary strong,.docstring>section>a.hero.docs-sourcelink strong{color:inherit}.hero.is-primary .title,.docstring>section>a.hero.docs-sourcelink .title{color:#fff}.hero.is-primary .subtitle,.docstring>section>a.hero.docs-sourcelink .subtitle{color:rgba(255,255,255,0.9)}.hero.is-primary .subtitle a:not(.button),.docstring>section>a.hero.docs-sourcelink .subtitle a:not(.button),.hero.is-primary .subtitle strong,.docstring>section>a.hero.docs-sourcelink .subtitle strong{color:#fff}@media screen and (max-width: 1055px){.hero.is-primary .navbar-menu,.docstring>section>a.hero.docs-sourcelink .navbar-menu{background-color:#4eb5de}}.hero.is-primary .navbar-item,.docstring>section>a.hero.docs-sourcelink .navbar-item,.hero.is-primary .navbar-link,.docstring>section>a.hero.docs-sourcelink .navbar-link{color:rgba(255,255,255,0.7)}.hero.is-primary a.navbar-item:hover,.docstring>section>a.hero.docs-sourcelink a.navbar-item:hover,.hero.is-primary a.navbar-item.is-active,.docstring>section>a.hero.docs-sourcelink a.navbar-item.is-active,.hero.is-primary .navbar-link:hover,.docstring>section>a.hero.docs-sourcelink .navbar-link:hover,.hero.is-primary .navbar-link.is-active,.docstring>section>a.hero.docs-sourcelink .navbar-link.is-active{background-color:#39acda;color:#fff}.hero.is-primary .tabs a,.docstring>section>a.hero.docs-sourcelink .tabs a{color:#fff;opacity:0.9}.hero.is-primary .tabs a:hover,.docstring>section>a.hero.docs-sourcelink .tabs a:hover{opacity:1}.hero.is-primary .tabs li.is-active a,.docstring>section>a.hero.docs-sourcelink .tabs li.is-active a{color:#4eb5de !important;opacity:1}.hero.is-primary .tabs.is-boxed a,.docstring>section>a.hero.docs-sourcelink .tabs.is-boxed a,.hero.is-primary .tabs.is-toggle a,.docstring>section>a.hero.docs-sourcelink .tabs.is-toggle a{color:#fff}.hero.is-primary .tabs.is-boxed a:hover,.docstring>section>a.hero.docs-sourcelink .tabs.is-boxed a:hover,.hero.is-primary .tabs.is-toggle a:hover,.docstring>section>a.hero.docs-sourcelink .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}.hero.is-primary .tabs.is-boxed li.is-active a,.docstring>section>a.hero.docs-sourcelink .tabs.is-boxed li.is-active a,.hero.is-primary .tabs.is-boxed li.is-active a:hover,.hero.is-primary .tabs.is-toggle li.is-active a,.docstring>section>a.hero.docs-sourcelink .tabs.is-toggle li.is-active a,.hero.is-primary .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#4eb5de}.hero.is-primary.is-bold,.docstring>section>a.hero.is-bold.docs-sourcelink{background-image:linear-gradient(141deg, #1bc7de 0%, #4eb5de 71%, #5fa9e7 100%)}@media screen and (max-width: 768px){.hero.is-primary.is-bold .navbar-menu,.docstring>section>a.hero.is-bold.docs-sourcelink .navbar-menu{background-image:linear-gradient(141deg, #1bc7de 0%, #4eb5de 71%, #5fa9e7 100%)}}.hero.is-link{background-color:#2e63b8;color:#fff}.hero.is-link a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-link strong{color:inherit}.hero.is-link .title{color:#fff}.hero.is-link .subtitle{color:rgba(255,255,255,0.9)}.hero.is-link .subtitle a:not(.button),.hero.is-link .subtitle strong{color:#fff}@media screen and (max-width: 1055px){.hero.is-link .navbar-menu{background-color:#2e63b8}}.hero.is-link .navbar-item,.hero.is-link .navbar-link{color:rgba(255,255,255,0.7)}.hero.is-link a.navbar-item:hover,.hero.is-link a.navbar-item.is-active,.hero.is-link .navbar-link:hover,.hero.is-link .navbar-link.is-active{background-color:#2958a4;color:#fff}.hero.is-link .tabs a{color:#fff;opacity:0.9}.hero.is-link .tabs a:hover{opacity:1}.hero.is-link .tabs li.is-active a{color:#2e63b8 !important;opacity:1}.hero.is-link .tabs.is-boxed a,.hero.is-link .tabs.is-toggle a{color:#fff}.hero.is-link .tabs.is-boxed a:hover,.hero.is-link .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}.hero.is-link .tabs.is-boxed li.is-active a,.hero.is-link .tabs.is-boxed li.is-active a:hover,.hero.is-link .tabs.is-toggle li.is-active a,.hero.is-link .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#2e63b8}.hero.is-link.is-bold{background-image:linear-gradient(141deg, #1b6098 0%, #2e63b8 71%, #2d51d2 100%)}@media screen and (max-width: 768px){.hero.is-link.is-bold .navbar-menu{background-image:linear-gradient(141deg, #1b6098 0%, #2e63b8 71%, #2d51d2 100%)}}.hero.is-info{background-color:#209cee;color:#fff}.hero.is-info a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-info strong{color:inherit}.hero.is-info .title{color:#fff}.hero.is-info .subtitle{color:rgba(255,255,255,0.9)}.hero.is-info .subtitle a:not(.button),.hero.is-info .subtitle strong{color:#fff}@media screen and (max-width: 1055px){.hero.is-info .navbar-menu{background-color:#209cee}}.hero.is-info .navbar-item,.hero.is-info .navbar-link{color:rgba(255,255,255,0.7)}.hero.is-info a.navbar-item:hover,.hero.is-info a.navbar-item.is-active,.hero.is-info .navbar-link:hover,.hero.is-info .navbar-link.is-active{background-color:#1190e3;color:#fff}.hero.is-info .tabs a{color:#fff;opacity:0.9}.hero.is-info .tabs a:hover{opacity:1}.hero.is-info .tabs li.is-active a{color:#209cee !important;opacity:1}.hero.is-info .tabs.is-boxed a,.hero.is-info .tabs.is-toggle a{color:#fff}.hero.is-info .tabs.is-boxed a:hover,.hero.is-info .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}.hero.is-info .tabs.is-boxed li.is-active a,.hero.is-info .tabs.is-boxed li.is-active a:hover,.hero.is-info .tabs.is-toggle li.is-active a,.hero.is-info .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#209cee}.hero.is-info.is-bold{background-image:linear-gradient(141deg, #05a6d6 0%, #209cee 71%, #3287f5 100%)}@media screen and (max-width: 768px){.hero.is-info.is-bold .navbar-menu{background-image:linear-gradient(141deg, #05a6d6 0%, #209cee 71%, #3287f5 100%)}}.hero.is-success{background-color:#22c35b;color:#fff}.hero.is-success a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-success strong{color:inherit}.hero.is-success .title{color:#fff}.hero.is-success .subtitle{color:rgba(255,255,255,0.9)}.hero.is-success .subtitle a:not(.button),.hero.is-success .subtitle strong{color:#fff}@media screen and (max-width: 1055px){.hero.is-success .navbar-menu{background-color:#22c35b}}.hero.is-success .navbar-item,.hero.is-success .navbar-link{color:rgba(255,255,255,0.7)}.hero.is-success a.navbar-item:hover,.hero.is-success a.navbar-item.is-active,.hero.is-success .navbar-link:hover,.hero.is-success .navbar-link.is-active{background-color:#1ead51;color:#fff}.hero.is-success .tabs a{color:#fff;opacity:0.9}.hero.is-success .tabs a:hover{opacity:1}.hero.is-success .tabs li.is-active a{color:#22c35b !important;opacity:1}.hero.is-success .tabs.is-boxed a,.hero.is-success .tabs.is-toggle a{color:#fff}.hero.is-success .tabs.is-boxed a:hover,.hero.is-success .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}.hero.is-success .tabs.is-boxed li.is-active a,.hero.is-success .tabs.is-boxed li.is-active a:hover,.hero.is-success .tabs.is-toggle li.is-active a,.hero.is-success .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#22c35b}.hero.is-success.is-bold{background-image:linear-gradient(141deg, #12a02c 0%, #22c35b 71%, #1fdf83 100%)}@media screen and (max-width: 768px){.hero.is-success.is-bold .navbar-menu{background-image:linear-gradient(141deg, #12a02c 0%, #22c35b 71%, #1fdf83 100%)}}.hero.is-warning{background-color:#ffdd57;color:rgba(0,0,0,0.7)}.hero.is-warning a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-warning strong{color:inherit}.hero.is-warning .title{color:rgba(0,0,0,0.7)}.hero.is-warning .subtitle{color:rgba(0,0,0,0.9)}.hero.is-warning .subtitle a:not(.button),.hero.is-warning .subtitle strong{color:rgba(0,0,0,0.7)}@media screen and (max-width: 1055px){.hero.is-warning .navbar-menu{background-color:#ffdd57}}.hero.is-warning .navbar-item,.hero.is-warning .navbar-link{color:rgba(0,0,0,0.7)}.hero.is-warning a.navbar-item:hover,.hero.is-warning a.navbar-item.is-active,.hero.is-warning .navbar-link:hover,.hero.is-warning .navbar-link.is-active{background-color:#ffd83e;color:rgba(0,0,0,0.7)}.hero.is-warning .tabs a{color:rgba(0,0,0,0.7);opacity:0.9}.hero.is-warning .tabs a:hover{opacity:1}.hero.is-warning .tabs li.is-active a{color:#ffdd57 !important;opacity:1}.hero.is-warning .tabs.is-boxed a,.hero.is-warning .tabs.is-toggle a{color:rgba(0,0,0,0.7)}.hero.is-warning .tabs.is-boxed a:hover,.hero.is-warning .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}.hero.is-warning .tabs.is-boxed li.is-active a,.hero.is-warning .tabs.is-boxed li.is-active a:hover,.hero.is-warning .tabs.is-toggle li.is-active a,.hero.is-warning .tabs.is-toggle li.is-active a:hover{background-color:rgba(0,0,0,0.7);border-color:rgba(0,0,0,0.7);color:#ffdd57}.hero.is-warning.is-bold{background-image:linear-gradient(141deg, #ffae24 0%, #ffdd57 71%, #fffa71 100%)}@media screen and (max-width: 768px){.hero.is-warning.is-bold .navbar-menu{background-image:linear-gradient(141deg, #ffae24 0%, #ffdd57 71%, #fffa71 100%)}}.hero.is-danger{background-color:#da0b00;color:#fff}.hero.is-danger a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-danger strong{color:inherit}.hero.is-danger .title{color:#fff}.hero.is-danger .subtitle{color:rgba(255,255,255,0.9)}.hero.is-danger .subtitle a:not(.button),.hero.is-danger .subtitle strong{color:#fff}@media screen and (max-width: 1055px){.hero.is-danger .navbar-menu{background-color:#da0b00}}.hero.is-danger .navbar-item,.hero.is-danger .navbar-link{color:rgba(255,255,255,0.7)}.hero.is-danger a.navbar-item:hover,.hero.is-danger a.navbar-item.is-active,.hero.is-danger .navbar-link:hover,.hero.is-danger .navbar-link.is-active{background-color:#c10a00;color:#fff}.hero.is-danger .tabs a{color:#fff;opacity:0.9}.hero.is-danger .tabs a:hover{opacity:1}.hero.is-danger .tabs li.is-active a{color:#da0b00 !important;opacity:1}.hero.is-danger .tabs.is-boxed a,.hero.is-danger .tabs.is-toggle a{color:#fff}.hero.is-danger .tabs.is-boxed a:hover,.hero.is-danger .tabs.is-toggle a:hover{background-color:rgba(10,10,10,0.1)}.hero.is-danger .tabs.is-boxed li.is-active a,.hero.is-danger .tabs.is-boxed li.is-active a:hover,.hero.is-danger .tabs.is-toggle li.is-active a,.hero.is-danger .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#da0b00}.hero.is-danger.is-bold{background-image:linear-gradient(141deg, #a70013 0%, #da0b00 71%, #f43500 100%)}@media screen and (max-width: 768px){.hero.is-danger.is-bold .navbar-menu{background-image:linear-gradient(141deg, #a70013 0%, #da0b00 71%, #f43500 100%)}}.hero.is-small .hero-body,#documenter .docs-sidebar form.docs-search>input.hero .hero-body{padding:1.5rem}@media screen and (min-width: 769px),print{.hero.is-medium .hero-body{padding:9rem 4.5rem}}@media screen and (min-width: 769px),print{.hero.is-large .hero-body{padding:18rem 6rem}}.hero.is-halfheight .hero-body,.hero.is-fullheight .hero-body,.hero.is-fullheight-with-navbar .hero-body{align-items:center;display:flex}.hero.is-halfheight .hero-body>.container,.hero.is-fullheight .hero-body>.container,.hero.is-fullheight-with-navbar .hero-body>.container{flex-grow:1;flex-shrink:1}.hero.is-halfheight{min-height:50vh}.hero.is-fullheight{min-height:100vh}.hero-video{overflow:hidden}.hero-video video{left:50%;min-height:100%;min-width:100%;position:absolute;top:50%;transform:translate3d(-50%, -50%, 0)}.hero-video.is-transparent{opacity:0.3}@media screen and (max-width: 768px){.hero-video{display:none}}.hero-buttons{margin-top:1.5rem}@media screen and (max-width: 768px){.hero-buttons .button{display:flex}.hero-buttons .button:not(:last-child){margin-bottom:0.75rem}}@media screen and (min-width: 769px),print{.hero-buttons{display:flex;justify-content:center}.hero-buttons .button:not(:last-child){margin-right:1.5rem}}.hero-head,.hero-foot{flex-grow:0;flex-shrink:0}.hero-body{flex-grow:1;flex-shrink:0;padding:3rem 1.5rem}@media screen and (min-width: 769px),print{.hero-body{padding:3rem 3rem}}.section{padding:3rem 1.5rem}@media screen and (min-width: 1056px){.section{padding:3rem 3rem}.section.is-medium{padding:9rem 4.5rem}.section.is-large{padding:18rem 6rem}}.footer{background-color:#fafafa;padding:3rem 1.5rem 6rem}h1 .docs-heading-anchor,h1 .docs-heading-anchor:hover,h1 .docs-heading-anchor:visited,h2 .docs-heading-anchor,h2 .docs-heading-anchor:hover,h2 .docs-heading-anchor:visited,h3 .docs-heading-anchor,h3 .docs-heading-anchor:hover,h3 .docs-heading-anchor:visited,h4 .docs-heading-anchor,h4 .docs-heading-anchor:hover,h4 .docs-heading-anchor:visited,h5 .docs-heading-anchor,h5 .docs-heading-anchor:hover,h5 .docs-heading-anchor:visited,h6 .docs-heading-anchor,h6 .docs-heading-anchor:hover,h6 .docs-heading-anchor:visited{color:#222}h1 .docs-heading-anchor-permalink,h2 .docs-heading-anchor-permalink,h3 .docs-heading-anchor-permalink,h4 .docs-heading-anchor-permalink,h5 .docs-heading-anchor-permalink,h6 .docs-heading-anchor-permalink{visibility:hidden;vertical-align:middle;margin-left:0.5em;font-size:0.7rem}h1 .docs-heading-anchor-permalink::before,h2 .docs-heading-anchor-permalink::before,h3 .docs-heading-anchor-permalink::before,h4 .docs-heading-anchor-permalink::before,h5 .docs-heading-anchor-permalink::before,h6 .docs-heading-anchor-permalink::before{font-family:"Font Awesome 6 Free";font-weight:900;content:"\f0c1"}h1:hover .docs-heading-anchor-permalink,h2:hover .docs-heading-anchor-permalink,h3:hover .docs-heading-anchor-permalink,h4:hover .docs-heading-anchor-permalink,h5:hover .docs-heading-anchor-permalink,h6:hover .docs-heading-anchor-permalink{visibility:visible}.docs-dark-only{display:none !important}pre{position:relative;overflow:hidden}pre code,pre code.hljs{padding:0 .75rem !important;overflow:auto;display:block}pre code:first-of-type,pre code.hljs:first-of-type{padding-top:0.5rem !important}pre code:last-of-type,pre code.hljs:last-of-type{padding-bottom:0.5rem !important}pre .copy-button{opacity:0.2;transition:opacity 0.2s;position:absolute;right:0em;top:0em;padding:0.5em;width:2.5em;height:2.5em;background:transparent;border:none;font-family:"Font Awesome 6 Free";color:#222;cursor:pointer;text-align:center}pre .copy-button:focus,pre .copy-button:hover{opacity:1;background:rgba(34,34,34,0.1);color:#2e63b8}pre .copy-button.success{color:#259a12;opacity:1}pre .copy-button.error{color:#cb3c33;opacity:1}pre:hover .copy-button{opacity:1}.admonition{background-color:#b5b5b5;border-style:solid;border-width:1px;border-color:#363636;border-radius:4px;font-size:1rem}.admonition strong{color:currentColor}.admonition.is-small,#documenter .docs-sidebar form.docs-search>input.admonition{font-size:.75rem}.admonition.is-medium{font-size:1.25rem}.admonition.is-large{font-size:1.5rem}.admonition.is-default{background-color:#b5b5b5;border-color:#363636}.admonition.is-default>.admonition-header{background-color:#363636;color:#fff}.admonition.is-default>.admonition-body{color:#fff}.admonition.is-info{background-color:#def0fc;border-color:#209cee}.admonition.is-info>.admonition-header{background-color:#209cee;color:#fff}.admonition.is-info>.admonition-body{color:rgba(0,0,0,0.7)}.admonition.is-success{background-color:#bdf4d1;border-color:#22c35b}.admonition.is-success>.admonition-header{background-color:#22c35b;color:#fff}.admonition.is-success>.admonition-body{color:rgba(0,0,0,0.7)}.admonition.is-warning{background-color:#fff3c5;border-color:#ffdd57}.admonition.is-warning>.admonition-header{background-color:#ffdd57;color:rgba(0,0,0,0.7)}.admonition.is-warning>.admonition-body{color:rgba(0,0,0,0.7)}.admonition.is-danger{background-color:#ffaba7;border-color:#da0b00}.admonition.is-danger>.admonition-header{background-color:#da0b00;color:#fff}.admonition.is-danger>.admonition-body{color:rgba(0,0,0,0.7)}.admonition.is-compat{background-color:#bdeff5;border-color:#1db5c9}.admonition.is-compat>.admonition-header{background-color:#1db5c9;color:#fff}.admonition.is-compat>.admonition-body{color:rgba(0,0,0,0.7)}.admonition-header{color:#fff;background-color:#363636;align-items:center;font-weight:700;justify-content:space-between;line-height:1.25;padding:0.5rem .75rem;position:relative}.admonition-header:before{font-family:"Font Awesome 6 Free";font-weight:900;margin-right:.75rem;content:"\f06a"}details.admonition.is-details>.admonition-header{list-style:none}details.admonition.is-details>.admonition-header:before{font-family:"Font Awesome 6 Free";font-weight:900;content:"\f055"}details.admonition.is-details[open]>.admonition-header:before{font-family:"Font Awesome 6 Free";font-weight:900;content:"\f056"}.admonition-body{color:#222;padding:0.5rem .75rem}.admonition-body pre{background-color:#f5f5f5}.admonition-body code{background-color:rgba(0,0,0,0.05)}.docstring{margin-bottom:1em;background-color:rgba(0,0,0,0);border:1px solid #dbdbdb;box-shadow:2px 2px 3px rgba(10,10,10,0.1);max-width:100%}.docstring>header{cursor:pointer;display:flex;flex-grow:1;align-items:stretch;padding:0.5rem .75rem;background-color:#f5f5f5;box-shadow:0 0.125em 0.25em rgba(10,10,10,0.1);box-shadow:none;border-bottom:1px solid #dbdbdb;overflow:auto}.docstring>header code{background-color:transparent}.docstring>header .docstring-article-toggle-button{min-width:1.1rem;padding:0.2rem 0.2rem 0.2rem 0}.docstring>header .docstring-binding{margin-right:0.3em}.docstring>header .docstring-category{margin-left:0.3em}.docstring>section{position:relative;padding:.75rem .75rem;border-bottom:1px solid #dbdbdb}.docstring>section:last-child{border-bottom:none}.docstring>section>a.docs-sourcelink{transition:opacity 0.3s;opacity:0;position:absolute;right:.375rem;bottom:.375rem}.docstring>section>a.docs-sourcelink:focus{opacity:1 !important}.docstring:hover>section>a.docs-sourcelink{opacity:0.2}.docstring:focus-within>section>a.docs-sourcelink{opacity:0.2}.docstring>section:hover a.docs-sourcelink{opacity:1}.documenter-example-output{background-color:#fff}.outdated-warning-overlay{position:fixed;top:0;left:0;right:0;box-shadow:0 0 10px rgba(0,0,0,0.3);z-index:999;background-color:#ffaba7;color:rgba(0,0,0,0.7);border-bottom:3px solid #da0b00;padding:10px 35px;text-align:center;font-size:15px}.outdated-warning-overlay .outdated-warning-closer{position:absolute;top:calc(50% - 10px);right:18px;cursor:pointer;width:12px}.outdated-warning-overlay a{color:#2e63b8}.outdated-warning-overlay a:hover{color:#363636}.content pre{border:1px solid #dbdbdb}.content code{font-weight:inherit}.content a code{color:#2e63b8}.content h1 code,.content h2 code,.content h3 code,.content h4 code,.content h5 code,.content h6 code{color:#222}.content table{display:block;width:initial;max-width:100%;overflow-x:auto}.content blockquote>ul:first-child,.content blockquote>ol:first-child,.content .admonition-body>ul:first-child,.content .admonition-body>ol:first-child{margin-top:0}pre,code{font-variant-ligatures:no-contextual}.breadcrumb a.is-disabled{cursor:default;pointer-events:none}.breadcrumb a.is-disabled,.breadcrumb a.is-disabled:hover{color:#222}.hljs{background:initial !important}.katex .katex-mathml{top:0;right:0}.katex-display,mjx-container,.MathJax_Display{margin:0.5em 0 !important}html{-moz-osx-font-smoothing:auto;-webkit-font-smoothing:auto}li.no-marker{list-style:none}#documenter .docs-main>article{overflow-wrap:break-word}#documenter .docs-main>article .math-container{overflow-x:auto;overflow-y:hidden}@media screen and (min-width: 1056px){#documenter .docs-main{max-width:52rem;margin-left:20rem;padding-right:1rem}}@media screen and (max-width: 1055px){#documenter .docs-main{width:100%}#documenter .docs-main>article{max-width:52rem;margin-left:auto;margin-right:auto;margin-bottom:1rem;padding:0 1rem}#documenter .docs-main>header,#documenter .docs-main>nav{max-width:100%;width:100%;margin:0}}#documenter .docs-main header.docs-navbar{background-color:#fff;border-bottom:1px solid #dbdbdb;z-index:2;min-height:4rem;margin-bottom:1rem;display:flex}#documenter .docs-main header.docs-navbar .breadcrumb{flex-grow:1;overflow-x:hidden}#documenter .docs-main header.docs-navbar .docs-sidebar-button{display:block;font-size:1.5rem;padding-bottom:0.1rem;margin-right:1rem}#documenter .docs-main header.docs-navbar .docs-right{display:flex;white-space:nowrap;gap:1rem;align-items:center}#documenter .docs-main header.docs-navbar .docs-right .docs-icon,#documenter .docs-main header.docs-navbar .docs-right .docs-label{display:inline-block}#documenter .docs-main header.docs-navbar .docs-right .docs-label{padding:0;margin-left:0.3em}@media screen and (max-width: 1055px){#documenter .docs-main header.docs-navbar .docs-right .docs-navbar-link{margin-left:0.4rem;margin-right:0.4rem}}#documenter .docs-main header.docs-navbar>*{margin:auto 0}@media screen and (max-width: 1055px){#documenter .docs-main header.docs-navbar{position:sticky;top:0;padding:0 1rem;transition-property:top, box-shadow;-webkit-transition-property:top, box-shadow;transition-duration:0.3s;-webkit-transition-duration:0.3s}#documenter .docs-main header.docs-navbar.headroom--not-top{box-shadow:.2rem 0rem .4rem #bbb;transition-duration:0.7s;-webkit-transition-duration:0.7s}#documenter .docs-main header.docs-navbar.headroom--unpinned.headroom--not-top.headroom--not-bottom{top:-4.5rem;transition-duration:0.7s;-webkit-transition-duration:0.7s}}#documenter .docs-main section.footnotes{border-top:1px solid #dbdbdb}#documenter .docs-main section.footnotes li .tag:first-child,#documenter .docs-main section.footnotes li .docstring>section>a.docs-sourcelink:first-child,#documenter .docs-main section.footnotes li .content kbd:first-child,.content #documenter .docs-main section.footnotes li kbd:first-child{margin-right:1em;margin-bottom:0.4em}#documenter .docs-main .docs-footer{display:flex;flex-wrap:wrap;margin-left:0;margin-right:0;border-top:1px solid #dbdbdb;padding-top:1rem;padding-bottom:1rem}@media screen and (max-width: 1055px){#documenter .docs-main .docs-footer{padding-left:1rem;padding-right:1rem}}#documenter .docs-main .docs-footer .docs-footer-nextpage,#documenter .docs-main .docs-footer .docs-footer-prevpage{flex-grow:1}#documenter .docs-main .docs-footer .docs-footer-nextpage{text-align:right}#documenter .docs-main .docs-footer .flexbox-break{flex-basis:100%;height:0}#documenter .docs-main .docs-footer .footer-message{font-size:0.8em;margin:0.5em auto 0 auto;text-align:center}#documenter .docs-sidebar{display:flex;flex-direction:column;color:#0a0a0a;background-color:#f5f5f5;border-right:1px solid #dbdbdb;padding:0;flex:0 0 18rem;z-index:5;font-size:1rem;position:fixed;left:-18rem;width:18rem;height:100%;transition:left 0.3s}#documenter .docs-sidebar.visible{left:0;box-shadow:.4rem 0rem .8rem #bbb}@media screen and (min-width: 1056px){#documenter .docs-sidebar.visible{box-shadow:none}}@media screen and (min-width: 1056px){#documenter .docs-sidebar{left:0;top:0}}#documenter .docs-sidebar .docs-logo{margin-top:1rem;padding:0 1rem}#documenter .docs-sidebar .docs-logo>img{max-height:6rem;margin:auto}#documenter .docs-sidebar .docs-package-name{flex-shrink:0;font-size:1.5rem;font-weight:700;text-align:center;white-space:nowrap;overflow:hidden;padding:0.5rem 0}#documenter .docs-sidebar .docs-package-name .docs-autofit{max-width:16.2rem}#documenter .docs-sidebar .docs-package-name a,#documenter .docs-sidebar .docs-package-name a:hover{color:#0a0a0a}#documenter .docs-sidebar .docs-version-selector{border-top:1px solid #dbdbdb;display:none;padding:0.5rem}#documenter .docs-sidebar .docs-version-selector.visible{display:flex}#documenter .docs-sidebar ul.docs-menu{flex-grow:1;user-select:none;border-top:1px solid #dbdbdb;padding-bottom:1.5rem}#documenter .docs-sidebar ul.docs-menu>li>.tocitem{font-weight:bold}#documenter .docs-sidebar ul.docs-menu>li li{font-size:.95rem;margin-left:1em;border-left:1px solid #dbdbdb}#documenter .docs-sidebar ul.docs-menu input.collapse-toggle{display:none}#documenter .docs-sidebar ul.docs-menu ul.collapsed{display:none}#documenter .docs-sidebar ul.docs-menu input:checked~ul.collapsed{display:block}#documenter .docs-sidebar ul.docs-menu label.tocitem{display:flex}#documenter .docs-sidebar ul.docs-menu label.tocitem .docs-label{flex-grow:2}#documenter .docs-sidebar ul.docs-menu label.tocitem .docs-chevron{display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1;font-size:.75rem;margin-left:1rem;margin-top:auto;margin-bottom:auto}#documenter .docs-sidebar ul.docs-menu label.tocitem .docs-chevron::before{font-family:"Font Awesome 6 Free";font-weight:900;content:"\f054"}#documenter .docs-sidebar ul.docs-menu input:checked~label.tocitem .docs-chevron::before{content:"\f078"}#documenter .docs-sidebar ul.docs-menu .tocitem{display:block;padding:0.5rem 0.5rem}#documenter .docs-sidebar ul.docs-menu .tocitem,#documenter .docs-sidebar ul.docs-menu .tocitem:hover{color:#0a0a0a;background:#f5f5f5}#documenter .docs-sidebar ul.docs-menu a.tocitem:hover,#documenter .docs-sidebar ul.docs-menu label.tocitem:hover{color:#0a0a0a;background-color:#ebebeb}#documenter .docs-sidebar ul.docs-menu li.is-active{border-top:1px solid #dbdbdb;border-bottom:1px solid #dbdbdb;background-color:#fff}#documenter .docs-sidebar ul.docs-menu li.is-active .tocitem,#documenter .docs-sidebar ul.docs-menu li.is-active .tocitem:hover{background-color:#fff;color:#0a0a0a}#documenter .docs-sidebar ul.docs-menu li.is-active ul.internal .tocitem:hover{background-color:#ebebeb;color:#0a0a0a}#documenter .docs-sidebar ul.docs-menu>li.is-active:first-child{border-top:none}#documenter .docs-sidebar ul.docs-menu ul.internal{margin:0 0.5rem 0.5rem;border-top:1px solid #dbdbdb}#documenter .docs-sidebar ul.docs-menu ul.internal li{font-size:.85rem;border-left:none;margin-left:0;margin-top:0.5rem}#documenter .docs-sidebar ul.docs-menu ul.internal .tocitem{width:100%;padding:0}#documenter .docs-sidebar ul.docs-menu ul.internal .tocitem::before{content:"⚬";margin-right:0.4em}#documenter .docs-sidebar form.docs-search{margin:auto;margin-top:0.5rem;margin-bottom:0.5rem}#documenter .docs-sidebar form.docs-search>input{width:14.4rem}#documenter .docs-sidebar #documenter-search-query{color:#707070;width:14.4rem;box-shadow:inset 0 1px 2px rgba(10,10,10,0.1)}@media screen and (min-width: 1056px){#documenter .docs-sidebar ul.docs-menu{overflow-y:auto;-webkit-overflow-scroll:touch}#documenter .docs-sidebar ul.docs-menu::-webkit-scrollbar{width:.3rem;background:none}#documenter .docs-sidebar ul.docs-menu::-webkit-scrollbar-thumb{border-radius:5px 0px 0px 5px;background:#e0e0e0}#documenter .docs-sidebar ul.docs-menu::-webkit-scrollbar-thumb:hover{background:#ccc}}@media screen and (max-width: 1055px){#documenter .docs-sidebar{overflow-y:auto;-webkit-overflow-scroll:touch}#documenter .docs-sidebar::-webkit-scrollbar{width:.3rem;background:none}#documenter .docs-sidebar::-webkit-scrollbar-thumb{border-radius:5px 0px 0px 5px;background:#e0e0e0}#documenter .docs-sidebar::-webkit-scrollbar-thumb:hover{background:#ccc}}kbd.search-modal-key-hints{border-radius:0.25rem;border:1px solid rgba(0,0,0,0.6);box-shadow:0 2px 0 1px rgba(0,0,0,0.6);cursor:default;font-size:0.9rem;line-height:1.5;min-width:0.75rem;text-align:center;padding:0.1rem 0.3rem;position:relative;top:-1px}.search-min-width-50{min-width:50%}.search-min-height-100{min-height:100%}.search-modal-card-body{max-height:calc(100vh - 15rem)}.search-result-link{border-radius:0.7em;transition:all 300ms}.search-result-link:hover,.search-result-link:focus{background-color:rgba(0,128,128,0.1)}.search-result-link .property-search-result-badge,.search-result-link .search-filter{transition:all 300ms}.property-search-result-badge,.search-filter{padding:0.15em 0.5em;font-size:0.8em;font-style:italic;text-transform:none !important;line-height:1.5;color:#f5f5f5;background-color:rgba(51,65,85,0.501961);border-radius:0.6rem}.search-result-link:hover .property-search-result-badge,.search-result-link:hover .search-filter,.search-result-link:focus .property-search-result-badge,.search-result-link:focus .search-filter{color:#f1f5f9;background-color:#333}.search-filter{color:#333;background-color:#f5f5f5;transition:all 300ms}.search-filter:hover,.search-filter:focus{color:#333}.search-filter-selected{color:#f5f5f5;background-color:rgba(139,0,139,0.5)}.search-filter-selected:hover,.search-filter-selected:focus{color:#f5f5f5}.search-result-highlight{background-color:#ffdd57;color:black}.search-divider{border-bottom:1px solid #dbdbdb}.search-result-title{width:85%;color:#333}.search-result-code-title{font-size:0.875rem;font-family:"JuliaMono","SFMono-Regular","Menlo","Consolas","Liberation Mono","DejaVu Sans Mono",monospace}#search-modal .modal-card-body::-webkit-scrollbar,#search-modal .filter-tabs::-webkit-scrollbar{height:10px;width:10px;background-color:transparent}#search-modal .modal-card-body::-webkit-scrollbar-thumb,#search-modal .filter-tabs::-webkit-scrollbar-thumb{background-color:gray;border-radius:1rem}#search-modal .modal-card-body::-webkit-scrollbar-track,#search-modal .filter-tabs::-webkit-scrollbar-track{-webkit-box-shadow:inset 0 0 6px rgba(0,0,0,0.6);background-color:transparent}.w-100{width:100%}.gap-2{gap:0.5rem}.gap-4{gap:1rem}.gap-8{gap:2rem}.ansi span.sgr1{font-weight:bolder}.ansi span.sgr2{font-weight:lighter}.ansi span.sgr3{font-style:italic}.ansi span.sgr4{text-decoration:underline}.ansi span.sgr7{color:#fff;background-color:#222}.ansi span.sgr8{color:transparent}.ansi span.sgr8 span{color:transparent}.ansi span.sgr9{text-decoration:line-through}.ansi span.sgr30{color:#242424}.ansi span.sgr31{color:#a7201f}.ansi span.sgr32{color:#066f00}.ansi span.sgr33{color:#856b00}.ansi span.sgr34{color:#2149b0}.ansi span.sgr35{color:#7d4498}.ansi span.sgr36{color:#007989}.ansi span.sgr37{color:gray}.ansi span.sgr40{background-color:#242424}.ansi span.sgr41{background-color:#a7201f}.ansi span.sgr42{background-color:#066f00}.ansi span.sgr43{background-color:#856b00}.ansi span.sgr44{background-color:#2149b0}.ansi span.sgr45{background-color:#7d4498}.ansi span.sgr46{background-color:#007989}.ansi span.sgr47{background-color:gray}.ansi span.sgr90{color:#616161}.ansi span.sgr91{color:#cb3c33}.ansi span.sgr92{color:#0e8300}.ansi span.sgr93{color:#a98800}.ansi span.sgr94{color:#3c5dcd}.ansi span.sgr95{color:#9256af}.ansi span.sgr96{color:#008fa3}.ansi span.sgr97{color:#f5f5f5}.ansi span.sgr100{background-color:#616161}.ansi span.sgr101{background-color:#cb3c33}.ansi span.sgr102{background-color:#0e8300}.ansi span.sgr103{background-color:#a98800}.ansi span.sgr104{background-color:#3c5dcd}.ansi span.sgr105{background-color:#9256af}.ansi span.sgr106{background-color:#008fa3}.ansi span.sgr107{background-color:#f5f5f5}code.language-julia-repl>span.hljs-meta{color:#066f00;font-weight:bolder}/*! + Theme: Default + Description: Original highlight.js style + Author: (c) Ivan Sagalaev + Maintainer: @highlightjs/core-team + Website: https://highlightjs.org/ + License: see project LICENSE + Touched: 2021 +*/pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#F3F3F3;color:#444}.hljs-comment{color:#697070}.hljs-tag,.hljs-punctuation{color:#444a}.hljs-tag .hljs-name,.hljs-tag .hljs-attr{color:#444}.hljs-keyword,.hljs-attribute,.hljs-selector-tag,.hljs-meta .hljs-keyword,.hljs-doctag,.hljs-name{font-weight:bold}.hljs-type,.hljs-string,.hljs-number,.hljs-selector-id,.hljs-selector-class,.hljs-quote,.hljs-template-tag,.hljs-deletion{color:#880000}.hljs-title,.hljs-section{color:#880000;font-weight:bold}.hljs-regexp,.hljs-symbol,.hljs-variable,.hljs-template-variable,.hljs-link,.hljs-selector-attr,.hljs-operator,.hljs-selector-pseudo{color:#ab5656}.hljs-literal{color:#695}.hljs-built_in,.hljs-bullet,.hljs-code,.hljs-addition{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta .hljs-string{color:#38a}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold}.gap-4{gap:1rem} diff --git a/v1.19.1/assets/themeswap.js b/v1.19.1/assets/themeswap.js new file mode 100644 index 000000000..9f5eebe6a --- /dev/null +++ b/v1.19.1/assets/themeswap.js @@ -0,0 +1,84 @@ +// Small function to quickly swap out themes. Gets put into the tag.. +function set_theme_from_local_storage() { + // Initialize the theme to null, which means default + var theme = null; + // If the browser supports the localstorage and is not disabled then try to get the + // documenter theme + if (window.localStorage != null) { + // Get the user-picked theme from localStorage. May be `null`, which means the default + // theme. + theme = window.localStorage.getItem("documenter-theme"); + } + // Check if the users preference is for dark color scheme + var darkPreference = + window.matchMedia("(prefers-color-scheme: dark)").matches === true; + // Initialize a few variables for the loop: + // + // - active: will contain the index of the theme that should be active. Note that there + // is no guarantee that localStorage contains sane values. If `active` stays `null` + // we either could not find the theme or it is the default (primary) theme anyway. + // Either way, we then need to stick to the primary theme. + // + // - disabled: style sheets that should be disabled (i.e. all the theme style sheets + // that are not the currently active theme) + var active = null; + var disabled = []; + var primaryLightTheme = null; + var primaryDarkTheme = null; + for (var i = 0; i < document.styleSheets.length; i++) { + var ss = document.styleSheets[i]; + // The tag of each style sheet is expected to have a data-theme-name attribute + // which must contain the name of the theme. The names in localStorage much match this. + var themename = ss.ownerNode.getAttribute("data-theme-name"); + // attribute not set => non-theme stylesheet => ignore + if (themename === null) continue; + // To distinguish the default (primary) theme, it needs to have the data-theme-primary + // attribute set. + if (ss.ownerNode.getAttribute("data-theme-primary") !== null) { + primaryLightTheme = themename; + } + // Check if the theme is primary dark theme so that we could store its name in darkTheme + if (ss.ownerNode.getAttribute("data-theme-primary-dark") !== null) { + primaryDarkTheme = themename; + } + // If we find a matching theme (and it's not the default), we'll set active to non-null + if (themename === theme) active = i; + // Store the style sheets of inactive themes so that we could disable them + if (themename !== theme) disabled.push(ss); + } + var activeTheme = null; + if (active !== null) { + // If we did find an active theme, we'll (1) add the theme--$(theme) class to + document.getElementsByTagName("html")[0].className = "theme--" + theme; + activeTheme = theme; + } else { + // If we did _not_ find an active theme, then we need to fall back to the primary theme + // which can either be dark or light, depending on the user's OS preference. + var activeTheme = darkPreference ? primaryDarkTheme : primaryLightTheme; + // In case it somehow happens that the relevant primary theme was not found in the + // preceding loop, we abort without doing anything. + if (activeTheme === null) { + console.error("Unable to determine primary theme."); + return; + } + // When switching to the primary light theme, then we must not have a class name + // for the tag. That's only for non-primary or the primary dark theme. + if (darkPreference) { + document.getElementsByTagName("html")[0].className = + "theme--" + activeTheme; + } else { + document.getElementsByTagName("html")[0].className = ""; + } + } + for (var i = 0; i < document.styleSheets.length; i++) { + var ss = document.styleSheets[i]; + // The tag of each style sheet is expected to have a data-theme-name attribute + // which must contain the name of the theme. The names in localStorage much match this. + var themename = ss.ownerNode.getAttribute("data-theme-name"); + // attribute not set => non-theme stylesheet => ignore + if (themename === null) continue; + // we'll disable all the stylesheets, except for the active one + ss.disabled = !(themename == activeTheme); + } +} +set_theme_from_local_storage(); diff --git a/v1.19.1/assets/warner.js b/v1.19.1/assets/warner.js new file mode 100644 index 000000000..3f6f5d008 --- /dev/null +++ b/v1.19.1/assets/warner.js @@ -0,0 +1,52 @@ +function maybeAddWarning() { + // DOCUMENTER_NEWEST is defined in versions.js, DOCUMENTER_CURRENT_VERSION and DOCUMENTER_STABLE + // in siteinfo.js. + // If either of these are undefined something went horribly wrong, so we abort. + if ( + window.DOCUMENTER_NEWEST === undefined || + window.DOCUMENTER_CURRENT_VERSION === undefined || + window.DOCUMENTER_STABLE === undefined + ) { + return; + } + + // Current version is not a version number, so we can't tell if it's the newest version. Abort. + if (!/v(\d+\.)*\d+/.test(window.DOCUMENTER_CURRENT_VERSION)) { + return; + } + + // Current version is newest version, so no need to add a warning. + if (window.DOCUMENTER_NEWEST === window.DOCUMENTER_CURRENT_VERSION) { + return; + } + + // Add a noindex meta tag (unless one exists) so that search engines don't index this version of the docs. + if (document.body.querySelector('meta[name="robots"]') === null) { + const meta = document.createElement("meta"); + meta.name = "robots"; + meta.content = "noindex"; + + document.getElementsByTagName("head")[0].appendChild(meta); + } + + const div = document.createElement("div"); + div.classList.add("outdated-warning-overlay"); + const closer = document.createElement("button"); + closer.classList.add("outdated-warning-closer", "delete"); + closer.addEventListener("click", function () { + document.body.removeChild(div); + }); + const href = window.documenterBaseURL + "/../" + window.DOCUMENTER_STABLE; + div.innerHTML = + 'This documentation is not for the latest stable release, but for either the development version or an older release.
    Click here to go to the documentation for the latest stable release.'; + div.appendChild(closer); + document.body.appendChild(div); +} + +if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", maybeAddWarning); +} else { + maybeAddWarning(); +} diff --git a/v1.19.1/changes/index.html b/v1.19.1/changes/index.html new file mode 100644 index 000000000..08fb20de8 --- /dev/null +++ b/v1.19.1/changes/index.html @@ -0,0 +1,11 @@ + +Changes · VoronoiFVM.jl

    Changes

    v1.19.0 Feb 01 2024

    • Enable equation block preconditioning for sparse unknown storage

    v1.18.0 Feb 01 2024

    • Re-shoring of OrdinaryDiffEq interface, no need of VoronoiFVMDiffEq.jl anymore It appeared that it is sufficient to depend on SciMLBase for this, and all tests can be done with OrdinaryDiffEq.jl

    v1.17.1 Jan 30, 2024

    • Bugfix for boundary node factors
    • Bugfix with types for RecursiveArrayTools

    v1.16.0 Dec 15, 2023

    • Bugfix for assembly of outflow bc
    • Bugfix for matrixtype=:banded
    • Updated plothistory:

    v1.15.0 Dec 1, 2023

    • Adjusted time/embedding stepping scheme, added num_final_steps to VoronoiFVM.SolverControl. This may lead to sligthly different results when solving time dependent problems. Some unit test values have been adapted. Before, accidentally, very small time steps at the end of an evolution were possible.

    v1.14.0 Nov 27, 2023

    v1.13.0 July 24, 2023

    v1.12.0 July 22, 2023

    • Add functionality for outflow boundary conditions

    v1.11.0 July 17, 2023

    • Add calc_divergences method for checking velocity field divergences
    • Fix form factor calculation and velocity projecion for unstructructured grids and cylindrical symmetry

    v1.10.0 July 11, 2023

    • Use AbstractTransientSolution in gridvisualize stuff

    v1.9.0 June 27, 2023

    • With control.handle_exceptions=true, in case of a failing step, time stepping and embeding now returns the solution calculated so far instead of throwing an error

    v1.8.0 June 20, 2023

    • LinearSolve 2.x

    v1.7.0 May 17, 2023

    • Discrete Sobolev norms

    v1.6.0 May 12, 2023

    • Rework linear solver strategies - prevent combinatorial explosion. E.g. gmres_iluzero is now GMRESIteration(ILUZeroPreconditioner()) etc.

    v1.5.0 May 9, 2023

    • Introduced solver strategies like gmres_iluzero(), direct_umfpack() etc. See documentation of the module VoronoiFVM.SolverStrategies. More to come.
    • edgewise assembly - faster in particular for 3D
    • Plan: edgewise assembly seems to be non-breaking, if this is confirmed, will be made default in 1.6 or (if it appears to be breaking for some) in 2.0.

    v1.4.0 May 3, 2023

    • equation-block preconditioning support with the help of ExtendableSparse.jl

    v1.3.0 April 13, 2023

    • inplace_linsolve! for dense linear system solution in flux functions
    • Mixture flow example 510

    v1.2.0 March 17, 2023

    • Initialization of quantities, create unknowns using Base.map

    v1.1.0 Feb 22, 2023

    • Edge reactions, Joule heating

    v1.0.0 Feb 22, 2023

    • full LinearSolve compatibility

    v0.19.0 Jan 31, 2023

    This is a breaking release. Implementations using default solver settings should continue to work without modifications, albeit possibly showing deprecation and allocation warnings. Really breaking is control of iterative linear solvers and allocation checks.

    Breaking

    • Make solve a method of CommonSolve.solve (and re-export it).
    • Rely on LinearSolve.jl for linear system solution including control of iterative solvers.
    • New verbosity handling. verbose can now be a Bool or a String of flag characters, allowing for control of different output categories. I would love to do this via logging, but there is still a long way to go IMHO
    • Allocation check is active by default with warnings instead of throwing an error. These warnings can be muted by passing a verbose string without 'a'. This is now the only control in this respect. All check_allocs methods/kwargs, control via environment variables have been removed.
    • Deprecation warnings can be switched off by passing a verbose string without 'd'.
    • Improve iteration logging etc., allow for logging of linear iterations ('l' flag character)

    Deprecations

    • Deprecated all VoronoiFVM.solve methods with signatures others than solve(system; kwargs...) which renders them incompatible to the philosophy of `CommonSolve. Updated examples accordingly.
    • Deprecated the following entries of SolverControl/solve kwargs: :tol_absolute => :abstol, :tol_relative => :reltol, :damp => :damp_initial, :damp_grow => :damp_growth :max_iterations => e:maxiters :tol_linear => :reltol_linear :max_lureuse =>
    • NewtonControl

    v0.18.8 - 0.18.10 Dec 11, 2022 - Jan 5, 2023

    • Internal restructuring: remove @create_physics_wrappers macro, reduce boilerplate in assembly, wrap repeating pattenrns into functions. The price in the moment is a bit of a slowdown of assembly.
    • Fix parameter dependency handling (now we can get parameter derivative without solving in dual numbers; see the runh() example in Example430. However in the moment the advantatge is not very clear, so this is on hold...

    v0.18.7 Dec 7, 2022

    • bump gridvisualize compat

    v0.18.6 Dec 3, 2022

    • enable non-diagonal mass matrices for VoronoiFVMDiffEq

    v0.18.5 Nov 30, 2022

    • ready for Julia 1.9, re-enable CI on nighly

    v0.18.4 Nov 29, 2022

    • Add API methods used by VoronoiFVMDiffEq.jl

    v0.18.3 Oct 18 2022

    • Removed some allocations

    v0.18.2 Oct 13 2022

    • Emerging capability of differencing wrt. parameters (experimental, see example 430)
    • Allow iterative methods from Krylov.jl
    • Proper Dirichlet initialization with bcondition
    • Allow for more general matrix structures (banded, tridiagonal, multidiagonal)

    v0.18.1 September 25 2022

    • Working spherical symmetry case

    v0.18 September 12 2022

    • Remove DifferentialEquations interface (move this to a glue package)

    The current method of activating it through require is too brittle with respect to versioning.

    v0.17.1 August 20 2022

    • Fix DifferentialEquations interface, start transition to LinearSolve

    v0.17.0 July 1 2022

    • ensure not to assemble data for species where they are not enabled This change should be breaking only for incorrect code where physics callbacks write into degrees of freedom which are not enabled

    v0.16.5 June 30, 2022

    • add iteration to solver options, allow to choose :cg, :bicgstab.
    • allow setting penalty with boundary_dirichlet!

    v0.16.4 May 25, 2022

    • fix x-t plots

    v0.16.3 March 18, 2022

    • Linearization API
    • relax some type constraints

    v0.16.2 Feb 18, 2022

    • ExtendableGrids 0.9

    v0.16.1 Feb 17, 2022

    • fix quantity postprocessing
    • define unknown access for abstract vectors instead of vectors
    • pass rhs/unknowns wrappers in postprocessing methods
    • integrals as a wrapper type with proper quantity handling

    v0.16.0 Feb 13, 2022

    • Expose ODEProblem (and possibly ODEFunc) from VoronoiFVM.System.
    • Breaking: Remove solve wrapper for DifferentialEquations.solve, instead recommend to call that directly
    • Breaking: Handle DifferentialEquations.jl via Requires.jl.

    v0.15.1 Jan 15, 2022

    • Documentation fixes
    • Fix OrdinaryDiffEq interface
    • added example for current calculation with Quantities
    • Fixed type instabilities in quantities interface

    v0.15.0 Jan 1, 2022

    • Breaking: History is not anymore returned by solve, instead it can be accessed via history after the solution.
    • Cleaned API:
    • OrdinaryDiffEq solver now in CI
    • scalarplot for 1D transient solutions
    • Sparsity detection via Symbolics.jl instead of the sunsetted SparsityDetection.jl

    v0.14.0 Dec 24, 2021

    Backward compatible, hopefully nonbreaking API simplification

    • Boundary conditions are now specified in breaction. Advantages:
      • easy x/t dependency
      • unified (upcoming) interface for parameters
      • unified handling of standard and nonstandard boundary conditions
      • simpler documentation
    • Made NewtonControl alias of SolverControl, continue to work with SolverControl
    • System constructor now directly takes physics callback functions, no need anymore to work with extra physics struct
    • solve() now takes "SolverControl" parameters as kwargs,no need anymore to work with extra NewtonControl/SolverControl struct
    • Notebooks as part of documentation and CI
    • See also the pluto notebook api-update.jl

    v0.13.2 Oct 29, 2021

    • Bernoulli function overhaul

    v0.13.1

    • sorted things with ExtendableGrids
    • nodal flux reconstruction (e.g. for visualization)

    v0.13.0, Oct 13, 2021

    • various bug fixes, explicit numbering of edge nodes

    v0.12.3, July 7, 2021

    • Add quantity id
    • Document quantities

    v0.12.2, July 7, 2021

    Introduce the notion of quantities which can be continuous or discontinuous at interfaces.

    • Quantity handling is implemented on top of species handling
    • Unknowns u and rhs y now passed to callbacks as wrapper types, and can be indexed by quantity or by species numbers. Moreover, this will allow to abstract parameters, gradients etc. in future versions.

    v0.12.0, July 2 2021

    • By default, the u parameter in flux callbacks is now a nspec x 2 array
    • unknowns(edge,u), viewK, viewL are obsolete, they still work for backward compatibility
    • physics.num_species is now meaningless, num_species is automatically detected.
    • SparseSystem and DenseSystem are now type aliases of a parametrized type instead of two independent subtypes of System

    v0.11.8

    • increase chunk size threshold to match argument length in calls to vectormodejacobian

    v0.11.7

    • First attempts on surface flux

    v0.11.1, April 13, 2021

    • Assembly loops cleaned from type instabilities
    • Optionally check for allocations due to type instabilities introduced in physics callbacks. See check_allocs! for more information.

    v0.11, April 12, 2021

    • Depending on Julia 1.5 now
    • Lineaer solvers now based on factorize! from ExtendableSparse 0.5
    • Documentation overhaul
    • Re-checking impedance calculation

    v0.10.13 April 1, 2021

    • Outflow boundary conditions

    v0.10.8 March 22, 2021

    • TransientSolution structure, transient solve
    • Solve compatible with DifferentialEquations.jl

    v0.10.3 Feb 11, 2021

    • Introduce non-mutating solve
    • Optionally record history if log kw is true in solve.

    v0.10.0 Jan 9 2021

    • Moving visualization to the package GridVisualize.jl, emerging from the visualization methods in ExtendableGrids

    v0.9.0 Dec 21 2020

    • Add the possibility to interface with DifferentialEquations.jl
    • Breaking: The API change to passing the unknowns to the an edge callback as a matrix turned out to be a dead end in the strategic sense. In order to extend functionality, we need to be able to pass more data to which we can apply differetiation. Particular plans involve bifurcation parameters and reconstructed gradients. So we return to the viewK/viewL pattern we had before. However, these are now aliases:
      • viewK(edge,u)=unknowns(edge,u,1)
      • viewL(edge,u)=unknowns(edge,u,2)
      In order to ease refactoring in the case where models have been implemented with Matrix access to the unknowns, unknowns(edge,u) returns a matrix of the edge unknowns. For refactoring, just rewrite e.g.
        function flux(y,u,edge)
    +        for ispec=1:nspec
    +            y[ispec]=u[ispec,1]-u[ispec,2]
    +        end
    +    end

    to

        function flux(y,u0,edge)
    +        u=unknowns(edge,u0)
    +        for ispec=1:nspec
    +            y[ispec]=u[ispec,1]-u[ispec,2]
    +        end
    +    end

    v0.8.5 Sep 1 2020

    • allow any object in Physics.data (thanks Jan Weidner)
    • add generic operator for non-canonical problem structures

    v0.8.4 July 25 2020

    • Update ExtendableGrids + ExtendableSparse

    v0.8.3 June 25 2020

    • Replace splatting by dispatch on availability of data record

    v0.8.2 May 15 2020

    • Form factors are now pre-calculated and stored
    • Introduced update_grid! for triggering re-calculation if coordinates have changed

    v0.8.1 May 2 2020

    • Introduce evolve! : time solver with automatic timestep control

    v0.8 Apr 28, 2020

    • Replaced VoronoiFVM grid module by ExtendableGrids.jl
    • Moved grid generation, modification, plotting over to ExtendableGrids
    • Necessary changes in codes using VoronoiFVM:
      • Replace grid.coord by coord obtained via coord=coordinates(grid) or coord=grid[Coordinates] after importing ExtendableGrids
      • Replace VoronoiFVM.plot by ExtendableGrids.plot.
      • In the plot method, Plotter is now a keyword argument
      • VoronoiFVM.Grid() now returns a ExtendableGrids.ExtendableGrid, in fact it is just an alias to ExtendableGrids.simplexgrid
      • For using any methods on grids like cellmask! one needs to use ExtendableGrids
      • Subgrids now are of the same type ExtendableGrids, views are currently defined for vectors only.

    v0.7 Feb 28 2020

    • API modification:

      • Breaking:

        • data parameter passed to physics callbacks only if Physics object is created with data parameter.

          This makes the API more consistent in the case that parameters are just taken from the closure (the scope where the physics functions are defined) and no data object has been created.

        • Replace node.coord[i] by node[i].

        • Replace edge.coordK[i] by edge[i,1].

        • Replace edge.coordL[i] by edge[i,2].

          This now directly accesses the coordinate field of the grid and avoids copying of the coordinates

      • Backward compatible:

        • No need for viewK and viewL in edge callbacks (they also make trouble with allocations...)
          • Replace uk[i] by u[i,1]
          • Replace ul[i] by u[i,2]
        • Replace VoronoiFVM.DenseSystem(...) by VoronoiFVM.System(..., unknown_storage=:dense)
        • Replace VoronoiFVM.SparseSystem(...) by VoronoiFVM.System(..., unknown_storage=:sparse)
    • No allocations anymore in assembly loop:

      • Replaced ElasticArray in Grid by normal one - this was the largest regression
      • Return nothing from mutating methods to avoid some allocations
      • Indexing in formfactors.jl with Int

    v0.6.5 Jan 25 2020

    • use updateindex! for matrix, depend on ExtendableSparse 0.2.6

    v0.6.4 2020-01-20

    • Rearranged + commented boundary assembly loop
    • Reworked + renamed some examples
    • Document that unknowns doesn't initialize values.

    v0.6.3 2019-12-21

    remove xcolptrs call Update dependency on ExtendableSparse

    v0.6.2 2019-12-20

    Updated dependency list (Triangulate ^0.4.0)

    v0.6.1, 2019-12-17

    • return "plotted" for being able to place colormap
    • require Triangulate >= 0.3.0

    v0.6.0, Dec 15 2019

    • Removed Triangle submodule, depend on new Triangulate.jl Triangle wrapper
    • link to source code in examples
    • boundary_dirichlet! etc methods for setting boundary conditions

    v0.5.6 Dec 5 2019

    • Bug fixes
    • check triangle input for min 3 points
    • check triangle edgelist for C_NULL
    • voronoi plot

    v0.5.5 Dec 4 2019

    • (Temporary) Copy of TriangleRaw as Triangle submodule. To be replaced by dependency on evisioned package

    v0.5.4 Dec 3 2019

    • Re-enabled ElasticArrays in grid structure (for the time being)
    • Added potkink example: this adds an inner boundary

    v0.5.3 Dec 1 2019

    • triangle in optional submodule
    • Modified API for plotting
      • Removed formal dependency on Plots and PyPlot
      • Use Plotter module as first parameter to plot methods - replaces fvmplot and fvmpyplot functions. Use VoronoiFVM.plot(PyPlot,...) resp. VoronoiFVM.plot(Plots,...)
      • No more complaints when package is used in environment with plots or pyplot installed
    • Modified API for impedance

    v0.5.2 Nov 19, 2019

    • Reorganized grid stuff
    • Included triangle (after Ideas from TriangleMesh.jl)

    v0.5.1 Nov 13, 2019

    • Fixed performance regression: AbstractArrays for Grid components were slow.
    • Added handling of cylindrical coordinates

    V0.5, November 10, 2019

    • Velocity projections
    • Added edge handling to grid struct

    V0.4.2, November 6, 2019

    • Replaced PyPlot by Plots
    • Better and more examples

    V0.4, July 12, 2019

    • Registered with Julia ecosystem
    • Enhance Newton solver by embedding, exception handling
    • Replace SparseMatrixCSC with ExtendableSparseMatrix
    • fixed allocation issues in assembly
    • assured that users get allocation stuff right via typed functions in physics structure
    • more julianic API

    V0.3, April 9 2019

    • Renamed from TwoPointFluxFVM to VoronoiFVM
    • Complete rewrite of assembly allowing sparse or dense matrix to store degree of freedom information
      • Solution is a nnodes x nspecies sparse or dense matrix
      • The wonderful array interface of Julia still provides slicing etc in order to access species without need to write any bulk_solution stuff or whatever when using the sparse variant
    • Re-export value() for debugging in physics functions
    • Test function handling for flux calculation
    • First working steps to impedance handling
    • Abolished Graph in favor of Grid, Graph was premature optimization...

    V0.2, Feb 20, 2019

    • Changed signature of all callback functions: This also allows to pass user defined arrays etc. to the callback functions. In particular, velocity vectors can be passed this way.

      • Besides of flux!(), they now all have node::VoronoiFVM.Node as a second argument.

      • flux!() has edge::VoronoiFVM.Edge as a second argument

      • the x argument in source!() is omitted, the same data are now found in node.coord

    • New method edgelength(edge::VoronoiFVM.Edge)

    V0.1, Dec. 2018

    • Initial release
    diff --git a/v1.19.1/citations.bib b/v1.19.1/citations.bib new file mode 100644 index 000000000..56cf588b0 --- /dev/null +++ b/v1.19.1/citations.bib @@ -0,0 +1,187 @@ +@inproceedings{matera2023reduced, + title={Reduced Basis Approach for Convection-Diffusion Equations with Non-linear Boundary Reaction Conditions}, + author={Matera, S and Merdon, C and Runge, D}, + booktitle={International Conference on Finite Volumes for Complex Applications}, + pages={335--343}, + year={2023}, + doi = {10.1007/978-3-031-40864-9_28}, + organization={Springer} +} + +@article{spetzler2023role, + title={The Role of Vacancy Dynamics in Two-Dimensional Memristive Devices}, + author={Spetzler, Benjamin and Abdel, Dilara and Schwierz, Frank and Ziegler, Martin and Farrell, Patricio}, + journal={Advanced Electronic Materials}, + pages={2300635}, + year={2023}, + doi = {10.1002/aelm.202300635}, + publisher={Wiley Online Library} +} + +@article{scholz2023hestia, + title={Hestia.jl: A Julia Library for Heat Conduction Modeling with Boundary Actuation.}, + author={Scholz, Stephan and Berger, Lothar}, + journal={Simul. Notes Eur.}, + volume={33}, + number={1}, + url = {https://www.sne-journal.org/fileadmin/user_upload_sne/SNE_Issues_OA/SNE_33_1/articles/sne.33.1.10634.sn.OA.pdf}, + pages={27--30}, + year={2023} +} + +@inproceedings{scharer2023transient, + title={A Transient Non-isothermal Cell Performance Model for Organic Redox Flow Batteries}, + author={Sch{\"a}rer, Roman Pascal and Schumacher, J{\"u}rgen}, + booktitle={19th Symposium on Modeling and Experimental Validation of Electrochemical Energy Technologies (ModVal), Duisburg, Germany, 21-23 March 2023}, + url = {https://flowbatteryforum.com/wp-content/uploads/2023/06/2355-Roman-Schaerer-Poster.pdf}, + year={2023} +} + +@inproceedings{fuhrmann2023two, + title={Two Entropic Finite Volume Schemes for a Nernst--Planck--Poisson System with Ion Volume Constraints}, + author={Fuhrmann, J{\"u}rgen and Gaudeul, Beno{\^\i}t and Keller, Christine}, + booktitle={International Conference on Finite Volumes for Complex Applications}, + pages={285--294}, + year={2023}, + organization={Springer}, + doi= {10.1007/978-3-031-40864-9_23} +} + +@article{VagnerPavelkaFuhrmannKlika2022, +title = {A multiscale thermodynamic generalization of {M}axwell-{S}tefan diffusion equations and of the dusty gas model}, +journal = {International Journal of Heat and Mass Transfer}, +volume = {199}, +pages = {123405}, +year = {2022}, +doi = {10.1016/j.ijheatmasstransfer.2022.123405}, +author = {P. V{\'a}gner and M. Pavelka and J. Fuhrmann and V. Klika}, +} + +@Article{MilosEtAlxJES2022, + author = { +Milo{\v{s}}, V. and +V{\'a}gner, P. and +Bud{\'a}{\v{c}}, D. and +Carda, M. and +Paidar, M. and +Fuhrmann, J. +and +Bouzek, K. +}, + title = {Generalized {P}oisson-{N}ernst-{P}lanck-based physical model of an {O2} | {LSM} | {YSZ} electrode}, + year = 2022, + journal = "Journal of the Electrochemical Society", + doi="10.1149/1945-7111/ac4a51", + number=169, + pages=044505 +} + + +@article{xiao2022julia, + title={Julia language in computational mechanics: A new competitor}, + author={Xiao, Lei and Mei, Gang and Xi, Ning and Piccialli, Francesco}, + journal={Archives of Computational Methods in Engineering}, + volume={29}, + number={3}, + pages={1713--1726}, + year={2022}, + doi = {10.1007/s11831-021-09636-0}, + publisher={Springer} +} + +@article{gaudeul2022entropy, + title={Entropy and convergence analysis for two finite volume schemes for a Nernst--Planck--Poisson system with ion volume constraints}, + author={Gaudeul, Beno{\^\i}t and Fuhrmann, J{\"u}rgen}, + journal={Numerische Mathematik}, + volume={151}, + number={1}, + pages={99--149}, + year={2022}, + doi = {10.1007/s00211-022-01279-y}, + publisher={Springer} +} + +@inproceedings{martins2022semiconductor, + title={From Semiconductor to Transistor-Level: Modeling, Simulation, and Layout Rendering Tools}, + author={Martins, Jo{\~a}o R Raposo de O and Alves, Francisco and Ferreira, Pietro Maris}, + booktitle={Colloque du GdR SOC2}, + url = {https://hal.science/hal-03690082}, + note = {hal-03690082}, + year={2022} +} + +@mastersthesis{jambrich2022consistent, + title={Consistent non-equilibrium thermodynamic modeling of hydrogen fuel cells}, + author={Jambrich, Jakub}, + year={2022}, + school={Univerzita Karlova, Matematicko-fyzik{\'a}ln{\'\i} fakulta}, + Url = {https://dspace.cuni.cz/bitstream/handle/20.500.11956/174167/120418015.pdf} +} + +@mastersthesis{chinnery2022tcad, + title={TCAD-Informed Surrogate Models of Semiconductor Devices}, + author={Chinnery, Samuel B}, + year={2022}, + url = {https://dspace.mit.edu/handle/1721.1/144946}, + school={Massachusetts Institute of Technology} +} + +@article{abdel2021modelling, + title={Modelling charge transport in perovskite solar cells: Potential-based and limiting ion depletion}, + author={Abdel, Dilara and V{\'a}gner, Petr and Fuhrmann, J{\"u}rgen and Farrell, Patricio}, + journal={Electrochimica Acta}, + volume={390}, + pages={138696}, + year={2021}, + doi = {10.1016/j.electacta.2021.138696}, + publisher={Elsevier} +} + + +@article{abdel2021assessing, + title={Assessing the quality of the excess chemical potential flux scheme for degenerate semiconductor device simulation}, + author={Abdel, Dilara and Farrell, Patricio and Fuhrmann, J{\"u}rgen}, + journal={Optical and Quantum Electronics}, + volume={53}, + pages={1--10}, + year={2021}, + doi = "10.1007/s11082-021-02803-4", + publisher={Springer} +} + + +@article{cances2021numerical, + title={A numerical-analysis-focused comparison of several finite volume schemes for a unipolar degenerate drift-diffusion model}, + author={Canc{\`e}s, Cl{\'e}ment and Chainais-Hillairet, Claire and Fuhrmann, J{\"u}rgen and Gaudeul, Beno{\^\i}t}, + journal={IMA Journal of Numerical Analysis}, + volume={41}, + number={1}, + pages={271--314}, + year={2021}, + doi = {10.1093/imanum/draa002}, + publisher={Oxford University Press} +} + +@article{park2021mathematical, + title={Mathematical modeling and analysis of microwave-assisted freeze-drying in biopharmaceutical applications}, + author={Park, Jinwoo and Cho, Jae Hyun and Braatz, Richard D}, + journal={Computers \& Chemical Engineering}, + volume={153}, + pages={107412}, + year={2021}, + doi = {10.1016/j.compchemeng.2021.107412}, + publisher={Elsevier} +} + +@inproceedings{cances2020four, + title={On four numerical schemes for a unipolar degenerate drift-diffusion model}, + author={Canc{\`e}s, Cl{\'e}ment and Hillairet, Claire Chainais and Fuhrmann, J{\"u}rgen and Gaudeul, Beno{\^\i}t}, + booktitle={Finite Volumes for Complex Applications IX-Methods, Theoretical Aspects, Examples: FVCA 9, Bergen, Norway, June 2020 IX}, + pages={163--171}, + year={2020}, + doi="10.1007/978-3-030-43651-3_13", + organization={Springer} +} + + + diff --git a/v1.19.1/devel/index.html b/v1.19.1/devel/index.html new file mode 100644 index 000000000..59f0233dd --- /dev/null +++ b/v1.19.1/devel/index.html @@ -0,0 +1,2 @@ + +Development hints · VoronoiFVM.jl

    Development hints

    Here, a bit of development hints are given which mainly concern tests and documentation generation.

    Pluto notebooks

    The pluto notebooks in this package are "triple use":

    • As typical Pluto notebooks, they are self-contained in the senses that they contain their own Project and Manifest files. So users can just download and execute them.
    • If they run with the environmnet variable PLUTO_PROJECT set to some julia environment, this environment will activated at the start of the notebook. In particular, they use Revise.jl so they can be run during development

    of VoronoiFVM.jl. See also https://github.com/fonsp/Pluto.jl/issues/1788 .

    • During CI tests, they are run as scripts. For this purpose they are wrapped into temporariy modules, and @test macros can used in the notebooks.
    diff --git a/v1.19.1/grid/index.html b/v1.19.1/grid/index.html new file mode 100644 index 000000000..8216e1f24 --- /dev/null +++ b/v1.19.1/grid/index.html @@ -0,0 +1,2 @@ + +Grid · VoronoiFVM.jl
    diff --git a/v1.19.1/impedance-derivation.tex b/v1.19.1/impedance-derivation.tex new file mode 100644 index 000000000..38bee477f --- /dev/null +++ b/v1.19.1/impedance-derivation.tex @@ -0,0 +1,378 @@ +\documentclass[12pt]{amsproc} +\usepackage[a4paper]{geometry} +\usepackage{float} +\usepackage{fouriernc} +\usepackage{verbatim} + +\begin{comment} + Where to publish this ? JOSS is merely a software journal. + - CPC ? + - WIAS TR + - As part of documenter ? + After all, yes + + Computing in Science \& Engineering (CiSE) + +cite: https://www.researchgate.net/publication/223773028_A_frequency-domain_approach_to_dynamical_modeling_of_electrochemical_power_sources +https://www.sciencedirect.com/science/article/abs/pii/S0013468697002387 +https://www.gamry.com/application-notes/EIS/basics-of-electrochemical-impedance-spectroscopy/ +https://iopscience.iop.org/article/10.1149/09401.0251ecst + + +\end{comment} + + + +\title{Small signal analysis for nonlinear evolution equations\\ Draft.} +\author{J. Fuhrmann} +\newcommand{\CV}{{\mathcal{V}}} +\newcommand{\VV}{\mathbb{V}} +\newcommand{\PP}{\mathbb{P}} +\newcommand{\TT}{\mathbb{T}} +\newcommand{\MM}{\mathbb{M}} +\newcommand{\RR}{\mathbb{R}} +\newtheorem{example}{Example} + +\newcommand{\Exp}[1]{e^{#1}} +\begin{document} +\maketitle +%\begin{multicols}{2} +\section{Frequency response of an electrical network} + +The current dependency on voltage of basic basic elements of +electrical networks can be subsummed using the notion of impedance. +Let $U(t)$ be a given voltage difference applied to as device, and +$I(t)$ be the resulting current. For a periodic voltage perturbation, +the frequency dependent impedance describes the ratio between the +amplitudes of the voltagee and the current response, respectitvely. +Assuming a an applied periodic voltage $U(t)=U_a\Exp{i\omega t}$ with +amplitude $U_a$ and frequency $\omega$, we get +\begin{table}[H] + \begin{center} +\renewcommand{\arraystretch}{1.4} +\begin{tabular}{lllll} +Circuit Element & Law & Standard form & Complex Form & Impedance\\ +Resistance & Ohm & $ I(t)=\frac1R U(t)$ & $I(t)=\frac1R U(t)$ & $Z(\omega)=R$ \\ +Capacity & Faraday & $ I(t)=C(t)\frac{d}{dt}U(t)$ & $I(t)=Ci\omega U(t)$& $Z(\omega)=\frac1{Ci\omega}$\\ +Inductivity & Henry & $ I(t)=I_0+\frac1L\int\limits_{t_0}^tU(\tau) d\tau$ & $I(t)=\frac1{Li\omega}$ U(t)&$Z(\omega)=Li\omega$ \\ +\end{tabular} +\caption{\label{tab:impedance} Impedance of three basic elements of electric circuits}\hfill +\end{center} +\end{table} + +Any network consisting of these elements can be analyzed in the +complex domain using Kirchoffs law and regarding the impedance as a +complex resistance. + +\section{Impedance spectroscopy in nonlinear evolution equations} + +Usually, the interpretations of impedance spectroscopy measurements of +other systems, e.g. electrochemical systems is performed using a +replacement circuit consisting of electrical network elements. Being +quite successful in many cases, this interpretation basically is +limited to compartment type models. + +Here, we discuss an approach of applying impedance spectroscopy to +abstract evolution equations based on small signal analysis. + +For a given time interval $\TT=[0,T]$, a Banach space $\CV$ called +{\em state space}, and a finite dimensional space $\PP$, called {\em +parameter space}, regard the abstract doubly nonlinear evolution +equation +\begin{equation}\label{eq:abstrevol} + \frac{d S(v(t),\lambda)}{dt} + D(v(t),\lambda)=0 +\end{equation} + +The state of the system is {\em measured} by some functional $M: \CV +\rightarrow \MM$, where $\MM$ is the finite dimensional {\em +measurement space}. We assume $M(v)=M^{stdy}(v)+ d_t M^{tran}(v)$. + +As an example, $\CV$ may be a finite dimensional space containing the +solution of some discretized system of partial differential equations. + + +Given a steady state $(v_0, \lambda_0)$ such that $D(v_0, +\lambda_0)=0$, measured by $M_0=M(v_0)$, we would like to trace its +response to a small, periodic perturbation $\lambda(t)= \lambda_a +\Exp{i\omega t} $. Expressing the measurement of this response as $M(t)= M_0+ +M_a(\omega) \Exp{i\omega t}$, we yield the impedance $Z(\omega)= +M_a(\omega)^{-1} \lambda_a$. + + + +In order to calculate the frequency response, we make the ansatz $v(t)=v_0+v_a\Exp{i\omega t}$ for +the perturbation of the state variable and calculate the first order Taylor expansions +of the terms $S,D$: +\begin{equation*} + \begin{split} + S(v_0+v_a\Exp{i\omega t},\lambda_0+\lambda_a\Exp{i\omega t})\approx&S(v_0,\lambda_0)+ + S_v(v_0,\lambda_0)v_a\Exp{i\omega t}+ + S_\lambda(v_0,\lambda_0)\lambda_a\Exp{i\omega t}\\ + D(v_0+v_a\Exp{i\omega t},\lambda_0+\lambda_a\Exp{i\omega t})\approx&D(v_0,\lambda_0)+ + D_v(v_0,\lambda_0)v_a\Exp{i\omega t}+ + D_\lambda(v_0,\lambda_0)\lambda_a\Exp{i\omega t} + \end{split} +\end{equation*} + +Putting them into equation \eqref{eq:abstrevol} and using the steady state condition yields +\begin{equation*} + \begin{split} + \frac{d}{dt}\left( + S_v(v_0,\lambda_0)v_a\Exp{i\omega t}+ + S_\lambda(v_0,\lambda_0)\lambda_a\Exp{i\omega t}\right)+ + D_v(v_0,\lambda_0)v_a\Exp{i\omega t}+ + D_\lambda(v_0,\lambda_0)\lambda_a\Exp{i\omega t}&=0\\ + i\omega\left( + S_v(v_0,\lambda_0)v_a\Exp{i\omega t}+ + S_\lambda(v_0,\lambda_0)\lambda_a\Exp{i\omega t}\right)+ + D_v(v_0,\lambda_0)v_a\Exp{i\omega t}+ + D_\lambda(v_0,\lambda_0)\lambda_a\Exp{i\omega t}&=0\\ + i\omega\left( + S_v(v_0,\lambda_0)v_a+ + S_\lambda(v_0,\lambda_0)\lambda_a\right)+ + D_v(v_0,\lambda_0)v_a+ + D_\lambda(v_0,\lambda_0)\lambda_a&=0 + \end{split} +\end{equation*} + + +For the measurement, we have +\begin{align*} + M(v_0+v_a\Exp{i\omega t})\approx&M^{stdy}(v_0)+ + M^{stdy}_v(v_0)v_a\Exp{i\omega t} + i\omega M^{tran}_v(v_0)v_a\Exp{i\omega t} +\end{align*} + +Assuming $S_\lambda=0$, $\dim \MM =\dim \PP=1$ we arrive at solving +\begin{equation}\label{eq:impedsolve0} + i\omega + S_v(v_0)v_a+ + D_v(v_0,\lambda_0)v_a+ + \lambda_a D_\lambda(v_0,\lambda_0)=0 +\end{equation} +for given $\omega$ for the unknown $v_a$. +The corresponding measurement yields: +\begin{align*} + M_a(\omega)= M^{stdy}_v(v_0)v_a + i\omega M^{tran}_v(v_0)v_a +\end{align*} + +The impedance then can be calculated as +\begin{equation*}\label{eq:imped0} + Z(\omega)= \frac{\lambda_a}{M_a(\omega)} +\end{equation*} + +Dividing \eqref{eq:impedsolve0} by $\lambda_a$ (and thus rescaling $v_a$) gives the final expressions +\begin{equation}\label{eq:impedsolve} + i\omega + S_v(v_0)v_a+ + D_v(v_0,\lambda_0)v_a+ + D_\lambda(v_0,\lambda_0)=0 +\end{equation} +and +\begin{equation}\label{eq:imped} + Z(\omega)= \frac{1}{ M^{stdy}_v(v_0)v_a + i\omega M^{tran}_v(v_0)v_a}. +\end{equation} + +\subsection{Excited Dirichlet boundary condition} +This dicussion is based on the idea to implement Dirichlet boundary conditions using the +penalty method and provides an easy way to handle deriviatives with respect to +the Dirichlet boundary value. We assume that our problem corresponds to a (discretized) PDE +in a domain $\Omega$ with boundary $\Gamma=\partial\Omega$. +\begin{example}{Dirichlet Boundary Conditions}\\ + Assume that $\Gamma_0,\Gamma_1\subset \Gamma$ are two disjunct parts of + the boundary. + For (discretized) differential operator $D_i$ defined + in $\Omega$ assume that $D_i(v)=0$ is the discretization of the + homogeneous Neumann boundary value problem. Adding Robin boundary value terms + for $\Gamma_0, \Gamma_1$ with Robin coefficient $\frac1\epsilon$ and boundary values + 0, resp. $\lambda$ can be formally + written as + \begin{align*} + D(v,\lambda)= D_i(v) + +\frac1\epsilon\delta_{\Gamma_0}(v) + +\frac1\epsilon\delta_{\Gamma_1}(v-\lambda), + \end{align*} + where $\delta$ means delta functions with support at the corresponding boundary. + The limit $\epsilon\to 0$ corresponds to the Dirichlet boundary condition. + This covers the case of an + applied voltage difference and an excitation at $\Gamma_1$. + + + Then +\begin{equation*} + \begin{split} + D_v(v,\lambda)= D_{i,v}(v)+ \frac1\epsilon\delta_{\Gamma_0}+ \frac1\epsilon\delta_{\Gamma_1}\\ + D_\lambda(v,\lambda)= -\frac1\epsilon\delta_{\Gamma_1}\\ + \end{split} +\end{equation*} +Therefore for the impedance calculation we have to solve +\begin{equation*} + i\omega S_v v_a + D_{i,v} v_a +\frac1\epsilon\delta_{\Gamma_0}v_a +\frac1\epsilon\delta_{\Gamma_1}(v_a-1) =0 +\end{equation*} +which for $\epsilon\to 0$ corresponds to the Dirichlet problem for the linearized equation +with boundary condition 0 on $\Gamma_0$ and 1 on $\Gamma_1$. +\end{example} + +\subsection{Flux calculation via test function} +For a discussion, see e.g. [Gajewski, WIAS TR], [Yoder, Gärtner], [Farrell et al, Handbook]. + +Assume that $D_i(v,\lambda) = \nabla \cdot J(v) + R(v)$. Let $T$ be be a test function +such that $\Delta T +\frac1\epsilon\delta_{\Gamma_0}(T) ++\frac1\epsilon\delta_{\Gamma_1}(T-1)=0$, which for $\epsilon\to 0$ corresponds to +a mixed boundary value problem with $T=0$ on $\Gamma_0$, $T=1$ on $\Gamma_1$ and $\partial_n T=0$ on +$\partial\Omega \setminus (\Gamma_0 \cup \Gamma_1)$. +Then the boundary flux integral as measurement can be calculated as +\begin{align*} + M(v)&=\int_{\Gamma_1} J(v,\lambda)\cdot n ds + =\int_{\Gamma_1} T J(v,\lambda)\cdot n ds + =\int_{\Gamma} T J(v,\lambda)\cdot n ds\\ + &=\int_{\Omega} \nabla\cdot \left(T J(v,\lambda)\right) d\vec x + =\int_\Omega \left(J(v) \nabla T + T (\nabla \cdot J)\right) d\vec x\\ + &=\int_\Omega \left(J(v) \nabla T + T (R(v,\lambda) + d_t S(v))\right) d\vec x +\end{align*} + + +Accordingly, $M^{stdy}(v)=\int_\Omega \left(J(v) \nabla T + T R(v)\right) d\vec x$ and + $M^{tran}(v) = \int_{\Omega} S(v) T d\vec x$ +and +$$M_a(\omega)= \int_\Omega \left(J_v(v_0)\nabla v_a \nabla T + T (R_v(v_0)v_a + i\omega S_v(v_0)v_a)\right) d\vec x$$ + +With a finite volume discretizatiom, this representation has a discrete counterpart. + + +\subsection{Examples with analytical impedance expressions} +These examples can be taken for benchmarking numerical methods. +\begin{example}{Current Response} + We calculate the impedance of the current response at $L$ to voltage change in $0$ of the linear reaction diffusion system in $(0,L)$ + \begin{equation*} + \begin{cases} + Cu_t - (Du_x)_x + Ru=&0\\ + u(0,t)=&\lambda\\ + u(L,t)=&0\\ + \end{cases} + \end{equation*} + As response functions we take the currents $I_0=Du_x(0,t)$ and $I_L=-Du_x(L,t)$. + The corresponding impedance equation is + \begin{equation*} + \begin{cases} + Ci\omega v - (Dv_x)_x +Rv =&0\\ + v(0,t)=&1\\ + v(L,t)=&0.\\ + \end{cases} + \end{equation*} +Setting $z=\sqrt{i\omega\frac{C}{D}+\frac{R}{D}}$, for the solution, we make the ansatz +\begin{equation*} + v=ae^{zx}+be^{-zx} +\end{equation*} +which fulfills the differential equation. +Setting $e^+=e^{zL},e^-=e^{-zL}$, from the boundary +conditions we get the system +\begin{equation*} + \begin{cases} + a+b&=1\\ + ae^++be^-&=0\\ + \end{cases} +\end{equation*} +with the solutions $a=\frac{e^-}{e^--e^+},b=\frac{e^+}{e^+-e^-}$ +Therefore, we have +\begin{equation*} + Dv_x(0)=Dz(a-b)=Dz\frac{e^-+e^+}{e^--e^+} +\end{equation*} +and +\begin{equation*} + Dv_x(L)=Dz(ae^+-be^-)=Dz\frac{e^-e^++e^+e^-}{e^--e^+}=\frac{2Dz}{e^--e^+} +\end{equation*} + +Alternatively, we might be interested in e.g. $M(v)=\int_\Omega |\nabla v|^2$ +as measurement. We have +\begin{align*} + \nabla v&=a z e^{zx} - b z e^{-zx}\\ + |\nabla v|^2&=a z e^{2zx} -ab + b z e^{-2zx}\\ + \int |\nabla v|^2 \; dx &= \frac{1}{2z}aze^{2zx} -\frac{1}{2z} b z e^{-2zx} - abx + C\\ + &= \frac{a}{2}e^{2zx} -\frac{b}{2} b e^{-2zx} -abx +C\\ + \int_0^L |\nabla v|^2 \; dx &= \frac{a}{2}e^{2zL} -\frac{b}{2} e^{-2zL} -abL + - \frac{a}{2} +\frac{b}{2}\\ + \int_0^L |\nabla v|^2 \; dx &= \frac{a}{2}(e^{2zL}-1) -\frac{b}{2} (e^{-2zL}-1) -abL\\ + &= \frac12\frac{e^{-zL}}{e^{-zL}-e^{zL}}(e^{2zL}-1) -\frac12\frac{e^{zL}}{e^{zL}-e^{-zL}} (e^{-2zL}-1) -abL\\ + &= \frac12\frac{e^{zL}-e^{-zL}}{e^{-zL}-e^{zL}} -\frac12\frac{e^{-zL}-e^{zL}}{e^{zL}-e^{-zL}} -abL\\ + &=-abL\\ + &= \frac{L}{e^{2zL} + e^{-2zL}-2} +\end{align*} + +But ... the measurement is different: it has to be the derivative at $v_0$ applied to the solution: +\begin{align*} + 2\int_\omega \nabla v_0 \nabla v_a +\end{align*} +Steady state: $u(0)=1$, $u(L)=0$. Let $d=\sqrt{\frac{R}{D}}$. +Set $u(x)=fe^{dx}+ ge^{-dx}$. +\begin{align*} + u'(x) &= fd e^{dx} -gde^{-dx}\\ + u''(x) &= fd^2 e^{dx} +gd^2e^{-dx}=d^2 u(x)\\ + u(0)=f+g\\ + u(L)=fe^{dL}+ge^{dL} +\end{align*} +So for $d^+=e^{dL}$ and $d^-=e^{-dL}$ we get +$f=\frac{d^-}{d^--d^+}$ and $g=\frac{d^+}{d^+-d^-}$ + +So we have to calculate +\begin{align*} + \nabla v_0 \nabla v_a&= (aze^{zx}-bze^{-zx})(fde^{dx}-gde^{-dx})\\ + &= azfde^{(z+d)x} -azgde^{(z-d)x} - bzfde^{(d-z)x} + bzgde^{-(z+d)x}\\ + \int \nabla v_0 \nabla v_a &= \frac{azfd}{z+d}e^{(z+d)x} -\frac{azgd}{z-d}e^{(z-d)x} - \frac{bzfd}{d-z}e^{(d-z)x} - \frac{bzgd}{z+d}e^{-(z+d)x}\\ + &= zd\left(\frac{af}{z+d}e^{(z+d)x} -\frac{ag}{z-d}e^{(z-d)x} - \frac{bf}{d-z}e^{(d-z)x} - \frac{bg}{z+d}e^{-(z+d)x}\right) +\end{align*} + +\end{example} + + +\begin{example}{Voltage response} +Regard the system + \begin{equation*} + \begin{cases} + Cu_t - (Du_x)_x + Ru=&0\\ + u(0,t)=&\lambda\\ + (Du_x)(L,t)=&0\\ + \end{cases} + \end{equation*} + and observe the values $u(L)$ and $Du_x(0)$. +The same ansatz as in the previous example leads to +\begin{equation*} + \begin{cases} + a+b&=1\\ + izae^+-izbe^-&=0\\ + \end{cases} +\end{equation*} +with the solutions $a=\frac{e^-}{e^++e^-}$ and $b=\frac{e^+}{e^++e^-}$ +Therefore, $Du_x(0)=iDz\frac{e^--e^+}{e^++e^-}$ and +$u(L)=\frac2{e^++e^-}$. +\end{example} + + +\section{Algorithmic implementation} + +Algorithmically, we proceed as follows: + +\begin{description} +\item[1] Given $\lambda$, solve steady state equation $D(v,\lambda)=0$ +\item[2] Obtain Jacobi matrix for stationary problem +\item[3] Obtain Jacobi matrix of time derivative +\item[4] Obtain Derivative of measurement (both steady state and transient parts) +\item[5] Prepare system for impedance problem +\item[6] For $\omega=\omega_0\dots\omega_1$: + \begin{description} + \item[6.1] set up complex matrix + \item[6.2] solve complex system, take old solution as initial value if this is done iteratively + \item[6.3]calculate impedance functional + \end{description} +\end{description} + +\end{document} + + + + + + + + + + diff --git a/v1.19.1/index.html b/v1.19.1/index.html new file mode 100644 index 000000000..90b000e85 --- /dev/null +++ b/v1.19.1/index.html @@ -0,0 +1,2 @@ + +Home · VoronoiFVM.jl

    VoronoiFVM.jl

    Build status DOI Zulip Chat

    Solver for coupled nonlinear partial differential equations (elliptic-parabolic conservation laws) based on the Voronoi finite volume method. It uses automatic differentiation via ForwardDiff.jl and DiffResults.jl to evaluate user functions along with their jacobians and calculate derivatives of solutions with respect to their parameters.

    Recent changes

    Please look up the list of recent changes

    Accompanying packages

    VoronoiFVM.jl and most of these packages are part of the meta package PDELib.jl.

    Some alternatives

    Citation

    If you use this package in your work, please cite it according to CITATION.cff

    Papers and preprints using this package

    Please consider a pull request if you have published work which could be added to this list.

    [1]
    S. Matera, C. Merdon and D. Runge. Reduced Basis Approach for Convection-Diffusion Equations with Non-linear Boundary Reaction Conditions. In: International Conference on Finite Volumes for Complex Applications (Springer, 2023); pp. 335–343.
    [2]
    B. Spetzler, D. Abdel, F. Schwierz, M. Ziegler and P. Farrell. The Role of Vacancy Dynamics in Two-Dimensional Memristive Devices. Advanced Electronic Materials, 2300635 (2023).
    [3]
    S. Scholz and L. Berger. Hestia.jl: A Julia Library for Heat Conduction Modeling with Boundary Actuation. Simul. Notes Eur. 33, 27–30 (2023).
    [4]
    R. P. Schärer and J. Schumacher. A Transient Non-isothermal Cell Performance Model for Organic Redox Flow Batteries. In: 19th Symposium on Modeling and Experimental Validation of Electrochemical Energy Technologies (ModVal), Duisburg, Germany, 21-23 March 2023 (2023).
    [5]
    J. Fuhrmann, B. Gaudeul and C. Keller. Two Entropic Finite Volume Schemes for a Nernst–Planck–Poisson System with Ion Volume Constraints. In: International Conference on Finite Volumes for Complex Applications (Springer, 2023); pp. 285–294.
    [6]
    P. Vágner, M. Pavelka, J. Fuhrmann and V. Klika. A multiscale thermodynamic generalization of Maxwell-Stefan diffusion equations and of the dusty gas model. International Journal of Heat and Mass Transfer 199, 123405 (2022).
    [7]
    V. Miloš, P. Vágner, D. Budáč, M. Carda, M. Paidar, J. Fuhrmann and K. Bouzek. Generalized Poisson-Nernst-Planck-based physical model of an O2 | LSM | YSZ electrode. Journal of the Electrochemical Society, 044505 (2022).
    [8]
    L. Xiao, G. Mei, N. Xi and F. Piccialli. Julia language in computational mechanics: A new competitor. Archives of Computational Methods in Engineering 29, 1713–1726 (2022).
    [9]
    B. Gaudeul and J. Fuhrmann. Entropy and convergence analysis for two finite volume schemes for a Nernst–Planck–Poisson system with ion volume constraints. Numerische Mathematik 151, 99–149 (2022).
    [10]
    J. R. Martins, F. Alves and P. M. Ferreira. From Semiconductor to Transistor-Level: Modeling, Simulation, and Layout Rendering Tools. In: Colloque du GdR SOC2 (2022), hal-03690082.
    [11]
    J. Jambrich. Consistent non-equilibrium thermodynamic modeling of hydrogen fuel cells. Master's thesis, Univerzita Karlova, Matematicko-fyzikálnı́ fakulta (2022).
    [12]
    S. B. Chinnery. TCAD-Informed Surrogate Models of Semiconductor Devices. Master's thesis, Massachusetts Institute of Technology (2022).
    [13]
    D. Abdel, P. Vágner, J. Fuhrmann and P. Farrell. Modelling charge transport in perovskite solar cells: Potential-based and limiting ion depletion. Electrochimica Acta 390, 138696 (2021).
    [14]
    D. Abdel, P. Farrell and J. Fuhrmann. Assessing the quality of the excess chemical potential flux scheme for degenerate semiconductor device simulation. Optical and Quantum Electronics 53, 1–10 (2021).
    [15]
    C. Cancès, C. Chainais-Hillairet, J. Fuhrmann and B. Gaudeul. A numerical-analysis-focused comparison of several finite volume schemes for a unipolar degenerate drift-diffusion model. IMA Journal of Numerical Analysis 41, 271–314 (2021).
    [16]
    J. Park, J. H. Cho and R. D. Braatz. Mathematical modeling and analysis of microwave-assisted freeze-drying in biopharmaceutical applications. Computers & Chemical Engineering 153, 107412 (2021).
    [17]
    C. Cancès, C. C. Hillairet, J. Fuhrmann and B. Gaudeul. On four numerical schemes for a unipolar degenerate drift-diffusion model. In: Finite Volumes for Complex Applications IX-Methods, Theoretical Aspects, Examples: FVCA 9, Bergen, Norway, June 2020 IX (Springer, 2020); pp. 163–171.
    diff --git a/v1.19.1/internal/index.html b/v1.19.1/internal/index.html new file mode 100644 index 000000000..0dac7fc7e --- /dev/null +++ b/v1.19.1/internal/index.html @@ -0,0 +1,48 @@ + +Internal API · VoronoiFVM.jl

    Internal API

    Besides of the interface methods for VoronoiFVMDiffEq, these are not exported and therefore should not be used outside of the package

    Wrapping evaluators for physics callbacks

    VoronoiFVM.ResEvaluatorType
    struct ResEvaluator{Tv<:Number, Func<:Function, G} <: VoronoiFVM.AbstractEvaluator

    Evaluator for functions from physics. Allows to call different types of physic functions (flux, reaction, source) an provides a common interface to different function formats (with data, without data etc.)

    • fwrap::Function: wrapper function in Format ready for Diffetential equations

    • y::Vector{Tv} where Tv<:Number: pre-allocated result

    • geom::Any: Geometry object # geometry (node, edge...)

    • nspec::Int64: number of species

    • isnontrivial::Bool: Is the function not one of nofunc ot nofunc2

    source
    VoronoiFVM.ResEvaluatorMethod
     ResEvaluator(physics,symb,uproto,geom,nspec)

    Constructor for ResEvaluator

    • physics Physics object
    • symb: symbol naming one of the functions in physics to be wrapped.
    • uproto: solution vector prototype,
    • geom: node, edge...
    • nspec: number of species
    source
    VoronoiFVM.evaluate!Method
    evaluate!(e::VoronoiFVM.ResEvaluator)
    +

    Call function in evaluator, store result in predefined memory.

    source
    VoronoiFVM.evaluate!Method
    evaluate!(e::VoronoiFVM.ResEvaluator, u)
    +

    Call function in evaluator, store result in predefined memory.

    source
    VoronoiFVM.resMethod
    res(
    +    e::VoronoiFVM.ResEvaluator
    +) -> Vector{Tv} where Tv<:Number
    +

    Retrieve evaluation result

    source
    VoronoiFVM.ResJacEvaluatorType
    struct ResJacEvaluator{Tv<:Number, Func<:Function, Cfg, Res, G} <: VoronoiFVM.AbstractEvaluator

    Evaluator for functions from physics and their Jacobians. Allows to call different types of physic functions (flux, reaction, source) an provides a common interface to different function formats (with data, without data etc.)

    • fwrap::Function: wrapper function in Format ready for Differential equations

    • config::Any: ForwardDiff.JacobianConfig

    • result::Any: DiffResults.JacobianResult

    • y::Vector{Tv} where Tv<:Number: pre-allocated result

    • geom::Any: Geometry object # geometry (node, edge...)

    • nspec::Int64: number of species

    • isnontrivial::Bool: Is the function not one of nofunc ot nofunc2

    source
    VoronoiFVM.ResJacEvaluatorMethod
    ResJacEvaluator(physics, symb, uproto, geom, nspec)
    +

    Constructor for ResJEvaluator

    • physics Physics object
    • symb: symbol naming one of the functions in physics to be wrapped.
    • uproto: solution vector prototype,
    • geom: node, edge...
    • nspec: number of species
    source
    VoronoiFVM.evaluate!Method
    evaluate!(e::VoronoiFVM.ResJacEvaluator, u)
    +

    Call function in evaluator, store result and jacobian in predefined memory.

    source
    VoronoiFVM.resMethod
    res(e::VoronoiFVM.ResJacEvaluator) -> Any
    +

    Retrieve evaluation result

    source
    VoronoiFVM.isnontrivialFunction
    isnontrivial(e::VoronoiFVM.AbstractEvaluator) -> Any
    +

    Does calling the evaluator giva nontrivial (nonzero) result?

    source

    Global node and edge assembly loops

    VoronoiFVM.CellwiseAssemblyDataType
    struct CellwiseAssemblyData{Tv, Ti} <: VoronoiFVM.AbstractAssemblyData{Tv, Ti}

    Data for cellwise assembly.

    • nodefactors::Matrix: Precomputed geometry factors for cell nodes. This is a ncells x nnodes_per_cell full matrix.
    • edgefactors::Matrix: Precomputed geometry factors for cell edges This is a ncells x nedge_per_cell full matrix.
    source
    VoronoiFVM.EdgewiseAssemblyDataType
    struct EdgewiseAssemblyData{Tv, Ti} <: VoronoiFVM.AbstractAssemblyData{Tv, Ti}
    • nodefactors::SparseArrays.SparseMatrixCSC{Tv, Ti} where {Tv, Ti}: Precomputed geometry factors for nodes. This is a nnodes x nregions sparse matrix.
    • edgefactors::SparseArrays.SparseMatrixCSC{Tv, Ti} where {Tv, Ti}: Precomputed geometry factors for edges This is a nedges x nregions sparse matrix.
    source
    VoronoiFVM._fill!Function
    _fill!(node, asmdata, inode, icell)
    +

    Fill node with the help of assemblydata.

    source
    _fill!(node, asmdata, ibnode, ibface)
    +

    Fill boundary node with the help of assemblydata.

    source
    _fill!(edge, asmdata, iedge, icell)
    +

    Fill edge with the help of assemblydata.

    source
    _fill!(bedge, asmdata, ibedge, ibface)
    +

    Fill boundary edge with the help of assemblydata.

    source
    _fill!(node, asmdata, k, inode)
    +

    Fill node with the help of assemblydata.

    source
    _fill!(edge, asmdata, k, iedge)
    +

    Fill edge with the help of assemblydata.

    source

    Local node and edge assembly loops

    Local assembly methods organize the assembly of data to those degrees of freedom (dofs) which are defined for a given node or edge. E.g. for an node residual for nspec defined species, only those entries need to be assembled into the global residual vector which correspond to actually defined degrees of freedom.

    Similarly for nspec x nspec node Jacobian, an for the nparam x nspec parameter derivatives.

    These local assembly methods organize the correct loops and call back to the concrete assembly methods passed to them. These receive global degrees of freedom and the local species numbers to be handled. The callbacks can be used as well for other purposes than assembly

    VoronoiFVM.assemble_res_jacFunction
    assemble_res_jac(node, system, asm_res, asm_jac, asm_param)
    +

    Assemble residual and jacobian for node functions. Parameters:

    • system: System to be worked with

    • node: node

    • asm_jac(idof,jdof,ispec,jspec): e.g. assemble entry ispec,jspec of local jacobian into entry idof,jdof of global matrix

    • asm_param(idof,ispec,iparam) shall assemble parameter derivatives

    source
    assemble_res_jac(bnode, system, asm_res, asm_jac, asm_param)
    +

    Assemble residual and jacobian for boundary node functions. See assemble_res_jac for more explanations.

    source
    assemble_res_jac(edge, system, asm_res, asm_jac, asm_param)
    +

    Assemble residual and jacobian for edge (flux) functions. Parameters:

    • system: System to be worked with
    • edge: edge
    • asm_res(idofK,idofL,ispec): e.g. assemble local ispec to global degrees of freedom in unknowns
    • asm_jac(idofK,jdofK,idofL,jdofL,ispec,jspec): e.g. assemble entry ispec,jspec of local jacobian into entry four entries defined by idofK and idofL of global matrix
    • asm_param(idofK,idofL,ispec,iparam) shall assemble parameter derivatives
    source
    assemble_res_jac(bedge, system, asm_res, asm_jac, asm_param)
    +

    Assemble residual and jacobian for boundary edge (flux) functions. See assemble_res_jac for more explanations.

    source
    VoronoiFVM.assemble_resFunction
    assemble_res(node, system, asm_res)
    +

    Assemble residual for node functions. See assemble_res_jac for more explanations.

    source
    assemble_res(bnode, system, asm_res)
    +

    Assemble residual for boundary node functions. See assemble_res_jac for more explanations.

    source
    assemble_res(edge, system, asm_res)
    +

    Assemble residual for edge (flux) functions. See assemble_res_jac for more explanations.

    source
    assemble_res(bedge, system, asm_res)
    +

    Assemble residual for boundary edge (flux) functions. See assemble_res_jac for more explanations.

    source

    Degree of Freedom management

    We distinguish

    • active degrees of freedom: these are the actual degrees of freedom
    • degrees of freedom (dof) potential degrees of freedom - the may be active dofs or dummy ones With sparse arrays there are no dummy ones, with dense arrays dummy are maske in the node_dof field
    • species: each degree of freedom has associated the species it represents and the node index where it is localized
    VoronoiFVM.getnodedofFunction
    getnodedof(system,ispec,inode)

    Get active or dummy degree of freedom associated with node and species

    source

    Abstract geometry data

    VoronoiFVM.AbstractNodeType
    abstract type AbstractNode{Tc<:Number, Tp<:Number, Ti<:Integer} <: VoronoiFVM.AbstractGeometryItem{Tc<:Number, Tp<:Number, Ti<:Integer}

    Abstract type for nodes.

    node[idim] gives the the corresponding coordinate.

    source
    VoronoiFVM.AbstractNodeDataType
    abstract type AbstractNodeData{Tv<:Number} <: AbstractArray{Tv<:Number, 1}

    Abstract type for data on nodes. u[ispec] accesses value of species at this node.

    source
    VoronoiFVM.AbstractEdgeType
    abstract type AbstractEdge{Tv<:Number, Tp<:Number, Ti<:Integer} <: VoronoiFVM.AbstractGeometryItem{Tv<:Number, Tp<:Number, Ti<:Integer}

    Abstract type for edges

    edge[idim,inode] gives coordinate of node.

    source
    VoronoiFVM.AbstractEdgeDataType
    abstract type AbstractEdgeData{Tv<:Number} <: AbstractArray{Tv<:Number, 2}

    Abstract type for data on edges. u[ispec,inode] accesses value of species at corresponding node.

    source

    Global assembly & helpers

    VoronoiFVM.eval_and_assembleFunction
    eval_and_assemble(
    +    system,
    +    U,
    +    UOld,
    +    F,
    +    time,
    +    tstep,
    +    λ,
    +    params;
    +    edge_cutoff
    +)
    +

    Main assembly method.

    Evaluate solution with result in right hand side F and assemble Jacobi matrix into system.matrix.

    source
    VoronoiFVM._addFunction
    _add(U::Array{Tv, 2}, idof, val) -> Any
    +

    Add residual value into global degree of freedom

    source
    _add(U::VoronoiFVM.SparseSolutionArray, idof, val) -> Any
    +

    Add residual value into global degree of freedom

    source

    Interface methods for VoronoiFVMDiffEq.jl

    VoronoiFVM._eval_res_jac!Function
    _eval_res_jac!(sys, u, t)
    +

    Evaluate functiaon and Jacobian at u if they have not been evaluated before at u. See https://github.com/SciML/DifferentialEquations.jl/issues/521 for discussion of another way to do this.

    source
    VoronoiFVM.mass_matrixFunction
    mass_matrix(system)
    +

    Calculate the mass matrix for use with ODEFunction. Return a Diagonal matrix if it occurs to be diagonal, otherwise return a SparseMatrixCSC.

    source
    VoronoiFVM.prepare_diffeq!Function
    prepare_diffeq!(sys, jacval, tjac)
    +

    Prepare system for use with VoronoiFVMDiffEq.

    • jacval: value at which to evaluate jacobian to obtatin prototype
    • tjac: time moment for jacobian

    Returns a prototype for the jacobian.

    source
    diff --git a/v1.19.1/method/index.html b/v1.19.1/method/index.html new file mode 100644 index 000000000..0f98478b8 --- /dev/null +++ b/v1.19.1/method/index.html @@ -0,0 +1,21 @@ + +The Voronoi finite volume method · VoronoiFVM.jl

    The Voronoi finite volume method

    Construction of control volumes

    • Start with a boundary conforming Delaunay triangulation of a polygonal domain (intervals in 1D, triangles in 2D, tetrahedra in 3D). Such a triangulation can be generated by e.g. by the mesh generators triangle and TetGen. These are available in Julia via Triangulate.jl and TetGen.jl. For simple geometries – tensor products of lower dimensional grids – such triangulation can be created more easily. The package ExtendableGrids.jl manages the grid data structure which is used in this package. SimplexGridFactory.jl interfaces this grid structure with Triangulate.jl and TetGen.jl and provides an API for incrementally setting up geometry descriptions.

    • Join triangle circumcenters by lines $\rightarrow$ create Voronoi cells which can serve as control volumes, akin to representative elementary volumes (REV) used to derive conservation laws.

    + +
    • Black + green: triangle nodes
    • Gray: triangle edges
    • Blue: triangle circumcenters
    • Red: Boundaries of Voronoi cells

    In order to make this construction possible, the triangulation must have the boundary conforming Delaunay property:

    • The interior of any triangle circumcircle does not contain any other node of the triangulation
    • All circumcircle centers lay within the domain

    In 2D, an equivalent condition is:

    • The sum of triangle angles opposite to a given interior edge is less than $\pi$
    • Triangle angles opposite to boundary edges are less than $\frac\pi2$.

    As a consequence, there is a 1:1 incidence between triangulation nodes and Voronoi cells. Moreover, the angle between the interface between two neighboring Voronoi cells and the edge between their corresponding nodes is $\frac\pi2$. Therefore the edge direction is aligned with the normal direction with respect to the boundary of the Voronoi cell. This makes it easy to use these Voronoi cells as REVs aka control volumes aka finite volume cells and to derive a space discretization for a conservation law based on very same balance rules used to derive this conservation law.

    The discretization approach

    + +

    Given a continuity equation $\nabla\cdot \vec j=f$ in a domain $\Omega$, integrate it over a control volume $\omega_k$ with associated node $\vec x_k$ and apply Gauss theorem:

    \[\begin{aligned} +0&=\int_{\omega_k} (\nabla\cdot \vec j -f )\ d\omega +=\int_{\partial\omega_k} \vec j\cdot \vec n ds - \int_{\omega_k} f d\omega\\ +&=\sum_{l\in N_k} \int_{\omega_k\cap \omega_l} \vec j\cdot \vec n ds + \int_{\partial\omega_k\cap \partial\Omega} \vec j\cdot \vec n ds - \int_{\omega_k} f d\omega \\ +&\approx \sum_{l\in N_k} \frac{\sigma_{kl}}{h_{kl}}g(u_k, u_l) - |\omega_k| f_k + \text{boundary terms} +\end{aligned}\]

    Here, $N_k$ is the set of neighbor control volumes, $\sigma_{kl}=|\omega_k\cap \omega_l|$, $h_{kl}=|\vec x_k -\vec x_l|$, where $|\cdot|$ denotes the measure (length resp. area) of a geometrical entity. In the approximation step, we replaced the normal flux integral over the interface between two control volumes by the measure of this interface multiplied by a function depending on the unknowns $u_k, u_l$ associated to the respective nodes divided by the distance between these nodes. The flux function $g$ can be derived from usual finite difference formulas discretizing a particular flux law.

    Flux laws

    For instance, for the diffusion flux $\vec j=-D\vec\nabla u$, we use $g(u_k, u_l)=D(u_k -u_l)$.

    For a convective diffusion flux $\vec j = -D\vec \nabla u + u \vec v$, one can chose the upwind flux

    \[\begin{aligned} +g(u_k, u_l)=D(u_k -u_l) + +v_{kl}\begin{cases} +u_k,& v_{kl}>0\\ +u_l,& v_{kl}\leq 0, +\end{cases} +\end{aligned}\]

    where $v_{kl}=\frac{h_{kl}}{\sigma_{kl}}\int_{\omega_k\cap \omega_l} \vec v \cdot \vec n_{kl} \ ds$ Fluxes also can depend nonlinearily on $u$.

    Boundary conditions

    To implement a Robin boundary condition on $\Gamma=\partial\Omega$

    \[- \vec j \cdot \vec n + a u = b,\]

    we note that by the very construction, the discretization nodes associated to control volumes adjacent to the domain boundary are located at the domain boundary, thus we can assume that the boundary condition is valid in the corresponding collocation node $u_k$. We assume that $\partial\omega_k\cap \partial_\Omega= \cup_{m\in\mathcal M_k} \gamma_{km}$ is the union of a finite number of line (plane) segments. For interior nodes, we set $\mathcal M_k = \emptyset$ . Thus, for the boundary terms in the above equation, we have

    \[\begin{aligned} +\text{boundary terms}&=\sum_{m\in\mathcal M_k} \int_{\gamma_{km}} \vec j \cdot \vec n d s + &\approx \sum_{m\in\mathcal M_k} |\gamma_{km}| \vec j \cdot \vec n\\ + &\approx\sum_{m\in\mathcal M_k} |\gamma_{km}| (au_k -b), +\end{aligned}\]

    We observe that for $\varepsilon\to 0$, the Robin boundary condition

    \[- \vec j \cdot \vec n + \frac{1}{\varepsilon}u = \frac{1}{\varepsilon}g\]

    tends to the Dirichlet bundary condition

    \[ u=g.\]

    Therefore, a Dirichlet boundary condition can be approximated by choosing a small value of $\varepsilon$ and implying the aforementioned Robin boundary conditions. This approach called penalty method is chosen for the implementation of Dirichlet boundary conditions in this package.

    Time dependent problems, reaction terms

    This approach easily generalizes to time dependent nonlinear transport-reaction problems with storage terms $s(u)$, reaction terms $r(u)$ and source terms $f$:

    \[\partial_t s(u) + \nabla \cdot \vec j + r(u) -f =0\]

    Semidiscretization in time (for implicit Euler) leads to

    \[\frac{s(u)-s(u^\flat)}{\tau} + \nabla \cdot \vec j + r(u) -f =0\]

    where $\tau$ is the time step size and $u^\flat$ is the solution from the old timestep. The approximation approach then for each control volume gives

    \[|\omega_k|\frac{s(u_k)-s(u_k^\flat)}{\tau} + \sum_{l\in N_k} \frac{\sigma_{kl}}{h_{kl}}g(u_k, u_l)+ \sum_{m\in\mathcal M_k} |\gamma_{km}| (au_k -b) + |\omega_k| (r(u_k)- f_k)=0\]

    If $n$ is the number of discretization nodes, we get a system of $n$ equations with $n$ unknowns which under proper conditions on $r,g,s$ has a unique solution.

    The implicit Euler method is the default solver for time dependent problems. Alternatively, ODE and DAE solvers from DifferentialEquations.jl can be used with an upcoming glue package.

    Generalizations to systems

    This approach generalizes to systems of partial differential equations, which formally can be written in the same way, but assuming that $u$ is a vector function of $\vec x,t$, and $r,g,s$ are vector functions of their arguments. The package allows to handle different sets of species in different subdomains of $\Omega$.

    Boundary reactions, boundary species

    In addition to $r,g,s$, the package allows to specify additional boundary species, boundary reaction, boundary flux and boundary storage terms.

    Why this method ?

    Independent of space dimension, the method (with properly chosen flux functions) is able to preserve a number of physical quantities if they are present on the continuous level:

    • local and global mass conservation
    • positivity of solutions
    • maximum principle: in the absence of source and reaction terms, local extrema of the stationary solution are located at the domain boundaries, never in the interior. For transient problems, local extrema in the interior can only come from the initial value.
    • Consistency to thermodynamics: entropy production etc.

    Many of these properties are hard to prove for finite element methods, in particular for the convection-diffusion case.

    Where is this method not appropriate ?

    There are a number of cases where this method needs to be replaced by something else or at least to be applied with great care:

    • Anisotropic diffusion only works with proper mesh alignment
    • Strongly varying capacity (in the function $s$) at domain interfaces lead to inexact breakthrough curves
    • Sharp moving convection fronts are smeared out too strongly

    History and literature

    The following list is work in progress and incomplete, but it references some sources behind the ideas in this package.

    • Macneal, R. H. (1953). An asymmetrical finite difference network. Quarterly of Applied Mathematics, 11(3), 295-310. (pdf via JSTOR). Perhaps this is the earliest mentioning of the method. Note that it was used on an electrical analog computer.
    • Gärtner, K., & Kamenski, L. (2019). Why do we need Voronoi cells and Delaunay meshes? arXiv preprint arXiv:1905.01738. A recent overview on the merits of the method. One of the authors belongs to the pioneers of its application in 3D.
    • Fuhrmann, J., & Langmach, H. (2001). Stability and existence of solutions of time-implicit finite volume schemes for viscous nonlinear conservation laws. Applied Numerical Mathematics, 37(1-2), 201-230. A discussion of the method applied to rather general nonlinear scalar problems.
    • Si, H., Gärtner, K., & Fuhrmann, J. (2010). Boundary conforming Delaunay mesh generation. Computational Mathematics and Mathematical Physics, 50(1), 38-53. Definition of the boundary conforming Delaunay property.
    • Eymard, R., Fuhrmann, J., & Gärtner, K. (2006). A finite volume scheme for nonlinear parabolic equations derived from one-dimensional local Dirichlet problems. Numerische Mathematik, 102(3), 463-495. General concept of the derivation of upwind fluxes for nonlinear problems.
    • Farrell, P., Rotundo, N., Doan, D. H., Kantner, M., Fuhrmann, J., & Koprucki, T. (2017). Drift-diffusion models. In Handbook of Optoelectronic Device Modeling and Simulation (pp. 733-772). CRC Press. Overview and introduction to the method applied to semiconductor device simulation. This problem class profits most from the desirable properties of the method.

    Software API and implementation

    The entities describing the discrete system can be subdivided into two categories:

    • Geometrical data: $|\omega_k|, \gamma_k, \sigma_{kl}, h_{kl}$ together with the connectivity information simplex grid. These data are calculated from the discretization grid.
    • Physics data: the number of species and the functions $s,g,r,f$ etc. describing the particular problem.

    The solution of the nonlinear systems of equations is performed by Newton's method combined with various direct and iterative linear solvers. The Jacobi matrices used in Newton's method are assembled from the constitutive functions with the help of forward mode automatic differentiation implemented in ForwardDiff.jl.

    diff --git a/v1.19.1/misc/index.html b/v1.19.1/misc/index.html new file mode 100644 index 000000000..6cf91eb4c --- /dev/null +++ b/v1.19.1/misc/index.html @@ -0,0 +1,7 @@ + +Miscellaneous · VoronoiFVM.jl

    Miscellaneous

    Useful helpers

    VoronoiFVM.valueFunction

    Extract value from dual number. Use to debug physics callbacks. Re-exported from ForwardDiff.jl

    source

    Form factor calculatione

    Velocity projections

    Additional grid methods

    VoronoiFVM.GridFunction
    Grid=ExtendableGrids.simplexgrid

    Re-Export of ExtendableGrids.simplexgrid

    source

    Exception types

    diff --git a/v1.19.1/module_examples/Example001_Solvers.jl b/v1.19.1/module_examples/Example001_Solvers.jl new file mode 100644 index 000000000..c84cb6def --- /dev/null +++ b/v1.19.1/module_examples/Example001_Solvers.jl @@ -0,0 +1,149 @@ +#= +# 001: New linear solver API +([source code](@__SOURCE_URL__)) +=# + +module Example001_Solvers + +## under development + +using Printf +using VoronoiFVM +using ExtendableGrids +using GridVisualize +using LinearSolve +using ExtendableSparse +using AMGCLWrap +using AlgebraicMultigrid +using LinearAlgebra +using Test + +function main(; n = 10, Plotter = nothing, assembly = :edgwwise, kwargs...) + h = 1.0 / convert(Float64, n) + X = collect(0.0:h:1.0) + Y = collect(0.0:h:1.0) + + grid = VoronoiFVM.Grid(X, Y) + nn = num_nodes(grid) + + eps = 1.0e-2 + + function reaction(f, u, node) + f[1] = u[1]^2 + end + + function flux(f, u, edge) + f[1] = eps * (u[1, 1]^2 - u[1, 2]^2) + end + + function source(f, node) + x1 = node[1] - 0.5 + x2 = node[2] - 0.5 + f[1] = exp(-20.0 * (x1^2 + x2^2)) + end + + function storage(f, u, node) + f[1] = u[1] + end + + function bcondition(f, u, node) + boundary_dirichlet!(f, + u, + node; + species = 1, + region = 2, + value = ramp(node.time; dt = (0, 0.1), du = (0, 1))) + boundary_dirichlet!(f, + u, + node; + species = 1, + region = 4, + value = ramp(node.time; dt = (0, 0.1), du = (0, 1))) + end + + sys = VoronoiFVM.System(grid; reaction, flux, source, storage, bcondition, assembly, + species = [1]) + @info "UMFPACK:" + umf_sol = solve(sys; inival = 0.5, method_linear = UMFPACKFactorization(), kwargs...) + + @info "KLU:" + sol = solve(sys; inival = 0.5, method_linear = KLUFactorization(), kwargs...) + @test norm(sol - umf_sol, Inf)<1.0e-7 + + @info "Sparspak:" + sol = solve(sys; inival = 0.5, method_linear = SparspakFactorization(), kwargs...) + @test norm(sol - umf_sol, Inf)<1.0e-7 + + @info "Krylov-ilu0:" + sol = solve(sys; + inival = 0.5, + method_linear = KrylovJL_BICGSTAB(), + precon_linear = ILUZeroPreconditioner(), + kwargs...) + @test norm(sol - umf_sol, Inf)<1.0e-7 + + @info "Krylov-block1" + sol = solve(sys; + inival = 0.5, + method_linear = KrylovJL_BICGSTAB(), + precon_linear = BlockPreconditioner(; partitioning = [1:(nn ÷ 2), (nn ÷ 2 + 1):nn], + factorization = ILU0Preconditioner()), + kwargs...) + @test norm(sol - umf_sol, Inf)<1.0e-7 + + @info "Krylov-block2" + sol = solve(sys; + inival = 0.5, + method_linear = KrylovJL_BICGSTAB(), + precon_linear = BlockPreconditioner(; partitioning = [1:2:nn, 2:2:nn], + factorization = UMFPACKFactorization()), + kwargs...) + @test norm(sol - umf_sol, Inf)<1.0e-7 + + @info "Krylov - delayed factorization:" + sol = solve(sys; + inival = 0.5, + method_linear = KrylovJL_BICGSTAB(), + precon_linear = SparspakFactorization(), + keepcurrent_linear =false, + kwargs...) + @test norm(sol - umf_sol, Inf)<1.0e-7 + + @info "Krylov - jacobi:" + sol = solve(sys; + inival = 0.5, + method_linear = KrylovJL_BICGSTAB(), + precon_linear = JacobiPreconditioner(), + keepcurrent_linear = true, + kwargs...) + @test norm(sol - umf_sol, Inf)<1.0e-7 + + @info "Krylov - SA_AMG:" + sol = solve(sys; + inival = 0.5, + method_linear = KrylovJL_BICGSTAB(), + precon_linear = SA_AMGPreconditioner(), + keepcurrent_linear = true, + kwargs...) + @test norm(sol - umf_sol, Inf)<1.0e-7 + + @info "Krylov - AMGCL_AMG:" + sol = solve(sys; + inival = 0.5, + method_linear = KrylovJL_BICGSTAB(), + precon_linear = AMGCL_AMGPreconditioner(), + keepcurrent_linear = true, + kwargs...) + @test norm(sol - umf_sol, Inf)<1.0e-7 + +end + +function runtests() + @testset "edgewise" begin + main(; assembly = :edgewise) + end + @testset "cellwise" begin + main(; assembly = :cellwise) + end +end +end diff --git a/v1.19.1/module_examples/Example001_Solvers/index.html b/v1.19.1/module_examples/Example001_Solvers/index.html new file mode 100644 index 000000000..0282fcbea --- /dev/null +++ b/v1.19.1/module_examples/Example001_Solvers/index.html @@ -0,0 +1,145 @@ + +001: New linear solver API · VoronoiFVM.jl

    001: New linear solver API

    (source code)

    module Example001_Solvers
    +
    +# under development
    +
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +using LinearSolve
    +using ExtendableSparse
    +using AMGCLWrap
    +using AlgebraicMultigrid
    +using LinearAlgebra
    +using Test
    +
    +function main(; n = 10, Plotter = nothing, assembly = :edgwwise, kwargs...)
    +    h = 1.0 / convert(Float64, n)
    +    X = collect(0.0:h:1.0)
    +    Y = collect(0.0:h:1.0)
    +
    +    grid = VoronoiFVM.Grid(X, Y)
    +    nn = num_nodes(grid)
    +
    +    eps = 1.0e-2
    +
    +    function reaction(f, u, node)
    +        f[1] = u[1]^2
    +    end
    +
    +    function flux(f, u, edge)
    +        f[1] = eps * (u[1, 1]^2 - u[1, 2]^2)
    +    end
    +
    +    function source(f, node)
    +        x1 = node[1] - 0.5
    +        x2 = node[2] - 0.5
    +        f[1] = exp(-20.0 * (x1^2 + x2^2))
    +    end
    +
    +    function storage(f, u, node)
    +        f[1] = u[1]
    +    end
    +
    +    function bcondition(f, u, node)
    +        boundary_dirichlet!(f,
    +                            u,
    +                            node;
    +                            species = 1,
    +                            region = 2,
    +                            value = ramp(node.time; dt = (0, 0.1), du = (0, 1)))
    +        boundary_dirichlet!(f,
    +                            u,
    +                            node;
    +                            species = 1,
    +                            region = 4,
    +                            value = ramp(node.time; dt = (0, 0.1), du = (0, 1)))
    +    end
    +
    +    sys = VoronoiFVM.System(grid; reaction, flux, source, storage, bcondition, assembly,
    +                            species = [1])
    +    @info "UMFPACK:"
    +    umf_sol = solve(sys; inival = 0.5, method_linear = UMFPACKFactorization(), kwargs...)
    +
    +    @info "KLU:"
    +    sol = solve(sys; inival = 0.5, method_linear = KLUFactorization(), kwargs...)
    +    @test norm(sol - umf_sol, Inf)<1.0e-7
    +
    +    @info "Sparspak:"
    +    sol = solve(sys; inival = 0.5, method_linear = SparspakFactorization(), kwargs...)
    +    @test norm(sol - umf_sol, Inf)<1.0e-7
    +
    +    @info "Krylov-ilu0:"
    +    sol = solve(sys;
    +                inival = 0.5,
    +                method_linear = KrylovJL_BICGSTAB(),
    +                precon_linear = ILUZeroPreconditioner(),
    +                kwargs...)
    +    @test norm(sol - umf_sol, Inf)<1.0e-7
    +
    +    @info "Krylov-block1"
    +    sol = solve(sys;
    +                inival = 0.5,
    +                method_linear = KrylovJL_BICGSTAB(),
    +                precon_linear = BlockPreconditioner(; partitioning = [1:(nn ÷ 2), (nn ÷ 2 + 1):nn],
    +                                                    factorization = ILU0Preconditioner()),
    +                      kwargs...)
    +    @test norm(sol - umf_sol, Inf)<1.0e-7
    +
    +    @info "Krylov-block2"
    +    sol = solve(sys;
    +                inival = 0.5,
    +                method_linear = KrylovJL_BICGSTAB(),
    +                precon_linear = BlockPreconditioner(; partitioning = [1:2:nn, 2:2:nn],
    +                                                    factorization = UMFPACKFactorization()),
    +                      kwargs...)
    +    @test norm(sol - umf_sol, Inf)<1.0e-7
    +
    +    @info "Krylov - delayed factorization:"
    +    sol = solve(sys;
    +                inival = 0.5,
    +                method_linear = KrylovJL_BICGSTAB(),
    +                precon_linear = SparspakFactorization(),
    +                keepcurrent_linear =false,
    +                kwargs...)
    +    @test norm(sol - umf_sol, Inf)<1.0e-7
    +
    +    @info "Krylov - jacobi:"
    +    sol = solve(sys;
    +                inival = 0.5,
    +                method_linear = KrylovJL_BICGSTAB(),
    +                precon_linear = JacobiPreconditioner(),
    +                keepcurrent_linear = true,
    +                kwargs...)
    +    @test norm(sol - umf_sol, Inf)<1.0e-7
    +
    +    @info "Krylov - SA_AMG:"
    +    sol = solve(sys;
    +                inival = 0.5,
    +                method_linear = KrylovJL_BICGSTAB(),
    +                precon_linear = SA_AMGPreconditioner(),
    +                keepcurrent_linear = true,
    +                kwargs...)
    +    @test norm(sol - umf_sol, Inf)<1.0e-7
    +
    +    @info "Krylov - AMGCL_AMG:"
    +    sol = solve(sys;
    +                inival = 0.5,
    +                method_linear = KrylovJL_BICGSTAB(),
    +                precon_linear = AMGCL_AMGPreconditioner(),
    +                keepcurrent_linear = true,
    +                kwargs...)
    +    @test norm(sol - umf_sol, Inf)<1.0e-7
    +
    +end
    +
    +function runtests()
    +    @testset "edgewise" begin
    +        main(; assembly = :edgewise)
    +    end
    +    @testset "cellwise" begin
    +        main(; assembly = :cellwise)
    +    end
    +end
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example002_EdgeReaction.jl b/v1.19.1/module_examples/Example002_EdgeReaction.jl new file mode 100644 index 000000000..dd015ffd5 --- /dev/null +++ b/v1.19.1/module_examples/Example002_EdgeReaction.jl @@ -0,0 +1,204 @@ +#= + # 002: check edge reaction +=# + +module Example002_EdgeReaction + +using Printf +using VoronoiFVM +using ExtendableGrids +using ExtendableSparse +using GridVisualize +using LinearAlgebra +using SimplexGridFactory +using Triangulate + +function main(; nref = 0, dim = 2, Plotter = nothing, verbose = "and", case = :compare_max, assembly = :edgewise) + X = 0:(0.25 * 2.0^-nref):1 + i0::Int = 0 + i1::Int = 0 + if dim == 1 + grid = simplexgrid(X) + i0 = 1 + i1 = 2 + elseif dim == 2 + b = SimplexGridBuilder(; Generator = Triangulate) + p00 = point!(b, 0, 0) + p10 = point!(b, 1, 0) + p11 = point!(b, 1, 1) + p01 = point!(b, 0, 1) + pxx = point!(b, 0.3, 0.3) + + facetregion!(b, 1) + facet!(b, p00, p10) + facetregion!(b, 2) + facet!(b, p10, p11) + facetregion!(b, 3) + facet!(b, p11, p01) + facetregion!(b, 4) + facet!(b, p01, p00) + grid = simplexgrid(b; maxvolume = 0.01 * 4.0^(-nref)) + i0 = 1 + i1 = 3 + elseif dim == 3 + grid = simplexgrid(X, X, X) + i0 = 5 + i1 = 6 + end + + function storage!(y, u, node) + y[1] = u[1] + end + + function flux!(y, u, edge) + y[1] = u[1, 1] - u[1, 2] + end + + # Three ways to give a constant reaction term. As a consequence, + # these need to yield the same solution. + # 1: classical node reaction, multiplied by control volume size + function reaction!(y, u, node) + y[1] = -1 + end + + # 2: Edge reaction. Here we give it as a constant, and wie need + # to turn the multiplication with σ/h into a multiplication with the + # half diamond volume. + # + # Half diamond volume calculation + # /|\ + # / | \ + # / |s \ + # ------- + # h + # A=s*h/2d . Our formfactor: σ=s/h => A=σ*h^2 + # - make transfer area to volume + # + # τ=1/h v= s*h/2d = σ*h^2/2d + # + function edgereaction!(y, u, edge) + h = meas(edge) + y[1] = -1 * h^2 / (2 * dim) + end + + # + # 3: "Joule heat:" |∇ϕ|^2=1 after 3.17 in Bradji/Herbin + # Here we divide twice by "h" to get the constant squared gradient. + # The multiplication with dim in 3.17 compensates the division + # we had before + ϕ = grid[Coordinates][1, :] + + function edgereaction2!(y, u, edge) + ϕK = ϕ[edge.node[1]] + ϕL = ϕ[edge.node[2]] + y[1] = -(ϕK - ϕL) * (ϕK - ϕL) / 2 + end + + if case == :compare_max + function bcondition!(y, u, node) + boundary_dirichlet!(y, u, node; species = 1, region = 1, value = 0) + boundary_dirichlet!(y, u, node; species = 1, region = 2, value = 0) + boundary_dirichlet!(y, u, node; species = 1, region = 3, value = 0) + boundary_dirichlet!(y, u, node; species = 1, region = 4, value = 0) + boundary_dirichlet!(y, u, node; species = 1, region = 5, value = 0) + boundary_dirichlet!(y, u, node; species = 1, region = 6, value = 0) + end + + sys_noderea = VoronoiFVM.System(grid; bcondition = bcondition!, flux = flux!, + reaction = reaction!, storage = storage!, + species = [1], is_linear = true, assembly) + sys_edgerea = VoronoiFVM.System(grid; bcondition = bcondition!, flux = flux!, + edgereaction = edgereaction!, storage = storage!, + species = [1], is_linear = true, assembly) + sys_edgerea2 = VoronoiFVM.System(grid; bcondition = bcondition!, flux = flux!, + edgereaction = edgereaction2!, storage = storage!, + species = [1], is_linear = true, assembly) + + sol_noderea = solve(sys_noderea; verbose) + sol_edgerea = solve(sys_edgerea; verbose) + sol_edgerea2 = solve(sys_edgerea2; verbose) + + vis = GridVisualizer(; Plotter, layout = (2, 2)) + scalarplot!(vis[1, 1], grid, sol_noderea[1, :]; title = "node reaction", + colormap = :hot) + scalarplot!(vis[2, 1], grid, sol_edgerea[1, :]; title = "edgerea1", colormap = :hot) + scalarplot!(vis[1, 2], grid, sol_edgerea2[1, :]; title = "edgerea2", + colormap = :hot) + + reveal(vis) + return maximum.([sol_noderea, sol_edgerea, sol_edgerea2]) + end + + if case == :compare_flux + function bcondition2!(y, u, node) + boundary_dirichlet!(y, u, node; species = 1, region = i1, value = 0) + end + + sys2_noderea = VoronoiFVM.System(grid; bcondition = bcondition2!, flux = flux!, + reaction = reaction!, storage = storage!, + species = [1], is_linear = true) + sys2_edgerea = VoronoiFVM.System(grid; bcondition = bcondition2!, flux = flux!, + edgereaction = edgereaction!, storage = storage!, + species = [1], is_linear = true) + sys2_edgerea2 = VoronoiFVM.System(grid; bcondition = bcondition2!, flux = flux!, + edgereaction = edgereaction2!, storage = storage!, + species = [1], is_linear = true) + + sol2_noderea = solve(sys2_noderea; verbose) + sol2_edgerea = solve(sys2_edgerea; verbose) + sol2_edgerea2 = solve(sys2_edgerea2; verbose) + + tfac2_noderea = TestFunctionFactory(sys2_noderea) + tfc2_noderea = testfunction(tfac2_noderea, [i0], [i1]) + + tfac2_edgerea = TestFunctionFactory(sys2_edgerea) + tfc2_edgerea = testfunction(tfac2_edgerea, [i0], [i1]) + + tfac2_edgerea2 = TestFunctionFactory(sys2_edgerea2) + tfc2_edgerea2 = testfunction(tfac2_edgerea2, [i0], [i1]) + + vis = GridVisualizer(; Plotter, layout = (2, 2)) + scalarplot!(vis[1, 1], grid, sol2_noderea[1, :]; title = "node reaction", + colormap = :hot) + scalarplot!(vis[2, 1], grid, sol2_edgerea[1, :]; title = "edgerea1", + colormap = :hot) + scalarplot!(vis[1, 2], grid, sol2_edgerea2[1, :]; title = "edgerea2", + colormap = :hot) + reveal(vis) + + I_noderea = integrate(sys2_noderea, tfc2_noderea, sol2_noderea) + I_edgerea = integrate(sys2_edgerea, tfc2_edgerea, sol2_edgerea) + I_edgerea2 = integrate(sys2_edgerea2, tfc2_edgerea2, sol2_edgerea2) + + return I_noderea, I_edgerea, I_edgerea2 + end +end + +using Test +function runtests() + res = fill(false, 3) + for dim = 1:3 + result_max = main(; case = :compare_max, assembly = :cellwise) + result_flux = main(; case = :compare_flux, assembly = :cellwise) + res[dim] = isapprox(result_max[1], result_max[2]; atol = 1.0e-6) && + isapprox(result_max[1], result_max[3]; atol = 1.0e-3) && + isapprox(result_flux[1], result_flux[2]; atol = 1.0e-10) && + isapprox(result_flux[1], result_flux[3]; atol = 1.0e-10) + end + res1 = all(a -> a, res) + + res = fill(false, 3) + for dim = 1:3 + result_max = main(; case = :compare_max, assembly = :edgwise) + result_flux = main(; case = :compare_flux, assembly = :edgwise) + res[dim] = isapprox(result_max[1], result_max[2]; atol = 1.0e-6) && + isapprox(result_max[1], result_max[3]; atol = 1.0e-3) && + isapprox(result_flux[1], result_flux[2]; atol = 1.0e-10) && + isapprox(result_flux[1], result_flux[3]; atol = 1.0e-10) + end + res2 = all(a -> a, res) + + @test res1 && res2 +end + +end diff --git a/v1.19.1/module_examples/Example002_EdgeReaction/index.html b/v1.19.1/module_examples/Example002_EdgeReaction/index.html new file mode 100644 index 000000000..12999bfed --- /dev/null +++ b/v1.19.1/module_examples/Example002_EdgeReaction/index.html @@ -0,0 +1,172 @@ + +002: check edge reaction · VoronoiFVM.jl

    002: check edge reaction

    module Example002_EdgeReaction
    +
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +using ExtendableSparse
    +using GridVisualize
    +using LinearAlgebra
    +using SimplexGridFactory
    +using Triangulate
    +
    +function main(; nref = 0, dim = 2, Plotter = nothing, verbose = "and", case = :compare_max, assembly = :edgewise)
    +    X = 0:(0.25 * 2.0^-nref):1
    +    i0::Int = 0
    +    i1::Int = 0
    +    if dim == 1
    +        grid = simplexgrid(X)
    +        i0 = 1
    +        i1 = 2
    +    elseif dim == 2
    +        b = SimplexGridBuilder(; Generator = Triangulate)
    +        p00 = point!(b, 0, 0)
    +        p10 = point!(b, 1, 0)
    +        p11 = point!(b, 1, 1)
    +        p01 = point!(b, 0, 1)
    +        pxx = point!(b, 0.3, 0.3)
    +
    +        facetregion!(b, 1)
    +        facet!(b, p00, p10)
    +        facetregion!(b, 2)
    +        facet!(b, p10, p11)
    +        facetregion!(b, 3)
    +        facet!(b, p11, p01)
    +        facetregion!(b, 4)
    +        facet!(b, p01, p00)
    +        grid = simplexgrid(b; maxvolume = 0.01 * 4.0^(-nref))
    +        i0 = 1
    +        i1 = 3
    +    elseif dim == 3
    +        grid = simplexgrid(X, X, X)
    +        i0 = 5
    +        i1 = 6
    +    end
    +
    +    function storage!(y, u, node)
    +        y[1] = u[1]
    +    end
    +
    +    function flux!(y, u, edge)
    +        y[1] = u[1, 1] - u[1, 2]
    +    end

    Three ways to give a constant reaction term. As a consequence, these need to yield the same solution. 1: classical node reaction, multiplied by control volume size

        function reaction!(y, u, node)
    +        y[1] = -1
    +    end

    2: Edge reaction. Here we give it as a constant, and wie need to turn the multiplication with σ/h into a multiplication with the half diamond volume.

    Half diamond volume calculation /|
    / |
    / |s
    –––- h A=sh/2d . Our formfactor: σ=s/h => A=σh^2

    • make transfer area to volume

    τ=1/h v= sh/2d = σh^2/2d

        function edgereaction!(y, u, edge)
    +        h = meas(edge)
    +        y[1] = -1 * h^2 / (2 * dim)
    +    end

    3: "Joule heat:" |∇ϕ|^2=1 after 3.17 in Bradji/Herbin Here we divide twice by "h" to get the constant squared gradient. The multiplication with dim in 3.17 compensates the division we had before

        ϕ = grid[Coordinates][1, :]
    +
    +    function edgereaction2!(y, u, edge)
    +        ϕK = ϕ[edge.node[1]]
    +        ϕL = ϕ[edge.node[2]]
    +        y[1] = -(ϕK - ϕL) * (ϕK - ϕL) / 2
    +    end
    +
    +    if case == :compare_max
    +        function bcondition!(y, u, node)
    +            boundary_dirichlet!(y, u, node; species = 1, region = 1, value = 0)
    +            boundary_dirichlet!(y, u, node; species = 1, region = 2, value = 0)
    +            boundary_dirichlet!(y, u, node; species = 1, region = 3, value = 0)
    +            boundary_dirichlet!(y, u, node; species = 1, region = 4, value = 0)
    +            boundary_dirichlet!(y, u, node; species = 1, region = 5, value = 0)
    +            boundary_dirichlet!(y, u, node; species = 1, region = 6, value = 0)
    +        end
    +
    +        sys_noderea = VoronoiFVM.System(grid; bcondition = bcondition!, flux = flux!,
    +                                        reaction = reaction!, storage = storage!,
    +                                        species = [1], is_linear = true, assembly)
    +        sys_edgerea = VoronoiFVM.System(grid; bcondition = bcondition!, flux = flux!,
    +                                        edgereaction = edgereaction!, storage = storage!,
    +                                        species = [1], is_linear = true, assembly)
    +        sys_edgerea2 = VoronoiFVM.System(grid; bcondition = bcondition!, flux = flux!,
    +                                         edgereaction = edgereaction2!, storage = storage!,
    +                                         species = [1], is_linear = true, assembly)
    +
    +        sol_noderea = solve(sys_noderea; verbose)
    +        sol_edgerea = solve(sys_edgerea; verbose)
    +        sol_edgerea2 = solve(sys_edgerea2; verbose)
    +
    +        vis = GridVisualizer(; Plotter, layout = (2, 2))
    +        scalarplot!(vis[1, 1], grid, sol_noderea[1, :]; title = "node reaction",
    +                    colormap = :hot)
    +        scalarplot!(vis[2, 1], grid, sol_edgerea[1, :]; title = "edgerea1", colormap = :hot)
    +        scalarplot!(vis[1, 2], grid, sol_edgerea2[1, :]; title = "edgerea2",
    +                    colormap = :hot)
    +
    +        reveal(vis)
    +        return maximum.([sol_noderea, sol_edgerea, sol_edgerea2])
    +    end
    +
    +    if case == :compare_flux
    +        function bcondition2!(y, u, node)
    +            boundary_dirichlet!(y, u, node; species = 1, region = i1, value = 0)
    +        end
    +
    +        sys2_noderea = VoronoiFVM.System(grid; bcondition = bcondition2!, flux = flux!,
    +                                         reaction = reaction!, storage = storage!,
    +                                         species = [1], is_linear = true)
    +        sys2_edgerea = VoronoiFVM.System(grid; bcondition = bcondition2!, flux = flux!,
    +                                         edgereaction = edgereaction!, storage = storage!,
    +                                         species = [1], is_linear = true)
    +        sys2_edgerea2 = VoronoiFVM.System(grid; bcondition = bcondition2!, flux = flux!,
    +                                          edgereaction = edgereaction2!, storage = storage!,
    +                                          species = [1], is_linear = true)
    +
    +        sol2_noderea = solve(sys2_noderea; verbose)
    +        sol2_edgerea = solve(sys2_edgerea; verbose)
    +        sol2_edgerea2 = solve(sys2_edgerea2; verbose)
    +
    +        tfac2_noderea = TestFunctionFactory(sys2_noderea)
    +        tfc2_noderea = testfunction(tfac2_noderea, [i0], [i1])
    +
    +        tfac2_edgerea = TestFunctionFactory(sys2_edgerea)
    +        tfc2_edgerea = testfunction(tfac2_edgerea, [i0], [i1])
    +
    +        tfac2_edgerea2 = TestFunctionFactory(sys2_edgerea2)
    +        tfc2_edgerea2 = testfunction(tfac2_edgerea2, [i0], [i1])
    +
    +        vis = GridVisualizer(; Plotter, layout = (2, 2))
    +        scalarplot!(vis[1, 1], grid, sol2_noderea[1, :]; title = "node reaction",
    +                    colormap = :hot)
    +        scalarplot!(vis[2, 1], grid, sol2_edgerea[1, :]; title = "edgerea1",
    +                    colormap = :hot)
    +        scalarplot!(vis[1, 2], grid, sol2_edgerea2[1, :]; title = "edgerea2",
    +                    colormap = :hot)
    +        reveal(vis)
    +
    +        I_noderea = integrate(sys2_noderea, tfc2_noderea, sol2_noderea)
    +        I_edgerea = integrate(sys2_edgerea, tfc2_edgerea, sol2_edgerea)
    +        I_edgerea2 = integrate(sys2_edgerea2, tfc2_edgerea2, sol2_edgerea2)
    +
    +        return I_noderea, I_edgerea, I_edgerea2
    +    end
    +end
    +
    +using Test
    +function runtests()
    +    res = fill(false, 3)
    +    for dim = 1:3
    +        result_max = main(; case = :compare_max, assembly = :cellwise)
    +        result_flux = main(; case = :compare_flux, assembly = :cellwise)
    +        res[dim] = isapprox(result_max[1], result_max[2]; atol = 1.0e-6) &&
    +                   isapprox(result_max[1], result_max[3]; atol = 1.0e-3) &&
    +                   isapprox(result_flux[1], result_flux[2]; atol = 1.0e-10) &&
    +                   isapprox(result_flux[1], result_flux[3]; atol = 1.0e-10)
    +    end
    +    res1 = all(a -> a, res)
    +
    +    res = fill(false, 3)
    +    for dim = 1:3
    +        result_max = main(; case = :compare_max, assembly = :edgwise)
    +        result_flux = main(; case = :compare_flux, assembly = :edgwise)
    +        res[dim] = isapprox(result_max[1], result_max[2]; atol = 1.0e-6) &&
    +                   isapprox(result_max[1], result_max[3]; atol = 1.0e-3) &&
    +                   isapprox(result_flux[1], result_flux[2]; atol = 1.0e-10) &&
    +                   isapprox(result_flux[1], result_flux[3]; atol = 1.0e-10)
    +    end
    +    res2 = all(a -> a, res)
    +
    +    @test res1 && res2
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example101_Laplace1D.jl b/v1.19.1/module_examples/Example101_Laplace1D.jl new file mode 100644 index 000000000..ba6dfdcb1 --- /dev/null +++ b/v1.19.1/module_examples/Example101_Laplace1D.jl @@ -0,0 +1,128 @@ +#= + +# 101: 1D Laplace equation +([source code](@__SOURCE_URL__)) + +Let $\Omega=(\gamma_1,\gamma_2)$ with $\gamma_1=0$, $\gamma_2=1$. +This is the simplest second order boundary value problem (BVP) +for a partial differential equation (PDE): + +```math +-\Delta u =0\\ +u(\gamma_1)=g_1\\ +u(\gamma_2)=g_2. +``` + +We replace the Dirichlet boundary condition by a Robin boundary +condition with a penalty parameter $\frac{1}{\varepsilon}$: + +```math +\nabla u(\gamma_1) + \frac{1}{\varepsilon}(u(\gamma_1)-g_1)=0 \\ +-\nabla u(\gamma_2) + \frac{1}{\varepsilon}(u(\gamma_2)-g_2) +=0 +``` + +This penalty method for the implementation of Dirichlet +boundary conditions is used throughout VoronoiFVM. + +In order to discretize it, we choose collocation points +$\gamma_1=x_1 < x_2 < \dots < x_n=\gamma_2$. + +For instance, we can choose 6 collocation points in $(0,1)$: +From these, we create a discretization grid structure +for working with the method. + +This implicitly creates a number of control volumes $\omega_k $ around each +discretization point $x_k$: Let +$\sigma_{k,k+1}=\frac{x_k+x_{k+1}}{2}$. Then $\omega_1=(\gamma_1,\sigma_{1,2})$, +$\omega_k= (\sigma_{k-1,k}, \sigma_{k,k+1})$ for $k=2\dots n-1$, $\omega_{n}=(\sigma_{n-1,n},\gamma_2)$. + +``` + x1 x2 x3 x4 x5 x6 + o-----o-----o-----o-----o-----o + |--|-----|-----|-----|-----|--| + ω1 ω2 ω3 ω4 ω5 ω6 +``` + +For each $\omega_k$, we integrate the equation + +```math +\begin{aligned} +0&=\int_{\omega_k} -\Delta u d\omega= -\int_{\partial \omega_k} \nabla u ds\\ +&= \begin{cases} +u'(\sigma_{1,2}) - u'(0)& k=1\\ +u'(\sigma_{k,k+1}) - u'(\sigma_{k-1,k}) & 1 +101: 1D Laplace equation · VoronoiFVM.jl

    101: 1D Laplace equation

    (source code)

    Let $\Omega=(\gamma_1,\gamma_2)$ with $\gamma_1=0$, $\gamma_2=1$. This is the simplest second order boundary value problem (BVP) for a partial differential equation (PDE):

    \[-\Delta u =0\\ +u(\gamma_1)=g_1\\ +u(\gamma_2)=g_2.\]

    We replace the Dirichlet boundary condition by a Robin boundary condition with a penalty parameter $\frac{1}{\varepsilon}$:

    \[\nabla u(\gamma_1) + \frac{1}{\varepsilon}(u(\gamma_1)-g_1)=0 \\ +-\nabla u(\gamma_2) + \frac{1}{\varepsilon}(u(\gamma_2)-g_2) +=0\]

    This penalty method for the implementation of Dirichlet boundary conditions is used throughout VoronoiFVM.

    In order to discretize it, we choose collocation points $\gamma_1=x_1 < x_2 < \dots < x_n=\gamma_2$.

    For instance, we can choose 6 collocation points in $(0,1)$: From these, we create a discretization grid structure for working with the method.

    This implicitly creates a number of control volumes $\omega_k $ around each discretization point $x_k$: Let $\sigma_{k,k+1}=\frac{x_k+x_{k+1}}{2}$. Then $\omega_1=(\gamma_1,\sigma_{1,2})$, $\omega_k= (\sigma_{k-1,k}, \sigma_{k,k+1})$ for $k=2\dots n-1$, $\omega_{n}=(\sigma_{n-1,n},\gamma_2)$.

     x1    x2    x3    x4    x5    x6
    + o-----o-----o-----o-----o-----o
    + |--|-----|-----|-----|-----|--|
    +  ω1  ω2     ω3    ω4    ω5  ω6

    For each $\omega_k$, we integrate the equation

    \[\begin{aligned} +0&=\int_{\omega_k} -\Delta u d\omega= -\int_{\partial \omega_k} \nabla u ds\\ +&= \begin{cases} +u'(\sigma_{1,2}) - u'(0)& k=1\\ +u'(\sigma_{k,k+1}) - u'(\sigma_{k-1,k}) & 1<k<n\\ +u'(1)- u'(\sigma_{n,n+1})&k=n +\end{cases}\\ +&\approx \begin{cases} +\frac{1}{x_2-x_1} g(u_1,u_2) + \frac{1}{\varepsilon}(u_1-0)& k=1\\ +\frac{1}{x_k-x_{k-1}}g(u_k,u_{k-1}) -\frac{1}{x_{k+1}-x_{k}}g(u_{k+1},u_{k}) & 1<k<n\\ +\frac{1}{\varepsilon}(u_n-1)+ \frac{1}{x_n-x_{n-1}} g(u_{n},u_{n-1})&k=n +\end{cases} +\end{aligned}\]

    In the last equation, we wrote $u_k=u(x_k)$ and $g(u_k,u_l)=u_k-u_l$. For the interior interfaces between control volumes, we replaced $u'$ by a difference quotient. In the boundary control volumes, we replaced $u'$ by the boundary conditions.

    In the example below, we fix a number of species and write a Julia function describing $g$, we create a physics record, and a finite volume system with one unknown species and a dense matrix to describe it's degrees of freedom (the matrix used to calculate the solution is sparse). We give the species the number 1 and enable it for grid region number one 1. Then, we set boundary conditions for species 1 at $\gamma_1, \gamma_2$.

    We create a zero initial value and a solution vector and initialize them.

    With these data, we solve the system.

    We wrap this example and all later ones into a module structure. This allows to load all of them at once into the REPL without name clashes. We shouldn't forget the corresponding end statement.

    module Example101_Laplace1D
    +
    +using VoronoiFVM
    +
    +function main()
    +    ispec = 1    ## Index of species we are working with
    +
    +    # Flux function which describes the flux
    +    # between neighboring control volumes
    +    function flux!(f, u, edge)
    +        f[1] = u[1, 1] - u[1, 2]
    +    end
    +
    +    function bcond!(args...)
    +        boundary_dirichlet!(args...; region = 1, value = 0)
    +        boundary_dirichlet!(args...; region = 2, value = 1)
    +    end
    +
    +    # Create a one dimensional discretization grid
    +    # Each grid cell belongs to a region marked by a region number
    +    # By default, there is only one region numbered with 1
    +    grid = VoronoiFVM.Grid(0:0.2:1)
    +
    +    # Create a finite volume system
    +    sys = VoronoiFVM.System(grid; flux = flux!, breaction = bcond!, species = ispec)
    +
    +    # Solve stationary problem
    +    solution = solve(sys; inival = 0)
    +
    +    # Return test value
    +    return sum(solution)
    +end
    +
    +using Test
    +function runtests()
    +    @test main() ≈ 3.0
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example102_StationaryConvectionDiffusion1D.jl b/v1.19.1/module_examples/Example102_StationaryConvectionDiffusion1D.jl new file mode 100644 index 000000000..ddd214266 --- /dev/null +++ b/v1.19.1/module_examples/Example102_StationaryConvectionDiffusion1D.jl @@ -0,0 +1,133 @@ +#= + +# 102: 1D Stationary convection-diffusion equation +([source code](@__SOURCE_URL__)) + +Solve the equation + +```math +-\nabla ( D \nabla u - v u) = 0 +``` +in $\Omega=(0,1)$ with boundary condition $u(0)=0$ and $u(1)=1$. +$v$ could be e.g. the velocity +of a moving medium or the gradient of an electric field. + +This is a convection dominant second order boundary value problem which obeys +a local and a global maximum principle: +the solution which is bounded by the values at the boundary and has no local extrema in the +interior. +If $v$ is large compared to $D$, a boundary layer is observed. + +The maximum principle of the solution can only be guaranteed it the discretization is +performed accordingly: the flux function must monotonically increase in the first argument +and monotonically decrease in the second argument. + +The example describes three possible ways to define the flux function and demonstrates +the impact on the qualitative properties of the solution. + +=# + +module Example102_StationaryConvectionDiffusion1D +using Printf +using VoronoiFVM +using ExtendableGrids +using GridVisualize + +## Central difference flux. The velocity term is discretized using the +## average of the solution in the endpoints of the grid. If the local Peclet +## number v*h/D>1, the monotonicity property is lost. Grid refinement +## can fix this situation by decreasing $h$. + +function central_flux!(f, u, edge, data) + f_diff = data.D * (u[1, 1] - u[1, 2]) + vh = project(edge, data.v) + f[1] = f_diff + vh * (u[1, 1] + u[1, 2]) / 2 +end + +## The simple upwind flux corrects the monotonicity properties essentially +## via brute force and loses one order of convergence for small $h$ compared +## to the central flux. + +function upwind_flux!(f, u, edge, data) + fdiff = data.D * (u[1] - u[1, 2]) + vh = project(edge, data.v) + if vh > 0 + f[1] = fdiff + vh * u[1, 1] + else + f[1] = fdiff + vh * u[1, 2] + end +end + +## The exponential fitting flux has the proper monotonicity properties and +## kind of interpolates in a clever way between central +## and upwind flux. It can be derived by solving the two-point boundary value problem +## at the grid interval analytically. + +## Bernoulli function used in the exponential fitting discretization +function bernoulli(x) + if abs(x) < nextfloat(eps(typeof(x))) + return 1 + end + return x / (exp(x) - 1) +end + +function exponential_flux!(f, u, edge, data) + vh = project(edge, data.v) + Bplus = data.D * bernoulli(vh / data.D) + Bminus = data.D * bernoulli(-vh / data.D) + f[1] = Bminus * u[1, 1] - Bplus * u[1, 2] +end + +function calculate(grid, data, flux, verbose) + sys = VoronoiFVM.System(grid, VoronoiFVM.Physics(; flux = flux, data = data)) + + ## Add species 1 to region 1 + enable_species!(sys, 1, [1]) + + ## Set boundary conditions + boundary_dirichlet!(sys, 1, 1, 0.0) + boundary_dirichlet!(sys, 1, 2, 1.0) + + ## Create a solution array + inival = unknowns(sys; inival = 0.5) + solution = unknowns(sys) + + ## Create solver control info + control = VoronoiFVM.NewtonControl() + control.verbose = verbose + + ## Stationary solution of the problem + solution = solve(sys; inival, verbose) + return solution +end + +function main(; n = 10, Plotter = nothing, verbose = false, D = 0.01, v = 1.0) + + ## Create a one-dimensional discretization + h = 1.0 / convert(Float64, n) + grid = VoronoiFVM.Grid(collect(0:h:1)) + + data = (v = [v], D = D) + + # Calculate three stationary solutions with different ways to calculate flux + solution_exponential = calculate(grid, data, exponential_flux!, verbose) + solution_upwind = calculate(grid, data, upwind_flux!, verbose) + solution_central = calculate(grid, data, central_flux!, verbose) + + # Visualize solutions using GridVisualize.jl + p = GridVisualizer(; Plotter = Plotter, layout = (3, 1)) + scalarplot!(p[1, 1], grid, solution_exponential[1, :]; title = "exponential") + scalarplot!(p[2, 1], grid, solution_upwind[1, :]; title = "upwind") + scalarplot!(p[3, 1], grid, solution_central[1, :]; title = "centered", show = true) + + ## Return test value + return sum(solution_exponential) + sum(solution_upwind) + sum(solution_central) +end + +using Test +function runtests() + testval = 2.523569744561089 + @test main() ≈ testval +end + +end diff --git a/v1.19.1/module_examples/Example102_StationaryConvectionDiffusion1D/index.html b/v1.19.1/module_examples/Example102_StationaryConvectionDiffusion1D/index.html new file mode 100644 index 000000000..0871e4af0 --- /dev/null +++ b/v1.19.1/module_examples/Example102_StationaryConvectionDiffusion1D/index.html @@ -0,0 +1,99 @@ + +102: 1D Stationary convection-diffusion equation · VoronoiFVM.jl

    102: 1D Stationary convection-diffusion equation

    (source code)

    Solve the equation

    \[-\nabla ( D \nabla u - v u) = 0\]

    in $\Omega=(0,1)$ with boundary condition $u(0)=0$ and $u(1)=1$. $v$ could be e.g. the velocity of a moving medium or the gradient of an electric field.

    This is a convection dominant second order boundary value problem which obeys a local and a global maximum principle: the solution which is bounded by the values at the boundary and has no local extrema in the interior. If $v$ is large compared to $D$, a boundary layer is observed.

    The maximum principle of the solution can only be guaranteed it the discretization is performed accordingly: the flux function must monotonically increase in the first argument and monotonically decrease in the second argument.

    The example describes three possible ways to define the flux function and demonstrates the impact on the qualitative properties of the solution.

    module Example102_StationaryConvectionDiffusion1D
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +
    +# Central difference flux. The velocity term is discretized using the
    +# average of the solution in the endpoints of the grid. If the local Peclet
    +# number v*h/D>1, the monotonicity property is lost.  Grid refinement
    +# can fix this situation by decreasing $h$.
    +
    +function central_flux!(f, u, edge, data)
    +    f_diff = data.D * (u[1, 1] - u[1, 2])
    +    vh = project(edge, data.v)
    +    f[1] = f_diff + vh * (u[1, 1] + u[1, 2]) / 2
    +end
    +
    +# The simple upwind flux corrects the monotonicity properties essentially
    +# via brute force and loses one order of convergence for small $h$ compared
    +# to the central flux.
    +
    +function upwind_flux!(f, u, edge, data)
    +    fdiff = data.D * (u[1] - u[1, 2])
    +    vh = project(edge, data.v)
    +    if vh > 0
    +        f[1] = fdiff + vh * u[1, 1]
    +    else
    +        f[1] = fdiff + vh * u[1, 2]
    +    end
    +end
    +
    +# The exponential fitting flux has the proper monotonicity properties and
    +# kind of interpolates in a clever way between central
    +# and upwind flux. It can be derived by solving the two-point boundary value problem
    +# at the grid interval analytically.
    +
    +# Bernoulli function used in the exponential fitting discretization
    +function bernoulli(x)
    +    if abs(x) < nextfloat(eps(typeof(x)))
    +        return 1
    +    end
    +    return x / (exp(x) - 1)
    +end
    +
    +function exponential_flux!(f, u, edge, data)
    +    vh = project(edge, data.v)
    +    Bplus = data.D * bernoulli(vh / data.D)
    +    Bminus = data.D * bernoulli(-vh / data.D)
    +    f[1] = Bminus * u[1, 1] - Bplus * u[1, 2]
    +end
    +
    +function calculate(grid, data, flux, verbose)
    +    sys = VoronoiFVM.System(grid, VoronoiFVM.Physics(; flux = flux, data = data))
    +
    +    # Add species 1 to region 1
    +    enable_species!(sys, 1, [1])
    +
    +    # Set boundary conditions
    +    boundary_dirichlet!(sys, 1, 1, 0.0)
    +    boundary_dirichlet!(sys, 1, 2, 1.0)
    +
    +    # Create a solution array
    +    inival = unknowns(sys; inival = 0.5)
    +    solution = unknowns(sys)
    +
    +    # Create solver control info
    +    control = VoronoiFVM.NewtonControl()
    +    control.verbose = verbose
    +
    +    # Stationary solution of the problem
    +    solution = solve(sys; inival, verbose)
    +    return solution
    +end
    +
    +function main(; n = 10, Plotter = nothing, verbose = false, D = 0.01, v = 1.0)
    +
    +    # Create a one-dimensional discretization
    +    h = 1.0 / convert(Float64, n)
    +    grid = VoronoiFVM.Grid(collect(0:h:1))
    +
    +    data = (v = [v], D = D)

    Calculate three stationary solutions with different ways to calculate flux

        solution_exponential = calculate(grid, data, exponential_flux!, verbose)
    +    solution_upwind = calculate(grid, data, upwind_flux!, verbose)
    +    solution_central = calculate(grid, data, central_flux!, verbose)

    Visualize solutions using GridVisualize.jl

        p = GridVisualizer(; Plotter = Plotter, layout = (3, 1))
    +    scalarplot!(p[1, 1], grid, solution_exponential[1, :]; title = "exponential")
    +    scalarplot!(p[2, 1], grid, solution_upwind[1, :]; title = "upwind")
    +    scalarplot!(p[3, 1], grid, solution_central[1, :]; title = "centered", show = true)
    +
    +    # Return test value
    +    return sum(solution_exponential) + sum(solution_upwind) + sum(solution_central)
    +end
    +
    +using Test
    +function runtests()
    +    testval = 2.523569744561089
    +    @test main() ≈ testval
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example103_ConvectionDiffusion1D.jl b/v1.19.1/module_examples/Example103_ConvectionDiffusion1D.jl new file mode 100644 index 000000000..ae31d8c3d --- /dev/null +++ b/v1.19.1/module_examples/Example103_ConvectionDiffusion1D.jl @@ -0,0 +1,86 @@ +#= + +# 103: 1D Convection-diffusion equation +([source code](@__SOURCE_URL__)) + +Solve the equation + +```math +\partial_t u -\nabla ( D \nabla u - v u) = 0 +``` +in $\Omega=(0,1)$ with homogeneous Neumann boundary condition +at $x=0$ and outflow boundary condition at $x=1$. +=# + +module Example103_ConvectionDiffusion1D +using Printf +using VoronoiFVM +using ExtendableGrids +using GridVisualize + +## Bernoulli function used in the exponential fitting discretization +function bernoulli(x) + if abs(x) < nextfloat(eps(typeof(x))) + return 1 + end + return x / (exp(x) - 1) +end + +function exponential_flux!(f, u, edge, data) + vh = project(edge, data.v) + Bplus = data.D * bernoulli(vh / data.D) + Bminus = data.D * bernoulli(-vh / data.D) + f[1] = Bminus * u[1, 1] - Bplus * u[1, 2] +end + +function outflow!(f, u, node, data) + if node.region == 2 + f[1] = data.v[1] * u[1] + end +end + +function main(; n = 10, Plotter = nothing, D = 0.01, v = 1.0, tend = 100) + + ## Create a one-dimensional discretization + h = 1.0 / n + grid = VoronoiFVM.Grid(0:h:1) + + data = (v = [v], D = D) + + sys = VoronoiFVM.System(grid, + VoronoiFVM.Physics(; flux = exponential_flux!, data = data, + breaction = outflow!)) + + ## Add species 1 to region 1 + enable_species!(sys, 1, [1]) + + ## Set boundary conditions + boundary_neumann!(sys, 1, 1, 0.0) + + ## Create a solution array + inival = unknowns(sys) + inival[1, :] .= map(x -> 1 - 2x, grid) + + ## Transient solution of the problem + control = VoronoiFVM.NewtonControl() + control.Δt = 0.01 * h + control.Δt_min = 0.01 * h + control.Δt_max = 0.1 * tend + tsol = solve(sys; inival, times = [0, tend], control) + + vis = GridVisualizer(; Plotter = Plotter) + for i = 1:length(tsol.t) + scalarplot!(vis[1, 1], grid, tsol[1, :, i]; flimits = (0, 1), + title = "t=$(tsol.t[i])", show = true) + sleep(0.01) + end + tsol +end + +using Test +function runtests() + tsol = main() + @test maximum(tsol) <= 1.0 && maximum(tsol[end]) < 1.0e-20 +end + +end diff --git a/v1.19.1/module_examples/Example103_ConvectionDiffusion1D/index.html b/v1.19.1/module_examples/Example103_ConvectionDiffusion1D/index.html new file mode 100644 index 000000000..8d84ba4c2 --- /dev/null +++ b/v1.19.1/module_examples/Example103_ConvectionDiffusion1D/index.html @@ -0,0 +1,73 @@ + +103: 1D Convection-diffusion equation · VoronoiFVM.jl

    103: 1D Convection-diffusion equation

    (source code)

    Solve the equation

    \[\partial_t u -\nabla ( D \nabla u - v u) = 0\]

    in $\Omega=(0,1)$ with homogeneous Neumann boundary condition at $x=0$ and outflow boundary condition at $x=1$.

    module Example103_ConvectionDiffusion1D
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +
    +# Bernoulli function used in the exponential fitting discretization
    +function bernoulli(x)
    +    if abs(x) < nextfloat(eps(typeof(x)))
    +        return 1
    +    end
    +    return x / (exp(x) - 1)
    +end
    +
    +function exponential_flux!(f, u, edge, data)
    +    vh = project(edge, data.v)
    +    Bplus = data.D * bernoulli(vh / data.D)
    +    Bminus = data.D * bernoulli(-vh / data.D)
    +    f[1] = Bminus * u[1, 1] - Bplus * u[1, 2]
    +end
    +
    +function outflow!(f, u, node, data)
    +    if node.region == 2
    +        f[1] = data.v[1] * u[1]
    +    end
    +end
    +
    +function main(; n = 10, Plotter = nothing, D = 0.01, v = 1.0, tend = 100)
    +
    +    # Create a one-dimensional discretization
    +    h = 1.0 / n
    +    grid = VoronoiFVM.Grid(0:h:1)
    +
    +    data = (v = [v], D = D)
    +
    +    sys = VoronoiFVM.System(grid,
    +                            VoronoiFVM.Physics(; flux = exponential_flux!, data = data,
    +                                               breaction = outflow!))
    +
    +    # Add species 1 to region 1
    +    enable_species!(sys, 1, [1])
    +
    +    # Set boundary conditions
    +    boundary_neumann!(sys, 1, 1, 0.0)
    +
    +    # Create a solution array
    +    inival = unknowns(sys)
    +    inival[1, :] .= map(x -> 1 - 2x, grid)
    +
    +    # Transient solution of the problem
    +    control = VoronoiFVM.NewtonControl()
    +    control.Δt = 0.01 * h
    +    control.Δt_min = 0.01 * h
    +    control.Δt_max = 0.1 * tend
    +    tsol = solve(sys; inival, times = [0, tend], control)
    +
    +    vis = GridVisualizer(; Plotter = Plotter)
    +    for i = 1:length(tsol.t)
    +        scalarplot!(vis[1, 1], grid, tsol[1, :, i]; flimits = (0, 1),
    +                    title = "t=$(tsol.t[i])", show = true)
    +        sleep(0.01)
    +    end
    +    tsol
    +end
    +
    +using Test
    +function runtests()
    +    tsol = main()
    +    @test maximum(tsol) <= 1.0 && maximum(tsol[end]) < 1.0e-20
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example105_NonlinearPoisson1D.jl b/v1.19.1/module_examples/Example105_NonlinearPoisson1D.jl new file mode 100644 index 000000000..ebd4e3bba --- /dev/null +++ b/v1.19.1/module_examples/Example105_NonlinearPoisson1D.jl @@ -0,0 +1,98 @@ +#= +# 105: 1D Nonlinear Poisson equation +([source code](@__SOURCE_URL__)) + +Solve the nonlinear Poisson equation + +```math +-\nabla \varepsilon \nabla u + e^{u}-e^{-u} = f +``` +in $\Omega=(0,1)$ with boundary condition $u(0)=0$ and $u(1)=1$ with +```math +f(x)= + \begin{cases} + 1&,x>0.5\\ + -1&, x<0.5 + \end{cases}. +``` + +This stationary problem is an example of a nonlinear Poisson equation or Poisson-Boltzmann equation. +Such equation occur e.g. in simulations of electrochemical systems and semiconductor devices. + +=# + +module Example105_NonlinearPoisson1D +using Printf +using VoronoiFVM +using ExtendableGrids +using GridVisualize + +function main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise) + + ## Create a one-dimensional discretization + h = 1.0 / convert(Float64, n) + grid = VoronoiFVM.Grid(collect(0:h:1)) + + ## A parameter which is "passed" to the flux function via scope + ϵ = 1.0e-3 + + ## Flux function which describes the flux + ## between neighboring control volumes + function flux!(f, u, edge) + f[1] = ϵ * (u[1, 1] - u[1, 2]) + end + + ## Source term + function source!(f, node) + if node[1] <= 0.5 + f[1] = 1 + else + f[1] = -1 + end + end + + ## Reaction term + function reaction!(f, u, node) + f[1] = exp(u[1]) - exp(-u[1]) + end + + ## Create a physics structure + physics = VoronoiFVM.Physics(; flux = flux!, + source = source!, + reaction = reaction!) + + ## Create a finite volume system - either + ## in the dense or the sparse version. + ## The difference is in the way the solution object + ## is stored - as dense or as sparse matrix + + sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly) + + ## Add species 1 to region 1 + enable_species!(sys, 1, [1]) + + ## Set boundary conditions + boundary_dirichlet!(sys, 1, 1, 0.0) + boundary_dirichlet!(sys, 1, 2, 1.0) + + ## Create a solution array + inival = unknowns(sys; inival = 0.5) + + ## Stationary solution of the problem + solution = solve(sys; inival, verbose) + + scalarplot(grid, solution[1, :]; title = "Nonlinear Poisson", Plotter = Plotter) + + return sum(solution) +end + +using Test +function runtests() + testval = 1.5247901344230088 + @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval && + main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval +end + +end diff --git a/v1.19.1/module_examples/Example105_NonlinearPoisson1D/index.html b/v1.19.1/module_examples/Example105_NonlinearPoisson1D/index.html new file mode 100644 index 000000000..effa35fee --- /dev/null +++ b/v1.19.1/module_examples/Example105_NonlinearPoisson1D/index.html @@ -0,0 +1,80 @@ + +105: 1D Nonlinear Poisson equation · VoronoiFVM.jl

    105: 1D Nonlinear Poisson equation

    (source code)

    Solve the nonlinear Poisson equation

    \[-\nabla \varepsilon \nabla u + e^{u}-e^{-u} = f\]

    in $\Omega=(0,1)$ with boundary condition $u(0)=0$ and $u(1)=1$ with

    \[f(x)= + \begin{cases} + 1&,x>0.5\\ + -1&, x<0.5 + \end{cases}.\]

    This stationary problem is an example of a nonlinear Poisson equation or Poisson-Boltzmann equation. Such equation occur e.g. in simulations of electrochemical systems and semiconductor devices.

    module Example105_NonlinearPoisson1D
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +
    +function main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise)
    +
    +    # Create a one-dimensional discretization
    +    h = 1.0 / convert(Float64, n)
    +    grid = VoronoiFVM.Grid(collect(0:h:1))
    +
    +    # A parameter which is "passed" to the flux function via scope
    +    ϵ = 1.0e-3
    +
    +    # Flux function which describes the flux
    +    # between neighboring control volumes
    +    function flux!(f, u, edge)
    +        f[1] = ϵ * (u[1, 1] - u[1, 2])
    +    end
    +
    +    # Source term
    +    function source!(f, node)
    +        if node[1] <= 0.5
    +            f[1] = 1
    +        else
    +            f[1] = -1
    +        end
    +    end
    +
    +    # Reaction term
    +    function reaction!(f, u, node)
    +        f[1] = exp(u[1]) - exp(-u[1])
    +    end
    +
    +    # Create a physics structure
    +    physics = VoronoiFVM.Physics(; flux = flux!,
    +                                 source = source!,
    +                                 reaction = reaction!)
    +
    +    # Create a finite volume system - either
    +    # in the dense or  the sparse version.
    +    # The difference is in the way the solution object
    +    # is stored - as dense or as sparse matrix
    +
    +    sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly)
    +
    +    # Add species 1 to region 1
    +    enable_species!(sys, 1, [1])
    +
    +    # Set boundary conditions
    +    boundary_dirichlet!(sys, 1, 1, 0.0)
    +    boundary_dirichlet!(sys, 1, 2, 1.0)
    +
    +    # Create a solution array
    +    inival = unknowns(sys; inival = 0.5)
    +
    +    # Stationary solution of the problem
    +    solution = solve(sys; inival, verbose)
    +
    +    scalarplot(grid, solution[1, :]; title = "Nonlinear Poisson", Plotter = Plotter)
    +
    +    return sum(solution)
    +end
    +
    +using Test
    +function runtests()
    +    testval = 1.5247901344230088
    +    @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example106_NonlinearDiffusion1D.jl b/v1.19.1/module_examples/Example106_NonlinearDiffusion1D.jl new file mode 100644 index 000000000..2223858c5 --- /dev/null +++ b/v1.19.1/module_examples/Example106_NonlinearDiffusion1D.jl @@ -0,0 +1,110 @@ +#= + +# 106: 1D Nonlinear Diffusion equation +([source code](@__SOURCE_URL__)) + +Solve the nonlinear diffusion equation + +```math +\partial_t u -\Delta u^m = 0 +``` +in $\Omega=(-1,1)$ with homogeneous Neumann boundary conditions using the implicit Euler method. + +This equation is also called "porous medium equation". +The Barenblatt solution is an exact solution of this problem which for m>1 has a finite support. +We initialize this problem with the exact solution for $t=t_0=0.001$. + +(see Barenblatt, G. I. "On nonsteady motions of gas and fluid in porous medium." Appl. Math. and Mech.(PMM) 16.1 (1952): 67-78.) +=# + +module Example106_NonlinearDiffusion1D +using Printf +using VoronoiFVM +using ExtendableGrids +using GridVisualize + +function barenblatt(x, t, m) + tx = t^(-1.0 / (m + 1.0)) + xx = x * tx + xx = xx * xx + xx = 1 - xx * (m - 1) / (2.0 * m * (m + 1)) + if xx < 0.0 + xx = 0.0 + end + return tx * xx^(1.0 / (m - 1.0)) +end + +function main(; n = 20, m = 2, Plotter = nothing, verbose = false, + unknown_storage = :sparse, tend = 0.01, tstep = 0.0001, assembly = :edgewise) + + ## Create a one-dimensional discretization + h = 1.0 / convert(Float64, n / 2) + X = collect(-1:h:1) + grid = VoronoiFVM.Grid(X) + + ## Flux function which describes the flux + ## between neighboring control volumes + function flux!(f, u, edge) + f[1] = u[1, 1]^m - u[1, 2]^m + end + + ## Storage term + function storage!(f, u, node) + f[1] = u[1] + end + + ## Create a physics structure + physics = VoronoiFVM.Physics(; flux = flux!, + storage = storage!) + + ## Create a finite volume system - either + ## in the dense or the sparse version. + ## The difference is in the way the solution object + ## is stored - as dense or as sparse matrix + sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly) + + ## Add species 1 to region 1 + enable_species!(sys, 1, [1]) + + ## Create a solution array + inival = unknowns(sys) + t0 = 0.001 + + ## Broadcast the initial value + inival[1, :] .= map(x -> barenblatt(x, t0, m), X) + + ## Create solver control info for constant time step size + control = VoronoiFVM.NewtonControl() + control.verbose = verbose + control.Δt_min = tstep + control.Δt_max = tstep + control.Δt = tstep + control.Δu_opt = 1 + + tsol = solve(sys; inival, times = [t0, tend], control) + + p = GridVisualizer(; Plotter = Plotter, layout = (1, 1), fast = true) + for i = 1:length(tsol) + time = tsol.t[i] + scalarplot!(p[1, 1], grid, tsol[1, :, i]; title = @sprintf("t=%.3g", time), + color = :red, label = "numerical", + markershape = :circle, markevery = 1) + scalarplot!(p[1, 1], grid, map(x -> barenblatt(x, time, m), grid); clear = false, + color = :green, + label = "exact", markershape = :none) + reveal(p) + sleep(1.0e-2) + end + return sum(tsol[end]) +end + +using Test +function runtests() + testval = 46.66666666647518 + @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval && + main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval +end + +end diff --git a/v1.19.1/module_examples/Example106_NonlinearDiffusion1D/index.html b/v1.19.1/module_examples/Example106_NonlinearDiffusion1D/index.html new file mode 100644 index 000000000..cf511ef1a --- /dev/null +++ b/v1.19.1/module_examples/Example106_NonlinearDiffusion1D/index.html @@ -0,0 +1,92 @@ + +106: 1D Nonlinear Diffusion equation · VoronoiFVM.jl

    106: 1D Nonlinear Diffusion equation

    (source code)

    Solve the nonlinear diffusion equation

    \[\partial_t u -\Delta u^m = 0\]

    in $\Omega=(-1,1)$ with homogeneous Neumann boundary conditions using the implicit Euler method.

    This equation is also called "porous medium equation". The Barenblatt solution is an exact solution of this problem which for m>1 has a finite support. We initialize this problem with the exact solution for $t=t_0=0.001$.

    (see Barenblatt, G. I. "On nonsteady motions of gas and fluid in porous medium." Appl. Math. and Mech.(PMM) 16.1 (1952): 67-78.)

    module Example106_NonlinearDiffusion1D
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +
    +function barenblatt(x, t, m)
    +    tx = t^(-1.0 / (m + 1.0))
    +    xx = x * tx
    +    xx = xx * xx
    +    xx = 1 - xx * (m - 1) / (2.0 * m * (m + 1))
    +    if xx < 0.0
    +        xx = 0.0
    +    end
    +    return tx * xx^(1.0 / (m - 1.0))
    +end
    +
    +function main(; n = 20, m = 2, Plotter = nothing, verbose = false,
    +              unknown_storage = :sparse, tend = 0.01, tstep = 0.0001, assembly = :edgewise)
    +
    +    # Create a one-dimensional discretization
    +    h = 1.0 / convert(Float64, n / 2)
    +    X = collect(-1:h:1)
    +    grid = VoronoiFVM.Grid(X)
    +
    +    # Flux function which describes the flux
    +    # between neighboring control volumes
    +    function flux!(f, u, edge)
    +        f[1] = u[1, 1]^m - u[1, 2]^m
    +    end
    +
    +    # Storage term
    +    function storage!(f, u, node)
    +        f[1] = u[1]
    +    end
    +
    +    # Create a physics structure
    +    physics = VoronoiFVM.Physics(; flux = flux!,
    +                                 storage = storage!)
    +
    +    # Create a finite volume system - either
    +    # in the dense or  the sparse version.
    +    # The difference is in the way the solution object
    +    # is stored - as dense or as sparse matrix
    +    sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly)
    +
    +    # Add species 1 to region 1
    +    enable_species!(sys, 1, [1])
    +
    +    # Create a solution array
    +    inival = unknowns(sys)
    +    t0 = 0.001
    +
    +    # Broadcast the initial value
    +    inival[1, :] .= map(x -> barenblatt(x, t0, m), X)
    +
    +    # Create solver control info for constant time step size
    +    control = VoronoiFVM.NewtonControl()
    +    control.verbose = verbose
    +    control.Δt_min = tstep
    +    control.Δt_max = tstep
    +    control.Δt = tstep
    +    control.Δu_opt = 1
    +
    +    tsol = solve(sys; inival, times = [t0, tend], control)
    +
    +    p = GridVisualizer(; Plotter = Plotter, layout = (1, 1), fast = true)
    +    for i = 1:length(tsol)
    +        time = tsol.t[i]
    +        scalarplot!(p[1, 1], grid, tsol[1, :, i]; title = @sprintf("t=%.3g", time),
    +                    color = :red, label = "numerical",
    +                    markershape = :circle, markevery = 1)
    +        scalarplot!(p[1, 1], grid, map(x -> barenblatt(x, time, m), grid); clear = false,
    +                    color = :green,
    +                    label = "exact", markershape = :none)
    +        reveal(p)
    +        sleep(1.0e-2)
    +    end
    +    return sum(tsol[end])
    +end
    +
    +using Test
    +function runtests()
    +    testval = 46.66666666647518
    +    @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example107_NonlinearStorage1D.jl b/v1.19.1/module_examples/Example107_NonlinearStorage1D.jl new file mode 100644 index 000000000..07ef70fbc --- /dev/null +++ b/v1.19.1/module_examples/Example107_NonlinearStorage1D.jl @@ -0,0 +1,108 @@ +#= + +# 107: 1D Nonlinear Storage +([source code](@__SOURCE_URL__)) + +This equation comes from the transformation of the nonlinear diffuision equation. +```math +\partial_t u^\frac{1}{m} -\Delta u = 0 +``` +in $\Omega=(-1,1)$ with homogeneous Neumann boundary conditions. +We can derive an exact solution from the Barenblatt solution of the previous +example. + +=# + +module Example107_NonlinearStorage1D +using Printf +using VoronoiFVM +using ExtendableGrids +using GridVisualize + +function barenblatt(x, t, m) + tx = t^(-1.0 / (m + 1.0)) + xx = x * tx + xx = xx * xx + xx = 1 - xx * (m - 1) / (2.0 * m * (m + 1)) + if xx < 0.0 + xx = 0.0 + end + return tx * xx^(1.0 / (m - 1.0)) +end + +function main(; n = 20, m = 2.0, Plotter = nothing, verbose = false, + unknown_storage = :sparse, tend = 0.01, tstep = 0.0001, assembly = :edgewise) + + ## Create a one-dimensional discretization + h = 1.0 / convert(Float64, n / 2) + X = collect(-1:h:1) + grid = VoronoiFVM.Grid(X) + + ## Flux function which describes the flux + ## between neighboring control volumes + function flux!(f, u, edge) + f[1] = u[1, 1] - u[1, 2] + end + + ϵ = 1.0e-10 + + ## Storage term + ## This needs to be regularized as its derivative + ## at 0 is infinity + function storage!(f, u, node) + f[1] = (ϵ + u[1])^(1.0 / m) + end + + ## Create a physics structure + physics = VoronoiFVM.Physics(; flux = flux!, + storage = storage!) + + ## Create a finite volume system - either + ## in the dense or the sparse version. + ## The difference is in the way the solution object + ## is stored - as dense or as sparse matrix + sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly) + + ## Add species 1 to region 1 + enable_species!(sys, 1, [1]) + + ## Create a solution array + inival = unknowns(sys) + solution = unknowns(sys) + t0 = 0.001 + + ## Broadcast the initial value + inival[1, :] .= map(x -> barenblatt(x, t0, m)^m, X) + + ## Create solver control info + control = VoronoiFVM.NewtonControl() + control.verbose = verbose + control.Δu_opt = 0.1 + control.force_first_step = true + tsol = solve(sys; inival, times = [t0, tend], control) + + if Plotter != nothing + p = GridVisualizer(; Plotter = Plotter, layout = (1, 1), fast = true) + for i = 1:length(tsol) + time = tsol.t[i] + scalarplot!(p[1, 1], grid, tsol[1, :, i]; title = @sprintf("t=%.3g", time), + color = :red, label = "numerical") + scalarplot!(p[1, 1], grid, map(x -> barenblatt(x, time, m)^m, grid); clear = false, + color = :green, label = "exact") + reveal(p) + sleep(1.0e-2) + end + end + return sum(tsol[end]) +end + +using Test +function runtests() + testval = 174.72418935404414 + @test main(; unknown_storage = :sparse, assembly = :edgewise)≈testval rtol=1.0e-5 + @test main(; unknown_storage = :dense, assembly = :edgewise)≈testval rtol=1.0e-5 + @test main(; unknown_storage = :sparse, assembly = :cellwise)≈testval rtol=1.0e-5 + @test main(; unknown_storage = :dense, assembly = :cellwise)≈testval rtol=1.0e-5 +end + +end diff --git a/v1.19.1/module_examples/Example107_NonlinearStorage1D/index.html b/v1.19.1/module_examples/Example107_NonlinearStorage1D/index.html new file mode 100644 index 000000000..adec3fdd8 --- /dev/null +++ b/v1.19.1/module_examples/Example107_NonlinearStorage1D/index.html @@ -0,0 +1,94 @@ + +107: 1D Nonlinear Storage · VoronoiFVM.jl

    107: 1D Nonlinear Storage

    (source code)

    This equation comes from the transformation of the nonlinear diffuision equation.

    \[\partial_t u^\frac{1}{m} -\Delta u = 0\]

    in $\Omega=(-1,1)$ with homogeneous Neumann boundary conditions. We can derive an exact solution from the Barenblatt solution of the previous example.

    module Example107_NonlinearStorage1D
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +
    +function barenblatt(x, t, m)
    +    tx = t^(-1.0 / (m + 1.0))
    +    xx = x * tx
    +    xx = xx * xx
    +    xx = 1 - xx * (m - 1) / (2.0 * m * (m + 1))
    +    if xx < 0.0
    +        xx = 0.0
    +    end
    +    return tx * xx^(1.0 / (m - 1.0))
    +end
    +
    +function main(; n = 20, m = 2.0, Plotter = nothing, verbose = false,
    +              unknown_storage = :sparse, tend = 0.01, tstep = 0.0001, assembly = :edgewise)
    +
    +    # Create a one-dimensional discretization
    +    h = 1.0 / convert(Float64, n / 2)
    +    X = collect(-1:h:1)
    +    grid = VoronoiFVM.Grid(X)
    +
    +    # Flux function which describes the flux
    +    # between neighboring control volumes
    +    function flux!(f, u, edge)
    +        f[1] = u[1, 1] - u[1, 2]
    +    end
    +
    +    ϵ = 1.0e-10
    +
    +    # Storage term
    +    # This needs to be regularized as its derivative
    +    # at 0 is infinity
    +    function storage!(f, u, node)
    +        f[1] = (ϵ + u[1])^(1.0 / m)
    +    end
    +
    +    # Create a physics structure
    +    physics = VoronoiFVM.Physics(; flux = flux!,
    +                                 storage = storage!)
    +
    +    # Create a finite volume system - either
    +    # in the dense or  the sparse version.
    +    # The difference is in the way the solution object
    +    # is stored - as dense or as sparse matrix
    +    sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly)
    +
    +    # Add species 1 to region 1
    +    enable_species!(sys, 1, [1])
    +
    +    # Create a solution array
    +    inival = unknowns(sys)
    +    solution = unknowns(sys)
    +    t0 = 0.001
    +
    +    # Broadcast the initial value
    +    inival[1, :] .= map(x -> barenblatt(x, t0, m)^m, X)
    +
    +    # Create solver control info
    +    control = VoronoiFVM.NewtonControl()
    +    control.verbose = verbose
    +    control.Δu_opt = 0.1
    +    control.force_first_step = true
    +    tsol = solve(sys; inival, times = [t0, tend], control)
    +
    +    if Plotter != nothing
    +        p = GridVisualizer(; Plotter = Plotter, layout = (1, 1), fast = true)
    +        for i = 1:length(tsol)
    +            time = tsol.t[i]
    +            scalarplot!(p[1, 1], grid, tsol[1, :, i]; title = @sprintf("t=%.3g", time),
    +                        color = :red, label = "numerical")
    +            scalarplot!(p[1, 1], grid, map(x -> barenblatt(x, time, m)^m, grid); clear = false,
    +                        color = :green, label = "exact")
    +            reveal(p)
    +            sleep(1.0e-2)
    +        end
    +    end
    +    return sum(tsol[end])
    +end
    +
    +using Test
    +function runtests()
    +    testval = 174.72418935404414
    +    @test main(; unknown_storage = :sparse, assembly = :edgewise)≈testval rtol=1.0e-5
    +    @test main(; unknown_storage = :dense, assembly = :edgewise)≈testval rtol=1.0e-5
    +    @test main(; unknown_storage = :sparse, assembly = :cellwise)≈testval rtol=1.0e-5
    +    @test main(; unknown_storage = :dense, assembly = :cellwise)≈testval rtol=1.0e-5
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example108_OrdinaryDiffEq1D.jl b/v1.19.1/module_examples/Example108_OrdinaryDiffEq1D.jl new file mode 100644 index 000000000..f8483e139 --- /dev/null +++ b/v1.19.1/module_examples/Example108_OrdinaryDiffEq1D.jl @@ -0,0 +1,105 @@ +#= +# 108: 1D Nonlinear Diffusion equation with ODE +([source code](@__SOURCE_URL__)) + +Solve the nonlinear diffusion equation + +```math +\partial_t u -\Delta u^m = 0 +``` +in $\Omega=(-1,1)$ with homogeneous Neumann boundary conditions using the implicit Euler method. + +This equation is also called "porous medium equation". +The Barenblatt solution is an exact solution of this problem which for m>1 has a finite support. +We initialize this problem with the exact solution for $t=t_0=0.001$. + +(see Barenblatt, G. I. "On nonsteady motions of gas and fluid in porous medium." Appl. Math. and Mech.(PMM) 16.1 (1952): 67-78.) +=# + +module Example108_OrdinaryDiffEq1D +using Printf +using VoronoiFVM +using ExtendableGrids +using GridVisualize +using OrdinaryDiffEq + +function barenblatt(x, t, m) + tx = t^(-1.0 / (m + 1.0)) + xx = x * tx + xx = xx * xx + xx = 1 - xx * (m - 1) / (2.0 * m * (m + 1)) + if xx < 0.0 + xx = 0.0 + end + return tx * xx^(1.0 / (m - 1.0)) +end + +function main(; n = 20, m = 2, Plotter = nothing, verbose = false, + unknown_storage = :sparse, tend = 0.01, assembly = :edgewise, solver=Rosenbrock23()) + + ## Create a one-dimensional discretization + h = 1.0 / convert(Float64, n / 2) + X = collect(-1:h:1) + grid = VoronoiFVM.Grid(X) + + ## Flux function which describes the flux + ## between neighboring control volumes + function flux!(f, u, edge) + f[1] = u[1, 1]^m - u[1, 2]^m + end + + ## Storage term + function storage!(f, u, node) + f[1] = u[1] + end + + ## Create a physics structure + physics = VoronoiFVM.Physics(; flux = flux!, + storage = storage!) + + ## Create a finite volume system - either + ## in the dense or the sparse version. + ## The difference is in the way the solution object + ## is stored - as dense or as sparse matrix + sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly) + + ## Add species 1 to region 1 + enable_species!(sys, 1, [1]) + + ## Create a solution array + inival = unknowns(sys) + t0 = 0.001 + + ## Broadcast the initial value + inival[1, :] .= map(x -> barenblatt(x, t0, m), X) + + problem = ODEProblem(sys,inival,(t0,tend)) + odesol = solve(problem,solver) + tsol=reshape(odesol,sys) + + + p = GridVisualizer(; Plotter = Plotter, layout = (1, 1), fast = true) + for i = 1:length(tsol) + time = tsol.t[i] + scalarplot!(p[1, 1], grid, tsol[1, :, i]; title = @sprintf("t=%.3g", time), + color = :red, label = "numerical", + markershape = :circle, markevery = 1) + scalarplot!(p[1, 1], grid, map(x -> barenblatt(x, time, m), grid); clear = false, + color = :green, + label = "exact", markershape = :none) + reveal(p) + sleep(1.0e-2) + end + return sum(tsol[end]) +end + +using Test +function runtests() + testval = 46.66666666671521 + @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval + @test main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval + @test main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval + @test main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval +end + +end diff --git a/v1.19.1/module_examples/Example108_OrdinaryDiffEq1D/index.html b/v1.19.1/module_examples/Example108_OrdinaryDiffEq1D/index.html new file mode 100644 index 000000000..4f0d87cfd --- /dev/null +++ b/v1.19.1/module_examples/Example108_OrdinaryDiffEq1D/index.html @@ -0,0 +1,88 @@ + +108: 1D Nonlinear Diffusion equation with ODE · VoronoiFVM.jl

    108: 1D Nonlinear Diffusion equation with ODE

    (source code)

    Solve the nonlinear diffusion equation

    \[\partial_t u -\Delta u^m = 0\]

    in $\Omega=(-1,1)$ with homogeneous Neumann boundary conditions using the implicit Euler method.

    This equation is also called "porous medium equation". The Barenblatt solution is an exact solution of this problem which for m>1 has a finite support. We initialize this problem with the exact solution for $t=t_0=0.001$.

    (see Barenblatt, G. I. "On nonsteady motions of gas and fluid in porous medium." Appl. Math. and Mech.(PMM) 16.1 (1952): 67-78.)

    module Example108_OrdinaryDiffEq1D
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +using OrdinaryDiffEq
    +
    +function barenblatt(x, t, m)
    +    tx = t^(-1.0 / (m + 1.0))
    +    xx = x * tx
    +    xx = xx * xx
    +    xx = 1 - xx * (m - 1) / (2.0 * m * (m + 1))
    +    if xx < 0.0
    +        xx = 0.0
    +    end
    +    return tx * xx^(1.0 / (m - 1.0))
    +end
    +
    +function main(; n = 20, m = 2, Plotter = nothing, verbose = false,
    +              unknown_storage = :sparse, tend = 0.01, assembly = :edgewise, solver=Rosenbrock23())
    +
    +    # Create a one-dimensional discretization
    +    h = 1.0 / convert(Float64, n / 2)
    +    X = collect(-1:h:1)
    +    grid = VoronoiFVM.Grid(X)
    +
    +    # Flux function which describes the flux
    +    # between neighboring control volumes
    +    function flux!(f, u, edge)
    +        f[1] = u[1, 1]^m - u[1, 2]^m
    +    end
    +
    +    # Storage term
    +    function storage!(f, u, node)
    +        f[1] = u[1]
    +    end
    +
    +    # Create a physics structure
    +    physics = VoronoiFVM.Physics(; flux = flux!,
    +                                 storage = storage!)
    +
    +    # Create a finite volume system - either
    +    # in the dense or  the sparse version.
    +    # The difference is in the way the solution object
    +    # is stored - as dense or as sparse matrix
    +    sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly)
    +
    +    # Add species 1 to region 1
    +    enable_species!(sys, 1, [1])
    +
    +    # Create a solution array
    +    inival = unknowns(sys)
    +    t0 = 0.001
    +
    +    # Broadcast the initial value
    +    inival[1, :] .= map(x -> barenblatt(x, t0, m), X)
    +
    +    problem = ODEProblem(sys,inival,(t0,tend))
    +    odesol = solve(problem,solver)
    +    tsol=reshape(odesol,sys)
    +
    +
    +    p = GridVisualizer(; Plotter = Plotter, layout = (1, 1), fast = true)
    +    for i = 1:length(tsol)
    +        time = tsol.t[i]
    +        scalarplot!(p[1, 1], grid, tsol[1, :, i]; title = @sprintf("t=%.3g", time),
    +                    color = :red, label = "numerical",
    +                    markershape = :circle, markevery = 1)
    +        scalarplot!(p[1, 1], grid, map(x -> barenblatt(x, time, m), grid); clear = false,
    +                    color = :green,
    +                    label = "exact", markershape = :none)
    +        reveal(p)
    +        sleep(1.0e-2)
    +    end
    +    return sum(tsol[end])
    +end
    +
    +using Test
    +function runtests()
    +    testval = 46.66666666671521
    +    @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval
    +    @test main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval
    +    @test main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval
    +    @test main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example110_ReactionDiffusion1D_TwoSpecies.jl b/v1.19.1/module_examples/Example110_ReactionDiffusion1D_TwoSpecies.jl new file mode 100644 index 000000000..322b89d44 --- /dev/null +++ b/v1.19.1/module_examples/Example110_ReactionDiffusion1D_TwoSpecies.jl @@ -0,0 +1,90 @@ +# # 110: 1D Reaction Diffusion equation with two species +# ([source code](@__SOURCE_URL__)) +# +# Solve the nonlinear coupled reaction diffusion problem +# +# ```math +# -\nabla (0.01+2u_2)\nabla u_1 + u_1u_2= 0.0001(0.01+x) +# ``` +# +# ```math +# -\nabla (0.01+2u_1)\nabla u_2 - u_1u_2 = 0.0001(1.01-x) +# ``` +# +# +# in $\Omega=(0,1)$ with boundary condition $u_1(0)=1$, $u_2(0)=0$ and $u_1(1)=1$, $u_2(1)=1$. +# + +module Example110_ReactionDiffusion1D_TwoSpecies + +using Printf +using VoronoiFVM +using ExtendableGrids +using GridVisualize + +function main(; n = 100, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise) + h = 1 / n + grid = VoronoiFVM.Grid(collect(0:h:1)) + + eps::Vector{Float64} = [1.0, 1.0] + + physics = VoronoiFVM.Physics( + ; reaction = function (f, u, node) + f[1] = u[1] * u[2] + f[2] = -u[1] * u[2] + end, + flux = function (f, u, edge) + nspecies = 2 + f[1] = eps[1] * (u[1, 1] - u[1, 2]) * + (0.01 + u[2, 1] + u[2, 2]) + f[2] = eps[2] * (u[2, 1] - u[2, 2]) * + (0.01 + u[1, 1] + u[1, 2]) + end, source = function (f, node) + f[1] = 1.0e-4 * (0.01 + node[1]) + f[2] = 1.0e-4 * (0.01 + 1.0 - node[1]) + end, storage = function (f, u, node) + f[1] = u[1] + f[2] = u[2] + end) + + sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage) + + enable_species!(sys, 1, [1]) + enable_species!(sys, 2, [1]) + + boundary_dirichlet!(sys, 1, 1, 1.0) + boundary_dirichlet!(sys, 1, 2, 0.0) + + boundary_dirichlet!(sys, 2, 1, 1.0) + boundary_dirichlet!(sys, 2, 2, 0.0) + + U = unknowns(sys) + U .= 0 + + control = VoronoiFVM.NewtonControl() + control.verbose = verbose + control.damp_initial = 0.1 + u5 = 0 + p = GridVisualizer(; Plotter = Plotter, layout = (2, 1)) + for xeps in [1.0, 0.5, 0.25, 0.1, 0.05, 0.025, 0.01] + eps = [xeps, xeps] + U = solve(sys; inival = U, control) + scalarplot!(p[1, 1], grid, U[1, :]; clear = true, title = "U1, eps=$(xeps)") + scalarplot!(p[2, 1], grid, U[2, :]; clear = true, title = "U2, eps=$(xeps)", + reveal = true) + sleep(0.2) + u5 = U[5] + end + return u5 +end + +using Test +function runtests() + testval = 0.7117546972922056 + + @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval && + main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval +end +end diff --git a/v1.19.1/module_examples/Example110_ReactionDiffusion1D_TwoSpecies/index.html b/v1.19.1/module_examples/Example110_ReactionDiffusion1D_TwoSpecies/index.html new file mode 100644 index 000000000..a584cad3c --- /dev/null +++ b/v1.19.1/module_examples/Example110_ReactionDiffusion1D_TwoSpecies/index.html @@ -0,0 +1,74 @@ + +110: 1D Reaction Diffusion equation with two species · VoronoiFVM.jl

    110: 1D Reaction Diffusion equation with two species

    (source code)

    Solve the nonlinear coupled reaction diffusion problem

    \[-\nabla (0.01+2u_2)\nabla u_1 + u_1u_2= 0.0001(0.01+x)\]

    \[-\nabla (0.01+2u_1)\nabla u_2 - u_1u_2 = 0.0001(1.01-x)\]

    in $\Omega=(0,1)$ with boundary condition $u_1(0)=1$, $u_2(0)=0$ and $u_1(1)=1$, $u_2(1)=1$.

    module Example110_ReactionDiffusion1D_TwoSpecies
    +
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +
    +function main(; n = 100, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise)
    +    h = 1 / n
    +    grid = VoronoiFVM.Grid(collect(0:h:1))
    +
    +    eps::Vector{Float64} = [1.0, 1.0]
    +
    +    physics = VoronoiFVM.Physics(
    +                                 ; reaction = function (f, u, node)
    +                                     f[1] = u[1] * u[2]
    +                                     f[2] = -u[1] * u[2]
    +                                 end,
    +                                 flux = function (f, u, edge)
    +                                     nspecies = 2
    +                                     f[1] = eps[1] * (u[1, 1] - u[1, 2]) *
    +                                            (0.01 + u[2, 1] + u[2, 2])
    +                                     f[2] = eps[2] * (u[2, 1] - u[2, 2]) *
    +                                            (0.01 + u[1, 1] + u[1, 2])
    +                                 end, source = function (f, node)
    +                                     f[1] = 1.0e-4 * (0.01 + node[1])
    +                                     f[2] = 1.0e-4 * (0.01 + 1.0 - node[1])
    +                                 end, storage = function (f, u, node)
    +                                     f[1] = u[1]
    +                                     f[2] = u[2]
    +                                 end)
    +
    +    sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage)
    +
    +    enable_species!(sys, 1, [1])
    +    enable_species!(sys, 2, [1])
    +
    +    boundary_dirichlet!(sys, 1, 1, 1.0)
    +    boundary_dirichlet!(sys, 1, 2, 0.0)
    +
    +    boundary_dirichlet!(sys, 2, 1, 1.0)
    +    boundary_dirichlet!(sys, 2, 2, 0.0)
    +
    +    U = unknowns(sys)
    +    U .= 0
    +
    +    control = VoronoiFVM.NewtonControl()
    +    control.verbose = verbose
    +    control.damp_initial = 0.1
    +    u5 = 0
    +    p = GridVisualizer(; Plotter = Plotter, layout = (2, 1))
    +    for xeps in [1.0, 0.5, 0.25, 0.1, 0.05, 0.025, 0.01]
    +        eps = [xeps, xeps]
    +        U = solve(sys; inival = U, control)
    +        scalarplot!(p[1, 1], grid, U[1, :]; clear = true, title = "U1, eps=$(xeps)")
    +        scalarplot!(p[2, 1], grid, U[2, :]; clear = true, title = "U2, eps=$(xeps)",
    +                    reveal = true)
    +        sleep(0.2)
    +        u5 = U[5]
    +    end
    +    return u5
    +end
    +
    +using Test
    +function runtests()
    +    testval = 0.7117546972922056
    +
    +    @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval
    +end
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example115_HeterogeneousCatalysis1D.jl b/v1.19.1/module_examples/Example115_HeterogeneousCatalysis1D.jl new file mode 100644 index 000000000..79d96b04c --- /dev/null +++ b/v1.19.1/module_examples/Example115_HeterogeneousCatalysis1D.jl @@ -0,0 +1,183 @@ +#= + +# 115: 1D heterogeneous catalysis + ([source code](@__SOURCE_URL__)) + +Let $\Omega=(0,1)$, $\Gamma_1=\{0\}$, $\Gamma_2=\{1\}$ +Regard a system of three species: $A,B,C$ and let +$u_A=[A]$, $u_B=[B]$ and $u_C=[C]$ be their corresponding concentrations. + +Species $A$ and $B$ exist in the interior of the domain, species $C$ +lives a the boundary $\Gamma_1$. We assume a heterogeneous reaction scheme +where $A$ reacts to $C$ and $C$ reacts to $B$: + +```math +\begin{aligned} + A &\leftrightarrow C\\ + C &\leftrightarrow B +\end{aligned} +``` +with reaction constants $k_{AC}^\pm$ and k_{BC}^\pm$. + +In $\Omega$, both $A$ and $B$ are transported through diffusion: + +```math +\begin{aligned} +\partial_t u_B - \nabla\cdot D_A \nabla u_A & = f_A\\ +\partial_t u_B - \nabla\cdot D_B \nabla u_B & = 0\\ +\end{aligned} +``` +Here, $f(x)$ is a source term creating $A$. +On $\Gamma_2$, we set boundary conditions +```math +\begin{aligned} +D_A \nabla u_A & = 0\\ +u_B&=0 +\end{aligned} +``` +describing no normal flux for $A$ and zero concentration of $B$. +On $\Gamma_1$, we use the mass action law to describe the boundary reaction and +the evolution of the boundary concentration $C$. We assume that there is a limited +amount of surface sites $S$ for species C, so in fact A has to react with a free +surface site in order to become $C$ which reflected by the factor $1-u_C$. The same +is true for $B$. +```math +\begin{aligned} +R_{AC}(u_A, u_C)&=k_{AC}^+ u_A(1-u_C) - k_{AC}^-u_C\\ +R_{BC}(u_C, u_B)&=k_{BC}^+ u_B(1-u_C) - k_{BC}^-u_C\\ +- D_A \nabla u_A + S R_{AC}(u_A, u_C)& =0 \\ +- D_B \nabla u_B + S R_{BC}(u_B, u_C)& =0 \\ +\partial_t C - R_{AC}(u_A, u_C) - R_{BC}(u_B, u_C) &=0 +\end{aligned} +``` + +=# + +module Example115_HeterogeneousCatalysis1D +using Printf +using VoronoiFVM +using ExtendableGrids +using GridVisualize +using LinearAlgebra + +function main(; n = 10, Plotter = nothing, verbose = false, tend = 1, + unknown_storage = :sparse, assembly = :edgewise) + h = 1.0 / convert(Float64, n) + X = collect(0.0:h:1.0) + N = length(X) + + grid = VoronoiFVM.Grid(X) + ## By default, \Gamma_1 at X[1] and \Gamma_2 is at X[end] + + ## Species numbers + iA = 1 + iB = 2 + iC = 3 + + ## Diffusion flux for species A and B + D_A = 1.0 + D_B = 1.0e-2 + function flux!(f, u, edge) + f[iA] = D_A * (u[iA, 1] - u[iA, 2]) + f[iB] = D_B * (u[iB, 1] - u[iB, 2]) + end + + ## Storage term of species A and B + function storage!(f, u, node) + f[iA] = u[iA] + f[iB] = u[iB] + end + + ## Source term for species a around 0.5 + function source!(f, node) + x1 = node[1] - 0.5 + f[iA] = exp(-100 * x1^2) + end + + ## Reaction constants (p = + , m = -) + ## Chosen to prefer path A-> C -> B + ## More over, A reacts faster than to C than C to B + ## leading to "catalyst poisoning", i.e. C taking up most of the + ## available catalyst sites + kp_AC = 100.0 + km_AC = 1.0 + + kp_BC = 0.1 + km_BC = 1.0 + + S = 0.01 + + R_AC(u_A, u_C) = kp_AC * u_A * (1 - u_C) - km_AC * u_C + R_BC(u_B, u_C) = kp_BC * u_B * (1 - u_C) - km_BC * u_C + + function breaction!(f, u, node) + if node.region == 1 + f[iA] = S * R_AC(u[iA], u[iC]) + f[iB] = S * R_BC(u[iB], u[iC]) + f[iC] = -R_BC(u[iB], u[iC]) - R_AC(u[iA], u[iC]) + end + end + + ## This is for the term \partial_t u_C at the boundary + function bstorage!(f, u, node) + if node.region == 1 + f[iC] = u[iC] + end + end + + physics = VoronoiFVM.Physics(; breaction = breaction!, + bstorage = bstorage!, + flux = flux!, + storage = storage!, + source = source!) + + sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage) + + ## Enable species in bulk resp + enable_species!(sys, iA, [1]) + enable_species!(sys, iB, [1]) + + ## Enable surface species + enable_boundary_species!(sys, iC, [1]) + + ## Set Dirichlet bc for species B on \Gamma_2 + boundary_dirichlet!(sys, iB, 2, 0.0) + + ## Initial values + inival = unknowns(sys) + inival .= 0.0 + U = unknowns(sys) + + tstep = 0.01 + time = 0.0 + + ## Data to store surface concentration vs time + + p = GridVisualizer(; Plotter = Plotter, layout = (3, 1)) + + control = fixed_timesteps!(VoronoiFVM.NewtonControl(), tstep) + tsol = solve(sys; inival, times = [0, tend], control, verbose = verbose) + + p = GridVisualizer(; Plotter = Plotter, layout = (3, 1), fast = true) + for it = 1:length(tsol) + time = tsol.t[it] + scalarplot!(p[1, 1], grid, tsol[iA, :, it]; clear = true, + title = @sprintf("[A]: (%.3f,%.3f)", extrema(tsol[iA, :, it])...)) + scalarplot!(p[2, 1], grid, tsol[iB, :, it]; clear = true, + title = @sprintf("[B]: (%.3f,%.3f)", extrema(tsol[iB, :, it])...)) + scalarplot!(p[3, 1], tsol.t[1:it], tsol[iC, 1, 1:it]; title = @sprintf("[C]"), + clear = true, show = true) + end + + return tsol[iC, 1, end] +end + +using Test +function runtests() + testval = 0.87544440641274 + @test isapprox(main(; unknown_storage = :sparse, assembly = :edgewise), testval; rtol = 1.0e-12) && + isapprox(main(; unknown_storage = :dense, assembly = :edgewise), testval; rtol = 1.0e-12) && + isapprox(main(; unknown_storage = :sparse, assembly = :cellwise), testval; rtol = 1.0e-12) && + isapprox(main(; unknown_storage = :dense, assembly = :cellwise), testval; rtol = 1.0e-12) +end +end diff --git a/v1.19.1/module_examples/Example115_HeterogeneousCatalysis1D/index.html b/v1.19.1/module_examples/Example115_HeterogeneousCatalysis1D/index.html new file mode 100644 index 000000000..282abac2f --- /dev/null +++ b/v1.19.1/module_examples/Example115_HeterogeneousCatalysis1D/index.html @@ -0,0 +1,144 @@ + +115: 1D heterogeneous catalysis · VoronoiFVM.jl

    115: 1D heterogeneous catalysis

    (source code)

    Let $\Omega=(0,1)$, $\Gamma_1=\{0\}$, $\Gamma_2=\{1\}$ Regard a system of three species: $A,B,C$ and let $u_A=[A]$, $u_B=[B]$ and $u_C=[C]$ be their corresponding concentrations.

    Species $A$ and $B$ exist in the interior of the domain, species $C$ lives a the boundary $\Gamma_1$. We assume a heterogeneous reaction scheme where $A$ reacts to $C$ and $C$ reacts to $B$:

    \[\begin{aligned} + A &\leftrightarrow C\\ + C &\leftrightarrow B +\end{aligned}\]

    with reaction constants $k_{AC}^\pm$ and k_{BC}^\pm$.

    In $\Omega$, both $A$ and $B$ are transported through diffusion:

    \[\begin{aligned} +\partial_t u_B - \nabla\cdot D_A \nabla u_A & = f_A\\ +\partial_t u_B - \nabla\cdot D_B \nabla u_B & = 0\\ +\end{aligned}\]

    Here, $f(x)$ is a source term creating $A$. On $\Gamma_2$, we set boundary conditions

    \[\begin{aligned} +D_A \nabla u_A & = 0\\ +u_B&=0 +\end{aligned}\]

    describing no normal flux for $A$ and zero concentration of $B$. On $\Gamma_1$, we use the mass action law to describe the boundary reaction and the evolution of the boundary concentration $C$. We assume that there is a limited amount of surface sites $S$ for species C, so in fact A has to react with a free surface site in order to become $C$ which reflected by the factor $1-u_C$. The same is true for $B$.

    \[\begin{aligned} +R_{AC}(u_A, u_C)&=k_{AC}^+ u_A(1-u_C) - k_{AC}^-u_C\\ +R_{BC}(u_C, u_B)&=k_{BC}^+ u_B(1-u_C) - k_{BC}^-u_C\\ +- D_A \nabla u_A + S R_{AC}(u_A, u_C)& =0 \\ +- D_B \nabla u_B + S R_{BC}(u_B, u_C)& =0 \\ +\partial_t C - R_{AC}(u_A, u_C) - R_{BC}(u_B, u_C) &=0 +\end{aligned}\]

    module Example115_HeterogeneousCatalysis1D
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +using LinearAlgebra
    +
    +function main(; n = 10, Plotter = nothing, verbose = false, tend = 1,
    +              unknown_storage = :sparse, assembly = :edgewise)
    +    h = 1.0 / convert(Float64, n)
    +    X = collect(0.0:h:1.0)
    +    N = length(X)
    +
    +    grid = VoronoiFVM.Grid(X)
    +    # By default, \Gamma_1 at X[1] and \Gamma_2 is at X[end]
    +
    +    # Species numbers
    +    iA = 1
    +    iB = 2
    +    iC = 3
    +
    +    # Diffusion flux for species A and B
    +    D_A = 1.0
    +    D_B = 1.0e-2
    +    function flux!(f, u, edge)
    +        f[iA] = D_A * (u[iA, 1] - u[iA, 2])
    +        f[iB] = D_B * (u[iB, 1] - u[iB, 2])
    +    end
    +
    +    # Storage term of species A and B
    +    function storage!(f, u, node)
    +        f[iA] = u[iA]
    +        f[iB] = u[iB]
    +    end
    +
    +    # Source term for species a around 0.5
    +    function source!(f, node)
    +        x1 = node[1] - 0.5
    +        f[iA] = exp(-100 * x1^2)
    +    end
    +
    +    # Reaction constants (p = + , m = -)
    +    # Chosen to prefer path A-> C -> B
    +    # More over, A reacts faster than to C than C to B
    +    # leading to "catalyst poisoning", i.e. C taking up most of the
    +    # available catalyst sites
    +    kp_AC = 100.0
    +    km_AC = 1.0
    +
    +    kp_BC = 0.1
    +    km_BC = 1.0
    +
    +    S = 0.01
    +
    +    R_AC(u_A, u_C) = kp_AC * u_A * (1 - u_C) - km_AC * u_C
    +    R_BC(u_B, u_C) = kp_BC * u_B * (1 - u_C) - km_BC * u_C
    +
    +    function breaction!(f, u, node)
    +        if node.region == 1
    +            f[iA] = S * R_AC(u[iA], u[iC])
    +            f[iB] = S * R_BC(u[iB], u[iC])
    +            f[iC] = -R_BC(u[iB], u[iC]) - R_AC(u[iA], u[iC])
    +        end
    +    end
    +
    +    # This is for the term \partial_t u_C at the boundary
    +    function bstorage!(f, u, node)
    +        if node.region == 1
    +            f[iC] = u[iC]
    +        end
    +    end
    +
    +    physics = VoronoiFVM.Physics(; breaction = breaction!,
    +                                 bstorage = bstorage!,
    +                                 flux = flux!,
    +                                 storage = storage!,
    +                                 source = source!)
    +
    +    sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage)
    +
    +    # Enable species in bulk resp
    +    enable_species!(sys, iA, [1])
    +    enable_species!(sys, iB, [1])
    +
    +    # Enable surface species
    +    enable_boundary_species!(sys, iC, [1])
    +
    +    # Set Dirichlet bc for species B on \Gamma_2
    +    boundary_dirichlet!(sys, iB, 2, 0.0)
    +
    +    # Initial values
    +    inival = unknowns(sys)
    +    inival .= 0.0
    +    U = unknowns(sys)
    +
    +    tstep = 0.01
    +    time = 0.0
    +
    +    # Data to store surface concentration vs time
    +
    +    p = GridVisualizer(; Plotter = Plotter, layout = (3, 1))
    +
    +    control = fixed_timesteps!(VoronoiFVM.NewtonControl(), tstep)
    +    tsol = solve(sys; inival, times = [0, tend], control, verbose = verbose)
    +
    +    p = GridVisualizer(; Plotter = Plotter, layout = (3, 1), fast = true)
    +    for it = 1:length(tsol)
    +        time = tsol.t[it]
    +        scalarplot!(p[1, 1], grid, tsol[iA, :, it]; clear = true,
    +                    title = @sprintf("[A]: (%.3f,%.3f)", extrema(tsol[iA, :, it])...))
    +        scalarplot!(p[2, 1], grid, tsol[iB, :, it]; clear = true,
    +                    title = @sprintf("[B]: (%.3f,%.3f)", extrema(tsol[iB, :, it])...))
    +        scalarplot!(p[3, 1], tsol.t[1:it], tsol[iC, 1, 1:it]; title = @sprintf("[C]"),
    +                    clear = true, show = true)
    +    end
    +
    +    return tsol[iC, 1, end]
    +end
    +
    +using Test
    +function runtests()
    +    testval = 0.87544440641274
    +    @test isapprox(main(; unknown_storage = :sparse, assembly = :edgewise), testval; rtol = 1.0e-12) &&
    +          isapprox(main(; unknown_storage = :dense, assembly = :edgewise), testval; rtol = 1.0e-12) &&
    +          isapprox(main(; unknown_storage = :sparse, assembly = :cellwise), testval; rtol = 1.0e-12) &&
    +          isapprox(main(; unknown_storage = :dense, assembly = :cellwise), testval; rtol = 1.0e-12)
    +end
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example120_ThreeRegions1D.jl b/v1.19.1/module_examples/Example120_ThreeRegions1D.jl new file mode 100644 index 000000000..ba431f743 --- /dev/null +++ b/v1.19.1/module_examples/Example120_ThreeRegions1D.jl @@ -0,0 +1,148 @@ +# # 120: Differing species sets in regions, 1D +# ([source code](@__SOURCE_URL__)) + +module Example120_ThreeRegions1D + +using Printf +using VoronoiFVM +using ExtendableGrids +using GridVisualize +using LinearSolve + +function main(; n = 30, Plotter = nothing, plot_grid = false, verbose = false, + unknown_storage = :sparse, tend = 10, + rely_on_corrections = false, assembly = :edgewise) + h = 3.0 / (n - 1) + X = collect(0:h:3.0) + grid = VoronoiFVM.Grid(X) + cellmask!(grid, [0.0], [1.0], 1) + cellmask!(grid, [1.0], [2.1], 2) + cellmask!(grid, [1.9], [3.0], 3) + + subgrid1 = subgrid(grid, [1]) + subgrid2 = subgrid(grid, [1, 2, 3]) + subgrid3 = subgrid(grid, [3]) + + if plot_grid + plotgrid(grid; Plotter = Plotter) + return + end + + eps = [1, 1, 1] + k = [1, 1, 1] + + function reaction(f, u, node) + if node.region == 1 + f[1] = k[1] * u[1] + f[2] = -k[1] * u[1] + elseif node.region == 3 + f[2] = k[3] * u[2] + f[3] = -k[3] * u[2] + else + f[1] = 0 + end + end + + function source(f, node) + if node.region == 1 + f[1] = 1.0e-4 * (3.0 - node[1]) + end + end + + if rely_on_corrections + ## Since 0.17.0 one can + ## write into the result also where + ## the corresponding species has not been enabled + ## Species information is used to prevent the assembly. + flux = function (f, u, edge) + for i = 1:3 + f[i] = eps[i] * (u[i, 1] - u[i, 2]) + end + end + + storage = function (f, u, node) + f .= u + end + else + ## This is the "old" way: + ## Write into result only where + ## the corresponding species has been enabled + flux = function (f, u, edge) + if edge.region == 1 + f[1] = eps[1] * (u[1, 1] - u[1, 2]) + f[2] = eps[2] * (u[2, 1] - u[2, 2]) + elseif edge.region == 2 + f[2] = eps[2] * (u[2, 1] - u[2, 2]) + elseif edge.region == 3 + f[2] = eps[2] * (u[2, 1] - u[2, 2]) + f[3] = eps[3] * (u[3, 1] - u[3, 2]) + end + end + + storage = function (f, u, node) + if node.region == 1 + f[1] = u[1] + f[2] = u[2] + elseif node.region == 2 + f[2] = u[2] + elseif node.region == 3 + f[2] = u[2] + f[3] = u[3] + end + end + end + + sys = VoronoiFVM.System(grid; flux, reaction, storage, source, + unknown_storage = unknown_storage, assembly = assembly) + + enable_species!(sys, 1, [1]) + enable_species!(sys, 2, [1, 2, 3]) + enable_species!(sys, 3, [3]) + + boundary_dirichlet!(sys, 3, 2, 0.0) + + testval = 0 + p = GridVisualizer(; Plotter = Plotter, layout = (1, 1)) + + testval = 0.0 + function plot_timestep(U, Uold, time, Δt) + U1 = view(U[1, :], subgrid1) + U2 = view(U[2, :], subgrid2) + U3 = view(U[3, :], subgrid3) + + testval += sum(U2) + + if Plotter == nothing + return + end + + scalarplot!(p[1, 1], subgrid1, U1; label = "spec1", color = (0.5, 0, 0), + xlimits = (0, 3), flimits = (0, 1e-3), + title = @sprintf("three regions t=%.3g", time)) + scalarplot!(p[1, 1], subgrid2, U2; label = "spec2", color = (0.0, 0.5, 0), + clear = false) + scalarplot!(p[1, 1], subgrid3, U3; label = "spec3", color = (0.0, 0.0, 0.5), + clear = false, show = true) + end + + tsol = solve(sys; inival = 0, times = (0, tend), post = plot_timestep, verbose = verbose, Δu_opt = 1.0e-5, + method_linear=KLUFactorization()) + + return testval +end + +using Test + +function runtests() + testval = 0.359448515181824 + @test main(; unknown_storage = :sparse, rely_on_corrections = false, assembly = :edgewise) ≈ testval + @test main(; unknown_storage = :dense, rely_on_corrections = false, assembly = :edgewise) ≈ testval + @test main(; unknown_storage = :sparse, rely_on_corrections = true, assembly = :edgewise) ≈ testval + @test main(; unknown_storage = :dense, rely_on_corrections = true, assembly = :edgewise) ≈ testval + @test main(; unknown_storage = :sparse, rely_on_corrections = false, assembly = :cellwise) ≈ testval + @test main(; unknown_storage = :dense, rely_on_corrections = false, assembly = :cellwise) ≈ testval + @test main(; unknown_storage = :sparse, rely_on_corrections = true, assembly = :cellwise) ≈ testval + @test main(; unknown_storage = :dense, rely_on_corrections = true, assembly = :cellwise) ≈ testval +end + +end diff --git a/v1.19.1/module_examples/Example120_ThreeRegions1D/index.html b/v1.19.1/module_examples/Example120_ThreeRegions1D/index.html new file mode 100644 index 000000000..f6c07b421 --- /dev/null +++ b/v1.19.1/module_examples/Example120_ThreeRegions1D/index.html @@ -0,0 +1,146 @@ + +120: Differing species sets in regions, 1D · VoronoiFVM.jl

    120: Differing species sets in regions, 1D

    (source code)

    module Example120_ThreeRegions1D
    +
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +using LinearSolve
    +
    +function main(; n = 30, Plotter = nothing, plot_grid = false, verbose = false,
    +              unknown_storage = :sparse, tend = 10,
    +              rely_on_corrections = false, assembly = :edgewise)
    +    h = 3.0 / (n - 1)
    +    X = collect(0:h:3.0)
    +    grid = VoronoiFVM.Grid(X)
    +    cellmask!(grid, [0.0], [1.0], 1)
    +    cellmask!(grid, [1.0], [2.1], 2)
    +    cellmask!(grid, [1.9], [3.0], 3)
    +
    +    subgrid1 = subgrid(grid, [1])
    +    subgrid2 = subgrid(grid, [1, 2, 3])
    +    subgrid3 = subgrid(grid, [3])
    +
    +    if plot_grid
    +        plotgrid(grid; Plotter = Plotter)
    +        return
    +    end
    +
    +    eps = [1, 1, 1]
    +    k = [1, 1, 1]
    +
    +    function reaction(f, u, node)
    +        if node.region == 1
    +            f[1] = k[1] * u[1]
    +            f[2] = -k[1] * u[1]
    +        elseif node.region == 3
    +            f[2] = k[3] * u[2]
    +            f[3] = -k[3] * u[2]
    +        else
    +            f[1] = 0
    +        end
    +    end
    +
    +    function source(f, node)
    +        if node.region == 1
    +            f[1] = 1.0e-4 * (3.0 - node[1])
    +        end
    +    end
    +
    +    if rely_on_corrections
    +        # Since 0.17.0 one can
    +        # write into the result also where
    +        # the corresponding species has not been enabled
    +        # Species information is used to prevent the assembly.
    +        flux = function (f, u, edge)
    +            for i = 1:3
    +                f[i] = eps[i] * (u[i, 1] - u[i, 2])
    +            end
    +        end
    +
    +        storage = function (f, u, node)
    +            f .= u
    +        end
    +    else
    +        # This is the "old" way:
    +        # Write into result only where
    +        # the corresponding species has been enabled
    +        flux = function (f, u, edge)
    +            if edge.region == 1
    +                f[1] = eps[1] * (u[1, 1] - u[1, 2])
    +                f[2] = eps[2] * (u[2, 1] - u[2, 2])
    +            elseif edge.region == 2
    +                f[2] = eps[2] * (u[2, 1] - u[2, 2])
    +            elseif edge.region == 3
    +                f[2] = eps[2] * (u[2, 1] - u[2, 2])
    +                f[3] = eps[3] * (u[3, 1] - u[3, 2])
    +            end
    +        end
    +
    +        storage = function (f, u, node)
    +            if node.region == 1
    +                f[1] = u[1]
    +                f[2] = u[2]
    +            elseif node.region == 2
    +                f[2] = u[2]
    +            elseif node.region == 3
    +                f[2] = u[2]
    +                f[3] = u[3]
    +            end
    +        end
    +    end
    +
    +    sys = VoronoiFVM.System(grid; flux, reaction, storage, source,
    +                            unknown_storage = unknown_storage, assembly = assembly)
    +
    +    enable_species!(sys, 1, [1])
    +    enable_species!(sys, 2, [1, 2, 3])
    +    enable_species!(sys, 3, [3])
    +
    +    boundary_dirichlet!(sys, 3, 2, 0.0)
    +
    +    testval = 0
    +    p = GridVisualizer(; Plotter = Plotter, layout = (1, 1))
    +
    +    testval = 0.0
    +    function plot_timestep(U, Uold, time, Δt)
    +        U1 = view(U[1, :], subgrid1)
    +        U2 = view(U[2, :], subgrid2)
    +        U3 = view(U[3, :], subgrid3)
    +
    +        testval += sum(U2)
    +
    +        if Plotter == nothing
    +            return
    +        end
    +
    +        scalarplot!(p[1, 1], subgrid1, U1; label = "spec1", color = (0.5, 0, 0),
    +                    xlimits = (0, 3), flimits = (0, 1e-3),
    +                    title = @sprintf("three regions t=%.3g", time))
    +        scalarplot!(p[1, 1], subgrid2, U2; label = "spec2", color = (0.0, 0.5, 0),
    +                    clear = false)
    +        scalarplot!(p[1, 1], subgrid3, U3; label = "spec3", color = (0.0, 0.0, 0.5),
    +                    clear = false, show = true)
    +    end
    +
    +    tsol = solve(sys; inival = 0, times = (0, tend), post = plot_timestep, verbose = verbose, Δu_opt = 1.0e-5,
    +                 method_linear=KLUFactorization())
    +
    +    return testval
    +end
    +
    +using Test
    +
    +function runtests()
    +    testval = 0.359448515181824
    +    @test main(; unknown_storage = :sparse, rely_on_corrections = false, assembly = :edgewise) ≈ testval
    +    @test main(; unknown_storage = :dense, rely_on_corrections = false, assembly = :edgewise) ≈ testval
    +    @test main(; unknown_storage = :sparse, rely_on_corrections = true, assembly = :edgewise) ≈ testval
    +    @test main(; unknown_storage = :dense, rely_on_corrections = true, assembly = :edgewise) ≈ testval
    +    @test main(; unknown_storage = :sparse, rely_on_corrections = false, assembly = :cellwise) ≈ testval
    +    @test main(; unknown_storage = :dense, rely_on_corrections = false, assembly = :cellwise) ≈ testval
    +    @test main(; unknown_storage = :sparse, rely_on_corrections = true, assembly = :cellwise) ≈ testval
    +    @test main(; unknown_storage = :dense, rely_on_corrections = true, assembly = :cellwise) ≈ testval
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example121_PoissonPointCharge1D.jl b/v1.19.1/module_examples/Example121_PoissonPointCharge1D.jl new file mode 100644 index 000000000..8586200bc --- /dev/null +++ b/v1.19.1/module_examples/Example121_PoissonPointCharge1D.jl @@ -0,0 +1,108 @@ +#= + # 121: 1D Poisson with point charge + ([source code](@__SOURCE_URL__)) + +Solve a Poisson equation +```math +- \Delta u = 0 +``` + +in $\Omega=(-1,1)$ +with a point charge $Q$ at $x=0$. +=# + +module Example121_PoissonPointCharge1D + +using Printf + +using VoronoiFVM +using ExtendableGrids +using GridVisualize + +function main(; nref = 0, Plotter = nothing, verbose = false, unknown_storage = :sparse, + brea = false, assembly = :edgewise) + + ## Create grid in (-1,1) refined around 0 + hmax = 0.2 / 2.0^nref + hmin = 0.05 / 2.0^nref + X1 = geomspace(-1.0, 0.0, hmax, hmin) + X2 = geomspace(0.0, 1.0, hmin, hmax) + X = glue(X1, X2) + grid = VoronoiFVM.Grid(X) + + ## Edit default region numbers: + ## additional boundary region 3 at 0.0 + bfacemask!(grid, [0.0], [0.0], 3) + ## Material 1 left of 0 + cellmask!(grid, [-1.0], [0.0], 1) + ## Material 2 right of 0 + cellmask!(grid, [0.0], [1.0], 2) + + Q::Float64 = 0.0 + + function flux!(f, u, edge) + f[1] = u[1, 1] - u[1, 2] + end + function storage!(f, u, node) + f[1] = u[1] + end + + ## Define boundary reaction defining charge + ## Note that the term is written on the left hand side, therefore the - sign + function breaction!(f, u, node) + if node.region == 3 + f[1] = -Q + end + end + + ## Create physics + physics = VoronoiFVM.Physics(; flux = flux!, + storage = storage!, + breaction = breaction!) + + ## Create system + sys = VoronoiFVM.System(grid, physics; unknown_storage = :dense, assembly = assembly) + + ## put potential into both regions + enable_species!(sys, 1, [1, 2]) + + ## Set boundary conditions + + boundary_dirichlet!(sys, 1, 1, 1.0) + boundary_dirichlet!(sys, 1, 2, 0.0) + + ## Create a solution array + U = unknowns(sys) + U .= 0 + + ## Create solver control info + control = VoronoiFVM.NewtonControl() + control.verbose = verbose + + vis = GridVisualizer(; Plotter = Plotter) + ## Solve and plot for several values of charge + for q in [0.1, 0.2, 0.4, 0.8, 1.6] + if brea + ## Charge in reaction term + Q = q + else + ## Charge as boundary condition + sys.boundary_values[1, 3] = q + end + U = solve(sys; inival = U, control) + + ## Plot data + + scalarplot!(vis, grid, U[1, :]; title = @sprintf("Q=%.2f", q), clear = true, + show = true) + end + return sum(U) +end + +using Test +function runtests() + testval = 20.254591679579015 + @test main(; assembly = :edgewise) ≈ testval && + main(; assembly = :cellwise) ≈ testval +end +end diff --git a/v1.19.1/module_examples/Example121_PoissonPointCharge1D/index.html b/v1.19.1/module_examples/Example121_PoissonPointCharge1D/index.html new file mode 100644 index 000000000..7e9350311 --- /dev/null +++ b/v1.19.1/module_examples/Example121_PoissonPointCharge1D/index.html @@ -0,0 +1,96 @@ + +121: 1D Poisson with point charge · VoronoiFVM.jl

    121: 1D Poisson with point charge

    (source code)

    Solve a Poisson equation

    \[- \Delta u = 0\]

    in $\Omega=(-1,1)$ with a point charge $Q$ at $x=0$.

    module Example121_PoissonPointCharge1D
    +
    +using Printf
    +
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +
    +function main(; nref = 0, Plotter = nothing, verbose = false, unknown_storage = :sparse,
    +              brea = false, assembly = :edgewise)
    +
    +    # Create grid in (-1,1) refined around 0
    +    hmax = 0.2 / 2.0^nref
    +    hmin = 0.05 / 2.0^nref
    +    X1 = geomspace(-1.0, 0.0, hmax, hmin)
    +    X2 = geomspace(0.0, 1.0, hmin, hmax)
    +    X = glue(X1, X2)
    +    grid = VoronoiFVM.Grid(X)
    +
    +    # Edit default region numbers:
    +    #   additional boundary region 3 at 0.0
    +    bfacemask!(grid, [0.0], [0.0], 3)
    +    # Material 1 left of 0
    +    cellmask!(grid, [-1.0], [0.0], 1)
    +    # Material 2 right of 0
    +    cellmask!(grid, [0.0], [1.0], 2)
    +
    +    Q::Float64 = 0.0
    +
    +    function flux!(f, u, edge)
    +        f[1] = u[1, 1] - u[1, 2]
    +    end
    +    function storage!(f, u, node)
    +        f[1] = u[1]
    +    end
    +
    +    # Define boundary reaction defining charge
    +    # Note that the term  is written on  the left hand side, therefore the - sign
    +    function breaction!(f, u, node)
    +        if node.region == 3
    +            f[1] = -Q
    +        end
    +    end
    +
    +    # Create physics
    +    physics = VoronoiFVM.Physics(; flux = flux!,
    +                                 storage = storage!,
    +                                 breaction = breaction!)
    +
    +    # Create system
    +    sys = VoronoiFVM.System(grid, physics; unknown_storage = :dense, assembly = assembly)
    +
    +    #  put potential into both regions
    +    enable_species!(sys, 1, [1, 2])
    +
    +    # Set boundary conditions
    +
    +    boundary_dirichlet!(sys, 1, 1, 1.0)
    +    boundary_dirichlet!(sys, 1, 2, 0.0)
    +
    +    # Create a solution array
    +    U = unknowns(sys)
    +    U .= 0
    +
    +    # Create solver control info
    +    control = VoronoiFVM.NewtonControl()
    +    control.verbose = verbose
    +
    +    vis = GridVisualizer(; Plotter = Plotter)
    +    # Solve and plot for several values of charge
    +    for q in [0.1, 0.2, 0.4, 0.8, 1.6]
    +        if brea
    +            # Charge in reaction term
    +            Q = q
    +        else
    +            # Charge as boundary condition
    +            sys.boundary_values[1, 3] = q
    +        end
    +        U = solve(sys; inival = U, control)
    +
    +        # Plot data
    +
    +        scalarplot!(vis, grid, U[1, :]; title = @sprintf("Q=%.2f", q), clear = true,
    +                    show = true)
    +    end
    +    return sum(U)
    +end
    +
    +using Test
    +function runtests()
    +    testval = 20.254591679579015
    +    @test main(; assembly = :edgewise) ≈ testval &&
    +          main(; assembly = :cellwise) ≈ testval
    +end
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example125_TestFunctions1D.jl b/v1.19.1/module_examples/Example125_TestFunctions1D.jl new file mode 100644 index 000000000..24af5b84c --- /dev/null +++ b/v1.19.1/module_examples/Example125_TestFunctions1D.jl @@ -0,0 +1,75 @@ +#= +# 125: Terminal flux calculation via test functions +([source code](@__SOURCE_URL__)) + +For a rather comprehensive explanation +see [225: Terminal flux calculation via test functions, nD](@ref) +=# + +module Example125_TestFunctions1D +using Printf +using VoronoiFVM +using ExtendableGrids +using GridVisualize + +function main(; n = 100, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise) + h = 1 / n + grid = VoronoiFVM.Grid(collect(0:h:1)) + + eps::Vector{Float64} = [1, 1.0e-1] + + physics = VoronoiFVM.Physics( + ; reaction = function (f, u, node) + f[1] = 10 * (u[1] - u[2]) + f[2] = 10 * (u[2] - u[1]) + end, flux = function (f, u, edge) + f[1] = eps[1] * (u[1, 1] - u[1, 2]) + f[2] = eps[2] * (u[2, 1] - u[2, 2]) + end, storage = function (f, u, node) + f[1] = u[1] + f[2] = u[2] + end) + sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly) + + enable_species!(sys, 1, [1]) + enable_species!(sys, 2, [1]) + + boundary_neumann!(sys, 1, 1, 0.01) + boundary_dirichlet!(sys, 2, 2, 0.0) + + factory = TestFunctionFactory(sys) + tf1 = testfunction(factory, [2], [1]) + tf2 = testfunction(factory, [1], [2]) + + inival = unknowns(sys) + inival[2, :] .= 0.1 + inival[1, :] .= 0.1 + + control = VoronoiFVM.NewtonControl() + control.verbose = verbose + control.damp_initial = 0.1 + I1 = 0 + p = GridVisualizer(; Plotter = Plotter, layout = (2, 1)) + for xeps in [1.0, 0.1, 0.01] + eps = [xeps, xeps] + U = solve(sys; inival, control) + I1 = integrate(sys, tf1, U) + coord = coordinates(grid) + inival .= U + scalarplot!(p[1, 1], grid, U[1, :]) + scalarplot!(p[2, 1], grid, U[2, :]) + reveal(p) + u5 = U[5] + end + return I1[1] +end + +using Test +function runtests() + testval = 0.01 + @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval && + main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval +end +end diff --git a/v1.19.1/module_examples/Example125_TestFunctions1D/index.html b/v1.19.1/module_examples/Example125_TestFunctions1D/index.html new file mode 100644 index 000000000..c7c11b0c5 --- /dev/null +++ b/v1.19.1/module_examples/Example125_TestFunctions1D/index.html @@ -0,0 +1,68 @@ + +125: Terminal flux calculation via test functions · VoronoiFVM.jl

    125: Terminal flux calculation via test functions

    (source code)

    For a rather comprehensive explanation see 225: Terminal flux calculation via test functions, nD

    module Example125_TestFunctions1D
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +
    +function main(; n = 100, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise)
    +    h = 1 / n
    +    grid = VoronoiFVM.Grid(collect(0:h:1))
    +
    +    eps::Vector{Float64} = [1, 1.0e-1]
    +
    +    physics = VoronoiFVM.Physics(
    +                                 ; reaction = function (f, u, node)
    +                                     f[1] = 10 * (u[1] - u[2])
    +                                     f[2] = 10 * (u[2] - u[1])
    +                                 end, flux = function (f, u, edge)
    +                                     f[1] = eps[1] * (u[1, 1] - u[1, 2])
    +                                     f[2] = eps[2] * (u[2, 1] - u[2, 2])
    +                                 end, storage = function (f, u, node)
    +                                     f[1] = u[1]
    +                                     f[2] = u[2]
    +                                 end)
    +    sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly)
    +
    +    enable_species!(sys, 1, [1])
    +    enable_species!(sys, 2, [1])
    +
    +    boundary_neumann!(sys, 1, 1, 0.01)
    +    boundary_dirichlet!(sys, 2, 2, 0.0)
    +
    +    factory = TestFunctionFactory(sys)
    +    tf1 = testfunction(factory, [2], [1])
    +    tf2 = testfunction(factory, [1], [2])
    +
    +    inival = unknowns(sys)
    +    inival[2, :] .= 0.1
    +    inival[1, :] .= 0.1
    +
    +    control = VoronoiFVM.NewtonControl()
    +    control.verbose = verbose
    +    control.damp_initial = 0.1
    +    I1 = 0
    +    p = GridVisualizer(; Plotter = Plotter, layout = (2, 1))
    +    for xeps in [1.0, 0.1, 0.01]
    +        eps = [xeps, xeps]
    +        U = solve(sys; inival, control)
    +        I1 = integrate(sys, tf1, U)
    +        coord = coordinates(grid)
    +        inival .= U
    +        scalarplot!(p[1, 1], grid, U[1, :])
    +        scalarplot!(p[2, 1], grid, U[2, :])
    +        reveal(p)
    +        u5 = U[5]
    +    end
    +    return I1[1]
    +end
    +
    +using Test
    +function runtests()
    +    testval = 0.01
    +    @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval
    +end
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example150_Impedance1D.jl b/v1.19.1/module_examples/Example150_Impedance1D.jl new file mode 100644 index 000000000..4cdaf4cd2 --- /dev/null +++ b/v1.19.1/module_examples/Example150_Impedance1D.jl @@ -0,0 +1,164 @@ +# # 150: Impedance calculation +# ([source code](@__SOURCE_URL__)) +# +# Impedance calculation for +# +# C u_t - (D u_x)_x + Ru = 0 in (0,1) +# u(0,t)=1 + exp(iωt) +# u(1,t)=0 +# +# Measurement: I(t)= D u_x(1,t) +# +# Steady state: +# - (D u0_x)_x + Ru0 = 0 +# u0(0,t)=1 +# u0(1,t)=0 +# +# Small signal ansatz for ω +# +# u(x,t)= u0(x)+ ua(x) exp(iωt) +# +# iωC ua - (D ua_x)_x + R u_a =0 +# ua(0)=1 +# ua(1)=0 +# +# + +module Example150_Impedance1D + +using Printf +using VoronoiFVM +using ExtendableGrids +using GridVisualize + +function main(; nref = 0, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise, + L = 1.0, R = 1.0, D = 1.0, C = 1.0, + ω0 = 1.0e-3, ω1 = 5.0e1) + + # Create array which is refined close to 0 + h0 = 0.005 / 2.0^nref + h1 = 0.1 / 2.0^nref + + X = VoronoiFVM.geomspace(0, L, h0, h1) + + # Create discretitzation grid + grid = VoronoiFVM.Grid(X) + + # Create and fill data + data = (R = R, D = D, C = C) + + # Declare constitutive functions + flux = function (f, u, edge, data) + f[1] = data.D * (u[1, 1] - u[1, 2]) + end + + storage = function (f, u, node, data) + f[1] = data.C * u[1] + end + + reaction = function (f, u, node, data) + f[1] = data.R * u[1] + end + + excited_bc = 1 + excited_bcval = 1.0 + excited_spec = 1 + meas_bc = 2 + + # Create physics struct + physics = VoronoiFVM.Physics(; data = data, + flux = flux, + storage = storage, + reaction = reaction) + # Create discrete system and enable species + sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly) + + enable_species!(sys, excited_spec, [1]) + + # Create test functions for current measurement + + factory = TestFunctionFactory(sys) + measurement_testfunction = testfunction(factory, [excited_bc], [meas_bc]) + + boundary_dirichlet!(sys, excited_spec, excited_bc, excited_bcval) + boundary_dirichlet!(sys, excited_spec, meas_bc, 0.0) + + steadystate = solve(sys) + + function meas_stdy(meas, U) + u = reshape(U, sys) + meas[1] = -VoronoiFVM.integrate_stdy(sys, measurement_testfunction, u)[excited_spec] + nothing + end + + function meas_tran(meas, U) + u = reshape(U, sys) + meas[1] = -VoronoiFVM.integrate_tran(sys, measurement_testfunction, u)[excited_spec] + nothing + end + + dmeas_stdy = measurement_derivative(sys, meas_stdy, steadystate) + dmeas_tran = measurement_derivative(sys, meas_tran, steadystate) + + # Create Impeadancs system from steady state + isys = VoronoiFVM.ImpedanceSystem(sys, steadystate, excited_spec, excited_bc) + + # Prepare recording of impedance results + allomega = zeros(0) + + # for calculated data + allI0 = zeros(Complex{Float64}, 0) + allIL = zeros(Complex{Float64}, 0) + + # for exact data + allIx0 = zeros(Complex{Float64}, 0) + allIxL = zeros(Complex{Float64}, 0) + + ω = ω0 + + UZ = unknowns(isys) + while ω < ω1 + + # solve impedance system + solve!(UZ, isys, ω) + + # calculate approximate solution + # obtain measurement in frequency domain + IL = impedance(isys, ω, steadystate, dmeas_stdy, dmeas_tran) + + # record approximate solution + push!(allomega, ω) + push!(allIL, IL) + + # record exact solution + iω = 1im * ω + z = sqrt(iω * data.C / data.D + data.R / data.D) + eplus = exp(z * L) + eminus = exp(-z * L) + IxL = 2.0 * data.D * z / (eplus - eminus) + + push!(allIxL, 1 / IxL) + + # increase omega + ω = ω * 1.1 + end + + p = GridVisualizer(; Plotter = Plotter) + scalarplot!(p, real(allIxL), imag(allIxL); label = "exact", color = :red, + linestyle = :dot) + scalarplot!(p, real(allIL), imag(allIL); label = "calc", show = true, clear = false, + color = :blue, linestyle = :solid) + + sum(allIL) +end + +using Test +function runtests() + testval = 57.92710286186797 + 23.163945443946027im + @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval && + main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval +end + +end diff --git a/v1.19.1/module_examples/Example150_Impedance1D/index.html b/v1.19.1/module_examples/Example150_Impedance1D/index.html new file mode 100644 index 000000000..3b586c7a2 --- /dev/null +++ b/v1.19.1/module_examples/Example150_Impedance1D/index.html @@ -0,0 +1,90 @@ + +150: Impedance calculation · VoronoiFVM.jl

    150: Impedance calculation

    (source code)

    Impedance calculation for

    C ut - (D ux)_x + Ru = 0 in (0,1) u(0,t)=1 + exp(iωt) u(1,t)=0

    Measurement: I(t)= D u_x(1,t)

    Steady state:

    • (D u0x)x + Ru0 = 0

    u0(0,t)=1 u0(1,t)=0

    Small signal ansatz for ω

    u(x,t)= u0(x)+ ua(x) exp(iωt)

    iωC ua - (D uax)x + R u_a =0 ua(0)=1 ua(1)=0

    module Example150_Impedance1D
    +
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +
    +function main(; nref = 0, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise,
    +              L = 1.0, R = 1.0, D = 1.0, C = 1.0,
    +              ω0 = 1.0e-3, ω1 = 5.0e1)

    Create array which is refined close to 0

        h0 = 0.005 / 2.0^nref
    +    h1 = 0.1 / 2.0^nref
    +
    +    X = VoronoiFVM.geomspace(0, L, h0, h1)

    Create discretitzation grid

        grid = VoronoiFVM.Grid(X)

    Create and fill data

        data = (R = R, D = D, C = C)

    Declare constitutive functions

        flux = function (f, u, edge, data)
    +        f[1] = data.D * (u[1, 1] - u[1, 2])
    +    end
    +
    +    storage = function (f, u, node, data)
    +        f[1] = data.C * u[1]
    +    end
    +
    +    reaction = function (f, u, node, data)
    +        f[1] = data.R * u[1]
    +    end
    +
    +    excited_bc = 1
    +    excited_bcval = 1.0
    +    excited_spec = 1
    +    meas_bc = 2

    Create physics struct

        physics = VoronoiFVM.Physics(; data = data,
    +                                 flux = flux,
    +                                 storage = storage,
    +                                 reaction = reaction)

    Create discrete system and enable species

        sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly)
    +
    +    enable_species!(sys, excited_spec, [1])

    Create test functions for current measurement

        factory = TestFunctionFactory(sys)
    +    measurement_testfunction = testfunction(factory, [excited_bc], [meas_bc])
    +
    +    boundary_dirichlet!(sys, excited_spec, excited_bc, excited_bcval)
    +    boundary_dirichlet!(sys, excited_spec, meas_bc, 0.0)
    +
    +    steadystate = solve(sys)
    +
    +    function meas_stdy(meas, U)
    +        u = reshape(U, sys)
    +        meas[1] = -VoronoiFVM.integrate_stdy(sys, measurement_testfunction, u)[excited_spec]
    +        nothing
    +    end
    +
    +    function meas_tran(meas, U)
    +        u = reshape(U, sys)
    +        meas[1] = -VoronoiFVM.integrate_tran(sys, measurement_testfunction, u)[excited_spec]
    +        nothing
    +    end
    +
    +    dmeas_stdy = measurement_derivative(sys, meas_stdy, steadystate)
    +    dmeas_tran = measurement_derivative(sys, meas_tran, steadystate)

    Create Impeadancs system from steady state

        isys = VoronoiFVM.ImpedanceSystem(sys, steadystate, excited_spec, excited_bc)

    Prepare recording of impedance results

        allomega = zeros(0)

    for calculated data

        allI0 = zeros(Complex{Float64}, 0)
    +    allIL = zeros(Complex{Float64}, 0)

    for exact data

        allIx0 = zeros(Complex{Float64}, 0)
    +    allIxL = zeros(Complex{Float64}, 0)
    +
    +    ω = ω0
    +
    +    UZ = unknowns(isys)
    +    while ω < ω1

    solve impedance system

            solve!(UZ, isys, ω)

    calculate approximate solution obtain measurement in frequency domain

            IL = impedance(isys, ω, steadystate, dmeas_stdy, dmeas_tran)

    record approximate solution

            push!(allomega, ω)
    +        push!(allIL, IL)

    record exact solution

            iω = 1im * ω
    +        z = sqrt(iω * data.C / data.D + data.R / data.D)
    +        eplus = exp(z * L)
    +        eminus = exp(-z * L)
    +        IxL = 2.0 * data.D * z / (eplus - eminus)
    +
    +        push!(allIxL, 1 / IxL)

    increase omega

            ω = ω * 1.1
    +    end
    +
    +    p = GridVisualizer(; Plotter = Plotter)
    +    scalarplot!(p, real(allIxL), imag(allIxL); label = "exact", color = :red,
    +                linestyle = :dot)
    +    scalarplot!(p, real(allIL), imag(allIL); label = "calc", show = true, clear = false,
    +                color = :blue, linestyle = :solid)
    +
    +    sum(allIL)
    +end
    +
    +using Test
    +function runtests()
    +    testval = 57.92710286186797 + 23.163945443946027im
    +    @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example151_Impedance1D.jl b/v1.19.1/module_examples/Example151_Impedance1D.jl new file mode 100644 index 000000000..56b349f51 --- /dev/null +++ b/v1.19.1/module_examples/Example151_Impedance1D.jl @@ -0,0 +1,171 @@ +# # 151: Impedance calculation +# ([source code](@__SOURCE_URL__)) + +# Same as Example150, but with new and more generic way of +# passing the parameter. + +# +# Impedance calculation for +# +# C u_t - (D u_x)_x + Ru = 0 in (0,1) +# u(0,t)=1 + exp(iωt) +# u(1,t)=0 +# +# Measurement: I(t)= D u_x(1,t) +# +# Steady state: +# - (D u0_x)_x + Ru0 = 0 +# u0(0,t)=1 +# u0(1,t)=0 +# +# Small signal ansatz for ω +# +# u(x,t)= u0(x)+ ua(x) exp(iωt) +# +# iωC ua - (D ua_x)_x + R u_a =0 +# ua(0)=1 +# ua(1)=0 +# +# + +module Example151_Impedance1D + +using Printf +using VoronoiFVM +using ExtendableGrids +using GridVisualize + +function main(; nref = 0, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise, + L = 1.0, R = 1.0, D = 1.0, C = 1.0, + ω0 = 1.0e-3, ω1 = 5.0e1) + + # Create array which is refined close to 0 + h0 = 0.005 / 2.0^nref + h1 = 0.1 / 2.0^nref + + X = VoronoiFVM.geomspace(0, L, h0, h1) + + # Create discretitzation grid + grid = VoronoiFVM.Grid(X) + + # Create and fill data + data = (R = R, D = D, C = C) + + # Declare constitutive functions + flux = function (f, u, edge, data) + f[1] = data.D * (u[1, 1] - u[1, 2]) + end + + storage = function (f, u, node, data) + f[1] = data.C * u[1] + end + + reaction = function (f, u, node, data) + f[1] = data.R * u[1] + end + + excited_bc = 1 + excited_bcval = 1.0 + excited_spec = 1 + meas_bc = 2 + + bc = function (f, u, node, data) + p = parameters(u) + boundary_dirichlet!(f, u, node; region = excited_bc, value = p[1]) + boundary_dirichlet!(f, u, node; region = meas_bc, value = 0.0) + end + + # Create discrete system and enable species + sys = VoronoiFVM.System(grid; unknown_storage = unknown_storage, + data = data, + flux = flux, + storage = storage, + reaction = reaction, + bcondition = bc, + nparams = 1, + species = 1, assembly = assembly) + + # Create test functions for current measurement + + factory = TestFunctionFactory(sys) + measurement_testfunction = testfunction(factory, [excited_bc], [meas_bc]) + + steadystate = solve(sys; inival = 0.0, params = [1.0]) + + function meas_stdy(meas, U) + u = reshape(U, sys) + meas[1] = -VoronoiFVM.integrate_stdy(sys, measurement_testfunction, u)[excited_spec] + nothing + end + + function meas_tran(meas, U) + u = reshape(U, sys) + meas[1] = -VoronoiFVM.integrate_tran(sys, measurement_testfunction, u)[excited_spec] + nothing + end + + dmeas_stdy = measurement_derivative(sys, meas_stdy, steadystate) + dmeas_tran = measurement_derivative(sys, meas_tran, steadystate) + + # Create Impeadancs system from steady state + isys = VoronoiFVM.ImpedanceSystem(sys, steadystate) + + # Prepare recording of impedance results + allomega = zeros(0) + + # for calculated data + allI0 = zeros(Complex{Float64}, 0) + allIL = zeros(Complex{Float64}, 0) + + # for exact data + allIx0 = zeros(Complex{Float64}, 0) + allIxL = zeros(Complex{Float64}, 0) + + ω = ω0 + + UZ = unknowns(isys) + while ω < ω1 + + # solve impedance system + solve!(UZ, isys, ω) + + # calculate approximate solution + # obtain measurement in frequency domain + IL = impedance(isys, ω, steadystate, dmeas_stdy, dmeas_tran) + + # record approximate solution + push!(allomega, ω) + push!(allIL, IL) + + # record exact solution + iω = 1im * ω + z = sqrt(iω * data.C / data.D + data.R / data.D) + eplus = exp(z * L) + eminus = exp(-z * L) + IxL = 2.0 * data.D * z / (eplus - eminus) + + push!(allIxL, 1 / IxL) + + # increase omega + ω = ω * 1.1 + end + + p = GridVisualizer(; Plotter = Plotter) + scalarplot!(p, real(allIxL), imag(allIxL); label = "exact", color = :red, + linestyle = :dot) + scalarplot!(p, real(allIL), imag(allIL); label = "calc", show = true, clear = false, + color = :blue, linestyle = :solid) + + sum(allIL) +end + +using Test +function runtests() + testval = 57.92710286186797 + 23.163945443946027im + @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval && + main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval +end + +end diff --git a/v1.19.1/module_examples/Example151_Impedance1D/index.html b/v1.19.1/module_examples/Example151_Impedance1D/index.html new file mode 100644 index 000000000..503e2b913 --- /dev/null +++ b/v1.19.1/module_examples/Example151_Impedance1D/index.html @@ -0,0 +1,95 @@ + +151: Impedance calculation · VoronoiFVM.jl

    151: Impedance calculation

    (source code)

    Same as Example150, but with new and more generic way of passing the parameter.

    Impedance calculation for

    C ut - (D ux)_x + Ru = 0 in (0,1) u(0,t)=1 + exp(iωt) u(1,t)=0

    Measurement: I(t)= D u_x(1,t)

    Steady state:

    • (D u0x)x + Ru0 = 0

    u0(0,t)=1 u0(1,t)=0

    Small signal ansatz for ω

    u(x,t)= u0(x)+ ua(x) exp(iωt)

    iωC ua - (D uax)x + R u_a =0 ua(0)=1 ua(1)=0

    module Example151_Impedance1D
    +
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +
    +function main(; nref = 0, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise,
    +              L = 1.0, R = 1.0, D = 1.0, C = 1.0,
    +              ω0 = 1.0e-3, ω1 = 5.0e1)

    Create array which is refined close to 0

        h0 = 0.005 / 2.0^nref
    +    h1 = 0.1 / 2.0^nref
    +
    +    X = VoronoiFVM.geomspace(0, L, h0, h1)

    Create discretitzation grid

        grid = VoronoiFVM.Grid(X)

    Create and fill data

        data = (R = R, D = D, C = C)

    Declare constitutive functions

        flux = function (f, u, edge, data)
    +        f[1] = data.D * (u[1, 1] - u[1, 2])
    +    end
    +
    +    storage = function (f, u, node, data)
    +        f[1] = data.C * u[1]
    +    end
    +
    +    reaction = function (f, u, node, data)
    +        f[1] = data.R * u[1]
    +    end
    +
    +    excited_bc = 1
    +    excited_bcval = 1.0
    +    excited_spec = 1
    +    meas_bc = 2
    +
    +    bc = function (f, u, node, data)
    +        p = parameters(u)
    +        boundary_dirichlet!(f, u, node; region = excited_bc, value = p[1])
    +        boundary_dirichlet!(f, u, node; region = meas_bc, value = 0.0)
    +    end

    Create discrete system and enable species

        sys = VoronoiFVM.System(grid; unknown_storage = unknown_storage,
    +                            data = data,
    +                            flux = flux,
    +                            storage = storage,
    +                            reaction = reaction,
    +                            bcondition = bc,
    +                            nparams = 1,
    +                            species = 1, assembly = assembly)

    Create test functions for current measurement

        factory = TestFunctionFactory(sys)
    +    measurement_testfunction = testfunction(factory, [excited_bc], [meas_bc])
    +
    +    steadystate = solve(sys; inival = 0.0, params = [1.0])
    +
    +    function meas_stdy(meas, U)
    +        u = reshape(U, sys)
    +        meas[1] = -VoronoiFVM.integrate_stdy(sys, measurement_testfunction, u)[excited_spec]
    +        nothing
    +    end
    +
    +    function meas_tran(meas, U)
    +        u = reshape(U, sys)
    +        meas[1] = -VoronoiFVM.integrate_tran(sys, measurement_testfunction, u)[excited_spec]
    +        nothing
    +    end
    +
    +    dmeas_stdy = measurement_derivative(sys, meas_stdy, steadystate)
    +    dmeas_tran = measurement_derivative(sys, meas_tran, steadystate)

    Create Impeadancs system from steady state

        isys = VoronoiFVM.ImpedanceSystem(sys, steadystate)

    Prepare recording of impedance results

        allomega = zeros(0)

    for calculated data

        allI0 = zeros(Complex{Float64}, 0)
    +    allIL = zeros(Complex{Float64}, 0)

    for exact data

        allIx0 = zeros(Complex{Float64}, 0)
    +    allIxL = zeros(Complex{Float64}, 0)
    +
    +    ω = ω0
    +
    +    UZ = unknowns(isys)
    +    while ω < ω1

    solve impedance system

            solve!(UZ, isys, ω)

    calculate approximate solution obtain measurement in frequency domain

            IL = impedance(isys, ω, steadystate, dmeas_stdy, dmeas_tran)

    record approximate solution

            push!(allomega, ω)
    +        push!(allIL, IL)

    record exact solution

            iω = 1im * ω
    +        z = sqrt(iω * data.C / data.D + data.R / data.D)
    +        eplus = exp(z * L)
    +        eminus = exp(-z * L)
    +        IxL = 2.0 * data.D * z / (eplus - eminus)
    +
    +        push!(allIxL, 1 / IxL)

    increase omega

            ω = ω * 1.1
    +    end
    +
    +    p = GridVisualizer(; Plotter = Plotter)
    +    scalarplot!(p, real(allIxL), imag(allIxL); label = "exact", color = :red,
    +                linestyle = :dot)
    +    scalarplot!(p, real(allIL), imag(allIL); label = "calc", show = true, clear = false,
    +                color = :blue, linestyle = :solid)
    +
    +    sum(allIL)
    +end
    +
    +using Test
    +function runtests()
    +    testval = 57.92710286186797 + 23.163945443946027im
    +    @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example160_UnipolarDriftDiffusion1D.jl b/v1.19.1/module_examples/Example160_UnipolarDriftDiffusion1D.jl new file mode 100644 index 000000000..830a1a5e4 --- /dev/null +++ b/v1.19.1/module_examples/Example160_UnipolarDriftDiffusion1D.jl @@ -0,0 +1,267 @@ +#= + +# 160: Unipolar degenerate drift-diffusion +([source code](@__SOURCE_URL__)) + +See: C. Cancès, C. Chainais-Hillairet, J. Fuhrmann, and B. Gaudeul, "A numerical-analysis-focused comparison of several finite volume schemes for a unipolar degenerate drift-diffusion model" IMA Journal of Numerical Analysis, vol. 41, no. 1, pp. 271–314, 2021. + +Available from [https://doi.org/10.1093/imanum/draa002](https://doi.org/10.1093/imanum/draa002), +the preprint is on [arxiv1907.11126](https://arxiv.org/abs/1907.11126). + +The problem consists of a Poisson equation for the electrostatic potential $\phi$: + +```math +-\nabla \varepsilon \nabla \phi = z(2c-1) +``` +and a degenerate drift-diffusion equation of the transport of a charged species $c$: + +```math +\partial_t u - \nabla\cdot \left(\nabla c + c \nabla (\phi - \log (1-c) )\right) +``` + +In particular, the paper, among others, investigates the "sedan" flux discretization which is able to handle the degeneracy coming from the $\log (1-c)$ term. The earliest reference to this scheme we found in the source code of the [SEDAN III](http://www-tcad.stanford.edu/tcad/programs/sedan3.html) semiconductor device simulator. +=# + +module Example160_UnipolarDriftDiffusion1D + +using Printf + +using VoronoiFVM +using ExtendableGrids +using GridVisualize +using LinearSolve + +mutable struct Data + eps::Float64 + z::Float64 + ic::Int32 + iphi::Int32 + V::Float64 + Data() = new() +end + +function classflux!(f, u, edge, data) + ic = data.ic + iphi = data.iphi + f[iphi] = data.eps * (u[iphi, 1] - u[iphi, 2]) + bp, bm = fbernoulli_pm(u[iphi, 1] - u[iphi, 2]) + f[ic] = bm * u[ic, 1] - bp * u[ic, 2] +end + +function storage!(f, u, node, data) + ic = data.ic + iphi = data.iphi + f[iphi] = 0 + f[ic] = u[ic] +end + +function reaction!(f, u, node, data) + ic = data.ic + iphi = data.iphi + f[iphi] = data.z * (1 - 2 * u[ic]) + f[ic] = 0 +end +const eps_reg=1.0e-10 +function sedanflux!(f, u, edge, data) + ic = data.ic + iphi = data.iphi + f[iphi] = data.eps * (u[iphi, 1] - u[iphi, 2]) + mu1 = -log1p(-u[ic, 1]+eps_reg) + mu2 = -log1p(-u[ic, 2]+eps_reg) + bp, bm = fbernoulli_pm(data.z * 2 * (u[iphi, 1] - u[iphi, 2]) + (mu1 - mu2)) + f[ic] = bm * u[ic, 1] - bp * u[ic, 2] +end + +function bcondition!(f, u, bnode, data) + V = ramp(bnode.time; dt = (0, 1.0e-2), du = (0, data.V)) + boundary_dirichlet!(f, u, bnode; species = data.iphi, region = 1, value = V) + boundary_dirichlet!(f, u, bnode; species = data.iphi, region = 2, value = 0) + boundary_dirichlet!(f, u, bnode; species = data.ic, region = 2, value = 0.5) +end + +function main(; + n = 20, + Plotter = nothing, + dlcap = false, + verbose = false, + phimax = 1, + dphi = 1.0e-1, + unknown_storage = :sparse, + assembly = :edgewise,) + h = 1.0 / convert(Float64, n) + grid = VoronoiFVM.Grid(collect(0:h:1)) + + data = Data() + data.eps = 1.0e-3 + data.z = -1 + data.iphi = 1 + data.ic = 2 + data.V = 5 + ic = data.ic + iphi = data.iphi + + physics = VoronoiFVM.Physics(; + data = data, + flux = sedanflux!, + reaction = reaction!, + breaction = bcondition!, + storage = storage!,) + + sys = VoronoiFVM.System(grid, + physics; + unknown_storage = unknown_storage, + species = [1, 2], + assembly = assembly,) + + inival = unknowns(sys) + @views inival[iphi, :] .= 0 + @views inival[ic, :] .= 0.5 + + if !dlcap + ## Create solver control info for constant time step size + tstep = 1.0e-5 + control = VoronoiFVM.NewtonControl() + control.verbose = false + control.Δt_min = tstep + control.Δt = tstep + control.Δt_grow = 1.1 + control.Δt_max = 0.1 + control.Δu_opt = 0.1 + control.damp_initial = 0.5 + + tsol = solve(sys; + method_linear = UMFPACKFactorization(), + inival, + times = [0.0, 10], + control = control,) + + vis = GridVisualizer(; Plotter = Plotter, layout = (1, 1), fast = true) + for log10t = -4:0.025:0 + time = 10^(log10t) + sol = tsol(time) + scalarplot!(vis[1, 1], + grid, + sol[iphi, :]; + label = "ϕ", + title = @sprintf("time=%.3g", time), + flimits = (0, 5), + color = :green,) + scalarplot!(vis[1, 1], + grid, + sol[ic, :]; + label = "c", + flimits = (0, 5), + clear = false, + color = :red,) + reveal(vis) + end + return sum(tsol[end]) + + else # Calculate double layer capacitance + U = unknowns(sys) + control = VoronoiFVM.NewtonControl() + control.damp_initial = 1.0e-5 + delta = 1.0e-4 + @views inival[iphi, :] .= 0 + @views inival[ic, :] .= 0.5 + sys.boundary_values[iphi, 1] = 0 + + delta = 1.0e-4 + vplus = zeros(0) + cdlplus = zeros(0) + vminus = zeros(0) + cdlminus = zeros(0) + cdl = 0.0 + vis = GridVisualizer(; Plotter = Plotter, layout = (2, 1), fast = true) + for dir in [1, -1] + phi = 0.0 + while phi < phimax + data.V = dir * phi + U = solve(sys; inival = U, control, time = 1.0) + Q = integrate(sys, physics.reaction, U) + data.V = dir * phi + delta + U = solve(sys; inival = U, control, time = 1.0) + Qdelta = integrate(sys, physics.reaction, U) + cdl = (Qdelta[iphi] - Q[iphi]) / delta + + if Plotter != nothing + scalarplot!(vis[1, 1], + grid, + U[iphi, :]; + label = "ϕ", + title = @sprintf("Δϕ=%.3g", phi), + flimits = (-5, 5), + clear = true, + color = :green,) + scalarplot!(vis[1, 1], + grid, + U[ic, :]; + label = "c", + flimits = (0, 5), + clear = false, + color = :red,) + end + if dir == 1 + push!(vplus, dir * phi) + push!(cdlplus, cdl) + else + push!(vminus, dir * phi) + push!(cdlminus, cdl) + end + + if Plotter != nothing + scalarplot!(vis[2, 1], [0, 1.0e-1], [0, 0.05]; color = :white, clear = true) + end + v = vcat(reverse(vminus), vplus) + c = vcat(reverse(cdlminus), cdlplus) + if length(v) >= 2 + scalarplot!(vis[2, 1], + v, + c; + color = :green, + clear = false, + title = "C_dl",) + end + + phi += dphi + reveal(vis) + end + end + + return cdl + end +end + +using Test +function runtests() + + + evolval = 18.721369939565655 + dlcapval = 0.025657355479449806 + rtol = 1.0e-5 + @test isapprox(main(; unknown_storage = :sparse, dlcap = false, assembly = :edgewise), + evolval; + rtol = rtol,) + @test isapprox(main(; unknown_storage = :sparse, dlcap = true, assembly = :edgewise), + dlcapval; + rtol = rtol,) + @test isapprox(main(; unknown_storage = :dense, dlcap = false, assembly = :edgewise), + evolval; + rtol = rtol,) + @test isapprox(main(; unknown_storage = :dense, dlcap = true, assembly = :edgewise), + dlcapval; + rtol = rtol,) + @test isapprox(main(; unknown_storage = :sparse, dlcap = false, assembly = :cellwise), + evolval; + rtol = rtol,) + @test isapprox(main(; unknown_storage = :sparse, dlcap = true, assembly = :cellwise), + dlcapval; + rtol = rtol,) + @test isapprox(main(; unknown_storage = :dense, dlcap = false, assembly = :cellwise), + evolval; + rtol = rtol,) + @test isapprox(main(; unknown_storage = :dense, dlcap = true, assembly = :cellwise), + dlcapval; + rtol = rtol,) +end +end diff --git a/v1.19.1/module_examples/Example160_UnipolarDriftDiffusion1D/index.html b/v1.19.1/module_examples/Example160_UnipolarDriftDiffusion1D/index.html new file mode 100644 index 000000000..d809ef85f --- /dev/null +++ b/v1.19.1/module_examples/Example160_UnipolarDriftDiffusion1D/index.html @@ -0,0 +1,244 @@ + +160: Unipolar degenerate drift-diffusion · VoronoiFVM.jl

    160: Unipolar degenerate drift-diffusion

    (source code)

    See: C. Cancès, C. Chainais-Hillairet, J. Fuhrmann, and B. Gaudeul, "A numerical-analysis-focused comparison of several finite volume schemes for a unipolar degenerate drift-diffusion model" IMA Journal of Numerical Analysis, vol. 41, no. 1, pp. 271–314, 2021.

    Available from https://doi.org/10.1093/imanum/draa002, the preprint is on arxiv1907.11126.

    The problem consists of a Poisson equation for the electrostatic potential $\phi$:

    \[-\nabla \varepsilon \nabla \phi = z(2c-1)\]

    and a degenerate drift-diffusion equation of the transport of a charged species $c$:

    \[\partial_t u - \nabla\cdot \left(\nabla c + c \nabla (\phi - \log (1-c) )\right)\]

    In particular, the paper, among others, investigates the "sedan" flux discretization which is able to handle the degeneracy coming from the $\log (1-c)$ term. The earliest reference to this scheme we found in the source code of the SEDAN III semiconductor device simulator.

    module Example160_UnipolarDriftDiffusion1D
    +
    +using Printf
    +
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +using LinearSolve
    +
    +mutable struct Data
    +    eps::Float64
    +    z::Float64
    +    ic::Int32
    +    iphi::Int32
    +    V::Float64
    +    Data() = new()
    +end
    +
    +function classflux!(f, u, edge, data)
    +    ic = data.ic
    +    iphi = data.iphi
    +    f[iphi] = data.eps * (u[iphi, 1] - u[iphi, 2])
    +    bp, bm = fbernoulli_pm(u[iphi, 1] - u[iphi, 2])
    +    f[ic] = bm * u[ic, 1] - bp * u[ic, 2]
    +end
    +
    +function storage!(f, u, node, data)
    +    ic = data.ic
    +    iphi = data.iphi
    +    f[iphi] = 0
    +    f[ic] = u[ic]
    +end
    +
    +function reaction!(f, u, node, data)
    +    ic = data.ic
    +    iphi = data.iphi
    +    f[iphi] = data.z * (1 - 2 * u[ic])
    +    f[ic] = 0
    +end
    +const eps_reg=1.0e-10
    +function sedanflux!(f, u, edge, data)
    +    ic = data.ic
    +    iphi = data.iphi
    +    f[iphi] = data.eps * (u[iphi, 1] - u[iphi, 2])
    +    mu1 = -log1p(-u[ic, 1]+eps_reg)
    +    mu2 = -log1p(-u[ic, 2]+eps_reg)
    +    bp, bm = fbernoulli_pm(data.z * 2 * (u[iphi, 1] - u[iphi, 2]) + (mu1 - mu2))
    +    f[ic] = bm * u[ic, 1] - bp * u[ic, 2]
    +end
    +
    +function bcondition!(f, u, bnode, data)
    +    V = ramp(bnode.time; dt = (0, 1.0e-2), du = (0, data.V))
    +    boundary_dirichlet!(f, u, bnode; species = data.iphi, region = 1, value = V)
    +    boundary_dirichlet!(f, u, bnode; species = data.iphi, region = 2, value = 0)
    +    boundary_dirichlet!(f, u, bnode; species = data.ic, region = 2, value = 0.5)
    +end
    +
    +function main(;
    +              n = 20,
    +              Plotter = nothing,
    +              dlcap = false,
    +              verbose = false,
    +              phimax = 1,
    +              dphi = 1.0e-1,
    +              unknown_storage = :sparse,
    +              assembly = :edgewise,)
    +    h = 1.0 / convert(Float64, n)
    +    grid = VoronoiFVM.Grid(collect(0:h:1))
    +
    +    data = Data()
    +    data.eps = 1.0e-3
    +    data.z = -1
    +    data.iphi = 1
    +    data.ic = 2
    +    data.V = 5
    +    ic = data.ic
    +    iphi = data.iphi
    +
    +    physics = VoronoiFVM.Physics(;
    +                                 data = data,
    +                                 flux = sedanflux!,
    +                                 reaction = reaction!,
    +                                 breaction = bcondition!,
    +                                 storage = storage!,)
    +
    +    sys = VoronoiFVM.System(grid,
    +                            physics;
    +                            unknown_storage = unknown_storage,
    +                            species = [1, 2],
    +                            assembly = assembly,)
    +
    +    inival = unknowns(sys)
    +    @views inival[iphi, :] .= 0
    +    @views inival[ic, :] .= 0.5
    +
    +    if !dlcap
    +        # Create solver control info for constant time step size
    +        tstep = 1.0e-5
    +        control = VoronoiFVM.NewtonControl()
    +        control.verbose = false
    +        control.Δt_min = tstep
    +        control.Δt = tstep
    +        control.Δt_grow = 1.1
    +        control.Δt_max = 0.1
    +        control.Δu_opt = 0.1
    +        control.damp_initial = 0.5
    +
    +        tsol = solve(sys;
    +                     method_linear = UMFPACKFactorization(),
    +                     inival,
    +                     times = [0.0, 10],
    +                     control = control,)
    +
    +        vis = GridVisualizer(; Plotter = Plotter, layout = (1, 1), fast = true)
    +        for log10t = -4:0.025:0
    +            time = 10^(log10t)
    +            sol = tsol(time)
    +            scalarplot!(vis[1, 1],
    +                        grid,
    +                        sol[iphi, :];
    +                        label = "ϕ",
    +                        title = @sprintf("time=%.3g", time),
    +                        flimits = (0, 5),
    +                        color = :green,)
    +            scalarplot!(vis[1, 1],
    +                        grid,
    +                        sol[ic, :];
    +                        label = "c",
    +                        flimits = (0, 5),
    +                        clear = false,
    +                        color = :red,)
    +            reveal(vis)
    +        end
    +        return sum(tsol[end])
    +
    +    else  # Calculate double layer capacitance
    +        U = unknowns(sys)
    +        control = VoronoiFVM.NewtonControl()
    +        control.damp_initial = 1.0e-5
    +        delta = 1.0e-4
    +        @views inival[iphi, :] .= 0
    +        @views inival[ic, :] .= 0.5
    +        sys.boundary_values[iphi, 1] = 0
    +
    +        delta = 1.0e-4
    +        vplus = zeros(0)
    +        cdlplus = zeros(0)
    +        vminus = zeros(0)
    +        cdlminus = zeros(0)
    +        cdl = 0.0
    +        vis = GridVisualizer(; Plotter = Plotter, layout = (2, 1), fast = true)
    +        for dir in [1, -1]
    +            phi = 0.0
    +            while phi < phimax
    +                data.V = dir * phi
    +                U = solve(sys; inival = U, control, time = 1.0)
    +                Q = integrate(sys, physics.reaction, U)
    +                data.V = dir * phi + delta
    +                U = solve(sys; inival = U, control, time = 1.0)
    +                Qdelta = integrate(sys, physics.reaction, U)
    +                cdl = (Qdelta[iphi] - Q[iphi]) / delta
    +
    +                if Plotter != nothing
    +                    scalarplot!(vis[1, 1],
    +                                grid,
    +                                U[iphi, :];
    +                                label = "ϕ",
    +                                title = @sprintf("Δϕ=%.3g", phi),
    +                                flimits = (-5, 5),
    +                                clear = true,
    +                                color = :green,)
    +                    scalarplot!(vis[1, 1],
    +                                grid,
    +                                U[ic, :];
    +                                label = "c",
    +                                flimits = (0, 5),
    +                                clear = false,
    +                                color = :red,)
    +                end
    +                if dir == 1
    +                    push!(vplus, dir * phi)
    +                    push!(cdlplus, cdl)
    +                else
    +                    push!(vminus, dir * phi)
    +                    push!(cdlminus, cdl)
    +                end
    +
    +                if Plotter != nothing
    +                    scalarplot!(vis[2, 1], [0, 1.0e-1], [0, 0.05]; color = :white, clear = true)
    +                end
    +                v = vcat(reverse(vminus), vplus)
    +                c = vcat(reverse(cdlminus), cdlplus)
    +                if length(v) >= 2
    +                    scalarplot!(vis[2, 1],
    +                                v,
    +                                c;
    +                                color = :green,
    +                                clear = false,
    +                                title = "C_dl",)
    +                end
    +
    +                phi += dphi
    +                reveal(vis)
    +            end
    +        end
    +
    +        return cdl
    +    end
    +end
    +
    +using Test
    +function runtests()
    +
    +
    +    evolval = 18.721369939565655
    +    dlcapval = 0.025657355479449806
    +    rtol = 1.0e-5
    +    @test isapprox(main(; unknown_storage = :sparse, dlcap = false, assembly = :edgewise),
    +                   evolval;
    +                   rtol = rtol,)
    +    @test isapprox(main(; unknown_storage = :sparse, dlcap = true, assembly = :edgewise),
    +                   dlcapval;
    +                   rtol = rtol,)
    +    @test isapprox(main(; unknown_storage = :dense, dlcap = false, assembly = :edgewise),
    +                   evolval;
    +                   rtol = rtol,)
    +    @test isapprox(main(; unknown_storage = :dense, dlcap = true, assembly = :edgewise),
    +                   dlcapval;
    +                   rtol = rtol,)
    +    @test isapprox(main(; unknown_storage = :sparse, dlcap = false, assembly = :cellwise),
    +                   evolval;
    +                   rtol = rtol,)
    +    @test isapprox(main(; unknown_storage = :sparse, dlcap = true, assembly = :cellwise),
    +                   dlcapval;
    +                   rtol = rtol,)
    +    @test isapprox(main(; unknown_storage = :dense, dlcap = false, assembly = :cellwise),
    +                   evolval;
    +                   rtol = rtol,)
    +    @test isapprox(main(; unknown_storage = :dense, dlcap = true, assembly = :cellwise),
    +                   dlcapval;
    +                   rtol = rtol,)
    +end
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example201_Laplace2D.jl b/v1.19.1/module_examples/Example201_Laplace2D.jl new file mode 100644 index 000000000..2cfc44557 --- /dev/null +++ b/v1.19.1/module_examples/Example201_Laplace2D.jl @@ -0,0 +1,49 @@ +#= + +# 201: 2D Laplace equation +([source code](@__SOURCE_URL__)) + +=# + +module Example201_Laplace2D + +using VoronoiFVM, ExtendableGrids +using GridVisualize +using LinearAlgebra +## Flux function which describes the flux +## between neighboring control volumes +function g!(f, u, edge) + f[1] = u[1, 1] - u[1, 2] +end + +function main(; Plotter = nothing, n = 5, is_linear = true, assembly = :edgewise) + nspecies = 1 + ispec = 1 + X = collect(0:(1.0 / n):1) + grid = VoronoiFVM.Grid(X, X) + + physics = VoronoiFVM.Physics(; flux = g!) + sys = VoronoiFVM.System(grid, physics; is_linear = is_linear, assembly = assembly) + enable_species!(sys, ispec, [1]) + boundary_dirichlet!(sys, ispec, 1, 0.0) + boundary_dirichlet!(sys, ispec, 3, 1.0) + solution = solve(sys; inival = 0) + nf = nodeflux(sys, solution) + vis = GridVisualizer(; Plotter = Plotter) + scalarplot!(vis, grid, solution[1, :]; clear = true, colormap = :summer) + vectorplot!(vis, grid, nf[:, 1, :]; clear = false, spacing = 0.1, vscale = 0.5) + reveal(vis) + return norm(solution) + norm(nf) +end + +## Called by unit test + +using Test +function runtests() + testval = 9.63318042491699 + + @test main(; assembly = :edgewise) ≈ testval && + main(; assembly = :cellwise) ≈ testval +end + +end diff --git a/v1.19.1/module_examples/Example201_Laplace2D/index.html b/v1.19.1/module_examples/Example201_Laplace2D/index.html new file mode 100644 index 000000000..6ff14ca9f --- /dev/null +++ b/v1.19.1/module_examples/Example201_Laplace2D/index.html @@ -0,0 +1,43 @@ + +201: 2D Laplace equation · VoronoiFVM.jl

    201: 2D Laplace equation

    (source code)

    module Example201_Laplace2D
    +
    +using VoronoiFVM, ExtendableGrids
    +using GridVisualize
    +using LinearAlgebra
    +# Flux function which describes the flux
    +# between neighboring control volumes
    +function g!(f, u, edge)
    +    f[1] = u[1, 1] - u[1, 2]
    +end
    +
    +function main(; Plotter = nothing, n = 5, is_linear = true, assembly = :edgewise)
    +    nspecies = 1
    +    ispec = 1
    +    X = collect(0:(1.0 / n):1)
    +    grid = VoronoiFVM.Grid(X, X)
    +
    +    physics = VoronoiFVM.Physics(; flux = g!)
    +    sys = VoronoiFVM.System(grid, physics; is_linear = is_linear, assembly = assembly)
    +    enable_species!(sys, ispec, [1])
    +    boundary_dirichlet!(sys, ispec, 1, 0.0)
    +    boundary_dirichlet!(sys, ispec, 3, 1.0)
    +    solution = solve(sys; inival = 0)
    +    nf = nodeflux(sys, solution)
    +    vis = GridVisualizer(; Plotter = Plotter)
    +    scalarplot!(vis, grid, solution[1, :]; clear = true, colormap = :summer)
    +    vectorplot!(vis, grid, nf[:, 1, :]; clear = false, spacing = 0.1, vscale = 0.5)
    +    reveal(vis)
    +    return norm(solution) + norm(nf)
    +end
    +
    +# Called by unit test
    +
    +using Test
    +function runtests()
    +    testval = 9.63318042491699
    +
    +    @test main(; assembly = :edgewise) ≈ testval &&
    +          main(; assembly = :cellwise) ≈ testval
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example203_CoordinateSystems.jl b/v1.19.1/module_examples/Example203_CoordinateSystems.jl new file mode 100644 index 000000000..51c1c52ba --- /dev/null +++ b/v1.19.1/module_examples/Example203_CoordinateSystems.jl @@ -0,0 +1,255 @@ +#= + +# 203: Various coordinate systems +([source code](@__SOURCE_URL__)) +=# + +module Example203_CoordinateSystems + +using VoronoiFVM +using LinearAlgebra +using ExtendableGrids +using GridVisualize + +function plot(grid, numerical, exact, Plotter) + vis = GridVisualizer(; Plotter = Plotter, layout = (2, 1)) + scalarplot!(vis[1, 1], grid, numerical[1, :]; title = "numerical") + scalarplot!(vis[2, 1], grid, exact; title = "exact", show = true) +end + +function flux(f, u, edge) + f[1] = u[1, 1] - u[1, 2] +end + +""" + symlapdisk(r,r2) + +Exact solution of homogeneous Dirichlet problem `-Δu=1` on disk of radius r2. +""" +symlapdisk(r, r2) = 0.25 * (r2^2 - r^2) + +""" + maindisk(;nref=0, r2=5.0, Plotter=nothing) + +Solve homogeneuous Dirichlet problem `-Δu=1` +on disk of radius r2, exact solution is `(r_2^2-r^2)/4`. + +In this case, the discretization appears to be exact. +""" +function maindisk(; nref = 0, r2 = 5.0, Plotter = nothing, assembly = :edgewise) + h = 0.1 * 2.0^(-nref) + R = collect(0:h:r2) + grid = VoronoiFVM.Grid(R) + circular_symmetric!(grid) + source(f, node) = f[1] = 1.0 + sys = VoronoiFVM.System(grid; source, flux, species = [1], assembly = assembly) + boundary_dirichlet!(sys; species = 1, region = 2, value = 0.0) + sol = solve(sys) + exact = symlapdisk.(coordinates(grid)[1, :], r2) + plot(grid, sol, exact, Plotter) + norm(sol[1, :] - exact, Inf) < 1.0e-14 +end + +""" + maincylinder(;nref=0, r2=5.0, z1=0, z2=1, Plotter=nothing) + +Solve homogeneuous Dirichlet problem `-Δu=1` +on disk of radius r2, exact solution is `(r_2^2-r^2)/4`. + +In this case, the discretization appears to be exact. +""" +function maincylinder(; + nref = 0, + r2 = 5.0, + z1 = 0.0, + z2 = 1.0, + Plotter = nothing, + assembly = :edgewise,) + h = 0.1 * 2.0^(-nref) + R = collect(0:h:r2) + Z = collect(z1:h:z2) + grid = VoronoiFVM.Grid(R, Z) + circular_symmetric!(grid) + source(f, node) = f[1] = 1.0 + sys = VoronoiFVM.System(grid; source, flux, species = [1], assembly = assembly) + boundary_dirichlet!(sys; species = 1, region = 2, value = 0.0) + sol = solve(sys) + exact = symlapdisk.(coordinates(grid)[1, :], r2) + plot(grid, sol, exact, Plotter) + norm(sol[1, :] - exact, Inf) < 1.0e-14 +end + +""" + maincylinder_unstruct(;Plotter=nothing) + +Solve homogeneuous Dirichlet problem `-Δu=1` +on disk of radius r2, exact solution is `(r_2^2-r^2)/4`. + +In this case, the discretization appears to be exact. +""" +function maincylinder_unstruct(; + Plotter = nothing, + assembly = :edgewise) + if VERSION < v"1.7" + # no pkdir + return true + end + nref = 0 + r2 = 5.0 + z1 = 0.0 + z2 = 1.0 + h = 0.1 * 2.0^(-nref) + grid = simplexgrid(joinpath(pkgdir(VoronoiFVM), "assets", "cyl_unstruct.sg")) + circular_symmetric!(grid) + source(f, node) = f[1] = 1.0 + sys = VoronoiFVM.System(grid; source, flux, species = [1], assembly = assembly) + boundary_dirichlet!(sys; species = 1, region = 2, value = 0.0) + sol = solve(sys) + exact = symlapdisk.(coordinates(grid)[1, :], r2) + plot(grid, sol, exact, Plotter) + norm(sol[1, :] - exact, Inf) < 0.0012 +end + +""" + symlapring(r,r1,r2) + +Exact solution of Dirichlet problem `-Δu=0` on ring between radii r1 and r2, +with boundary value 1 at r1 and 0 at r2. +""" +symlapring(r, r1, r2) = (log(r) - log(r2)) / (log(r1) - log(r2)) + +""" + mainring(;nref=0, r2=5.0, Plotter=nothing) + +of Dirichlet problem `-Δu=0` on ring between radii r1 and r2, +with boundary value 1 at r1 and 0 at r2. Test of quadratic convergence. +""" +function mainring(; nref = 0, r1 = 1.0, r2 = 5.0, Plotter = nothing, assembly = :edgewise) + h = 0.1 * 2.0^(-nref) + R = collect(r1:h:r2) + grid = VoronoiFVM.Grid(R) + circular_symmetric!(grid) + source(f, node) = f[1] = 0.0 + sys = VoronoiFVM.System(grid; source, flux, species = [1], assembly = assembly) + boundary_dirichlet!(sys; species = 1, region = 1, value = 1.0) + boundary_dirichlet!(sys; species = 1, region = 2, value = 0.0) + sol = solve(sys) + exact = symlapring.(coordinates(grid)[1, :], r1, r2) + plot(grid, sol, exact, Plotter) + norm(sol[1, :] - exact, Inf) / h^2 < 0.01 +end + +""" + maincylindershell(;nref=0, r2=5.0, z1=0.0, z2=1.0, Plotter=nothing) + +of Dirichlet problem `-Δu=0` on cylindershell between radii r1 and r2, +with boundary value 1 at r1 and 0 at r2. Test of quadratic convergence. +""" +function maincylindershell(; + nref = 0, + r1 = 1.0, + r2 = 5.0, + z1 = 0.0, + z2 = 1.0, + Plotter = nothing, + assembly = :edgewise,) + h = 0.1 * 2.0^(-nref) + R = collect(r1:h:r2) + Z = collect(z1:h:z2) + grid = VoronoiFVM.Grid(R, Z) + circular_symmetric!(grid) + source(f, node) = f[1] = 0.0 + sys = VoronoiFVM.System(grid; source, flux, species = [1], assembly = assembly) + boundary_dirichlet!(sys; species = 1, region = 4, value = 1.0) + boundary_dirichlet!(sys; species = 1, region = 2, value = 0.0) + sol = solve(sys) + exact = symlapring.(coordinates(grid)[1, :], r1, r2) + plot(grid, sol, exact, Plotter) + norm(sol[1, :] - exact, Inf) / h^2 < 0.01 +end + +""" + symlapsphere(r,r2) + +Exact solution of homogeneous Dirichlet problem `-Δu=1` on sphere of radius r2. +""" +symlapsphere(r, r2) = (r2^2 - r^2) / 6.0 + +""" + mainsphere(;nref=0, r2=5.0, Plotter=nothing) + +Solve homogeneuous Dirichlet problem `-Δu=1` +on sphere of radius r2, exact solution is `(r_2^2-r^2)/4`. + +In this case, the discretization appears to be exact. +""" +function mainsphere(; nref = 0, r2 = 5.0, Plotter = nothing, assembly = :edgewise) + h = 0.1 * 2.0^(-nref) + R = collect(0:h:r2) + grid = VoronoiFVM.Grid(R) + spherical_symmetric!(grid) + source(f, node) = f[1] = 1.0 + sys = VoronoiFVM.System(grid; source, flux, species = [1], assembly = assembly) + boundary_dirichlet!(sys; species = 1, region = 2, value = 0.0) + sol = solve(sys) + exact = symlapsphere.(coordinates(grid)[1, :], r2) + plot(grid, sol, exact, Plotter) + norm(sol[1, :] - exact, Inf) < 1.0e-14 +end + +""" + symlapsphereshell(r,r1,r2) + +Exact solution of Dirichlet problem `-Δu=0` on sphereshell between radii r1 and r2, +with boundary value 1 at r1 and 0 at r2. +""" +symlapsphereshell(r, r1, r2) = (r2 * r1 / r - r1) / (r2 - r1) + +""" + mainsphereshell(;nref=0, r2=5.0, Plotter=nothing) + +of Dirichlet problem `-Δu=0` on sphereshell between radii r1 and r2, +with boundary value 1 at r1 and 0 at r2. Test of quadratic convergence. +""" +function mainsphereshell(; + nref = 0, + r1 = 1.0, + r2 = 5.0, + Plotter = nothing, + assembly = :edgewise,) + h = 0.1 * 2.0^(-nref) + R = collect(r1:h:r2) + grid = VoronoiFVM.Grid(R) + spherical_symmetric!(grid) + source(f, node) = f[1] = 0.0 + sys = VoronoiFVM.System(grid; source, flux, species = [1], assembly = assembly) + boundary_dirichlet!(sys; species = 1, region = 1, value = 1.0) + boundary_dirichlet!(sys; species = 1, region = 2, value = 0.0) + sol = solve(sys) + exact = symlapsphereshell.(coordinates(grid)[1, :], r1, r2) + plot(grid, sol, exact, Plotter) + norm(sol[1, :] - exact, Inf) / h^2 < 0.04 +end + +# +# Called by unit test + +using Test# +function runtests() + @test maindisk(; assembly = :edgewise) && + mainring(; assembly = :edgewise) && + maincylinder(; assembly = :edgewise) && + maincylinder_unstruct(; assembly = :edgewise) && + maincylindershell(; assembly = :edgewise) && + mainsphere(; assembly = :edgewise) && + mainsphereshell(; assembly = :edgewise) && + maindisk(; assembly = :cellwise) && + mainring(; assembly = :cellwise) && + maincylinder(; assembly = :cellwise) && + maincylinder_unstruct(; assembly = :cellwise) && + maincylindershell(; assembly = :cellwise) && + mainsphere(; assembly = :cellwise) && + mainsphereshell(; assembly = :cellwise) +end + +end diff --git a/v1.19.1/module_examples/Example203_CoordinateSystems/index.html b/v1.19.1/module_examples/Example203_CoordinateSystems/index.html new file mode 100644 index 000000000..d6f943d37 --- /dev/null +++ b/v1.19.1/module_examples/Example203_CoordinateSystems/index.html @@ -0,0 +1,243 @@ + +203: Various coordinate systems · VoronoiFVM.jl

    203: Various coordinate systems

    (source code)

    module Example203_CoordinateSystems
    +
    +using VoronoiFVM
    +using LinearAlgebra
    +using ExtendableGrids
    +using GridVisualize
    +
    +function plot(grid, numerical, exact, Plotter)
    +    vis = GridVisualizer(; Plotter = Plotter, layout = (2, 1))
    +    scalarplot!(vis[1, 1], grid, numerical[1, :]; title = "numerical")
    +    scalarplot!(vis[2, 1], grid, exact; title = "exact", show = true)
    +end
    +
    +function flux(f, u, edge)
    +    f[1] = u[1, 1] - u[1, 2]
    +end
    +
    +"""
    +    symlapdisk(r,r2)
    +
    +Exact solution of homogeneous Dirichlet problem `-Δu=1` on disk of radius r2.
    +"""
    +symlapdisk(r, r2) = 0.25 * (r2^2 - r^2)
    +
    +"""
    +    maindisk(;nref=0, r2=5.0, Plotter=nothing)
    +
    +Solve homogeneuous Dirichlet problem  `-Δu=1`
    +on disk of radius r2, exact solution is `(r_2^2-r^2)/4`.
    +
    +In this case, the discretization appears to be exact.
    +"""
    +function maindisk(; nref = 0, r2 = 5.0, Plotter = nothing, assembly = :edgewise)
    +    h = 0.1 * 2.0^(-nref)
    +    R = collect(0:h:r2)
    +    grid = VoronoiFVM.Grid(R)
    +    circular_symmetric!(grid)
    +    source(f, node) = f[1] = 1.0
    +    sys = VoronoiFVM.System(grid; source, flux, species = [1], assembly = assembly)
    +    boundary_dirichlet!(sys; species = 1, region = 2, value = 0.0)
    +    sol = solve(sys)
    +    exact = symlapdisk.(coordinates(grid)[1, :], r2)
    +    plot(grid, sol, exact, Plotter)
    +    norm(sol[1, :] - exact, Inf) < 1.0e-14
    +end
    +
    +"""
    +    maincylinder(;nref=0, r2=5.0, z1=0, z2=1, Plotter=nothing)
    +
    +Solve homogeneuous Dirichlet problem  `-Δu=1`
    +on disk of radius r2, exact solution is `(r_2^2-r^2)/4`.
    +
    +In this case, the discretization appears to be exact.
    +"""
    +function maincylinder(;
    +                      nref = 0,
    +                      r2 = 5.0,
    +                      z1 = 0.0,
    +                      z2 = 1.0,
    +                      Plotter = nothing,
    +                      assembly = :edgewise,)
    +    h = 0.1 * 2.0^(-nref)
    +    R = collect(0:h:r2)
    +    Z = collect(z1:h:z2)
    +    grid = VoronoiFVM.Grid(R, Z)
    +    circular_symmetric!(grid)
    +    source(f, node) = f[1] = 1.0
    +    sys = VoronoiFVM.System(grid; source, flux, species = [1], assembly = assembly)
    +    boundary_dirichlet!(sys; species = 1, region = 2, value = 0.0)
    +    sol = solve(sys)
    +    exact = symlapdisk.(coordinates(grid)[1, :], r2)
    +    plot(grid, sol, exact, Plotter)
    +    norm(sol[1, :] - exact, Inf) < 1.0e-14
    +end
    +
    +"""
    +    maincylinder_unstruct(;Plotter=nothing)
    +
    +Solve homogeneuous Dirichlet problem  `-Δu=1`
    +on disk of radius r2, exact solution is `(r_2^2-r^2)/4`.
    +
    +In this case, the discretization appears to be exact.
    +"""
    +function maincylinder_unstruct(;
    +                               Plotter = nothing,
    +                               assembly = :edgewise)
    +    if VERSION < v"1.7"

    no pkdir

            return true
    +    end
    +    nref = 0
    +    r2 = 5.0
    +    z1 = 0.0
    +    z2 = 1.0
    +    h = 0.1 * 2.0^(-nref)
    +    grid = simplexgrid(joinpath(pkgdir(VoronoiFVM), "assets", "cyl_unstruct.sg"))
    +    circular_symmetric!(grid)
    +    source(f, node) = f[1] = 1.0
    +    sys = VoronoiFVM.System(grid; source, flux, species = [1], assembly = assembly)
    +    boundary_dirichlet!(sys; species = 1, region = 2, value = 0.0)
    +    sol = solve(sys)
    +    exact = symlapdisk.(coordinates(grid)[1, :], r2)
    +    plot(grid, sol, exact, Plotter)
    +    norm(sol[1, :] - exact, Inf) < 0.0012
    +end
    +
    +"""
    +    symlapring(r,r1,r2)
    +
    +Exact solution of Dirichlet problem `-Δu=0` on ring between radii r1 and r2,
    +with boundary value 1 at r1 and 0 at r2.
    +"""
    +symlapring(r, r1, r2) = (log(r) - log(r2)) / (log(r1) - log(r2))
    +
    +"""
    +    mainring(;nref=0, r2=5.0, Plotter=nothing)
    +
    +of Dirichlet problem `-Δu=0` on ring between radii r1 and r2,
    +with boundary value 1 at r1 and 0 at r2. Test of quadratic convergence.
    +"""
    +function mainring(; nref = 0, r1 = 1.0, r2 = 5.0, Plotter = nothing, assembly = :edgewise)
    +    h = 0.1 * 2.0^(-nref)
    +    R = collect(r1:h:r2)
    +    grid = VoronoiFVM.Grid(R)
    +    circular_symmetric!(grid)
    +    source(f, node) = f[1] = 0.0
    +    sys = VoronoiFVM.System(grid; source, flux, species = [1], assembly = assembly)
    +    boundary_dirichlet!(sys; species = 1, region = 1, value = 1.0)
    +    boundary_dirichlet!(sys; species = 1, region = 2, value = 0.0)
    +    sol = solve(sys)
    +    exact = symlapring.(coordinates(grid)[1, :], r1, r2)
    +    plot(grid, sol, exact, Plotter)
    +    norm(sol[1, :] - exact, Inf) / h^2 < 0.01
    +end
    +
    +"""
    +    maincylindershell(;nref=0, r2=5.0, z1=0.0, z2=1.0, Plotter=nothing)
    +
    +of Dirichlet problem `-Δu=0` on cylindershell between radii r1 and r2,
    +with boundary value 1 at r1 and 0 at r2. Test of quadratic convergence.
    +"""
    +function maincylindershell(;
    +                           nref = 0,
    +                           r1 = 1.0,
    +                           r2 = 5.0,
    +                           z1 = 0.0,
    +                           z2 = 1.0,
    +                           Plotter = nothing,
    +                           assembly = :edgewise,)
    +    h = 0.1 * 2.0^(-nref)
    +    R = collect(r1:h:r2)
    +    Z = collect(z1:h:z2)
    +    grid = VoronoiFVM.Grid(R, Z)
    +    circular_symmetric!(grid)
    +    source(f, node) = f[1] = 0.0
    +    sys = VoronoiFVM.System(grid; source, flux, species = [1], assembly = assembly)
    +    boundary_dirichlet!(sys; species = 1, region = 4, value = 1.0)
    +    boundary_dirichlet!(sys; species = 1, region = 2, value = 0.0)
    +    sol = solve(sys)
    +    exact = symlapring.(coordinates(grid)[1, :], r1, r2)
    +    plot(grid, sol, exact, Plotter)
    +    norm(sol[1, :] - exact, Inf) / h^2 < 0.01
    +end
    +
    +"""
    +    symlapsphere(r,r2)
    +
    +Exact solution of homogeneous Dirichlet problem `-Δu=1` on sphere of radius r2.
    +"""
    +symlapsphere(r, r2) = (r2^2 - r^2) / 6.0
    +
    +"""
    +    mainsphere(;nref=0, r2=5.0, Plotter=nothing)
    +
    +Solve homogeneuous Dirichlet problem  `-Δu=1`
    +on sphere of radius r2, exact solution is `(r_2^2-r^2)/4`.
    +
    +In this case, the discretization appears to be exact.
    +"""
    +function mainsphere(; nref = 0, r2 = 5.0, Plotter = nothing, assembly = :edgewise)
    +    h = 0.1 * 2.0^(-nref)
    +    R = collect(0:h:r2)
    +    grid = VoronoiFVM.Grid(R)
    +    spherical_symmetric!(grid)
    +    source(f, node) = f[1] = 1.0
    +    sys = VoronoiFVM.System(grid; source, flux, species = [1], assembly = assembly)
    +    boundary_dirichlet!(sys; species = 1, region = 2, value = 0.0)
    +    sol = solve(sys)
    +    exact = symlapsphere.(coordinates(grid)[1, :], r2)
    +    plot(grid, sol, exact, Plotter)
    +    norm(sol[1, :] - exact, Inf) < 1.0e-14
    +end
    +
    +"""
    +    symlapsphereshell(r,r1,r2)
    +
    +Exact solution of Dirichlet problem `-Δu=0` on sphereshell between radii r1 and r2,
    +with boundary value 1 at r1 and 0 at r2.
    +"""
    +symlapsphereshell(r, r1, r2) = (r2 * r1 / r - r1) / (r2 - r1)
    +
    +"""
    +    mainsphereshell(;nref=0, r2=5.0, Plotter=nothing)
    +
    +of Dirichlet problem `-Δu=0` on sphereshell between radii r1 and r2,
    +with boundary value 1 at r1 and 0 at r2. Test of quadratic convergence.
    +"""
    +function mainsphereshell(;
    +                         nref = 0,
    +                         r1 = 1.0,
    +                         r2 = 5.0,
    +                         Plotter = nothing,
    +                         assembly = :edgewise,)
    +    h = 0.1 * 2.0^(-nref)
    +    R = collect(r1:h:r2)
    +    grid = VoronoiFVM.Grid(R)
    +    spherical_symmetric!(grid)
    +    source(f, node) = f[1] = 0.0
    +    sys = VoronoiFVM.System(grid; source, flux, species = [1], assembly = assembly)
    +    boundary_dirichlet!(sys; species = 1, region = 1, value = 1.0)
    +    boundary_dirichlet!(sys; species = 1, region = 2, value = 0.0)
    +    sol = solve(sys)
    +    exact = symlapsphereshell.(coordinates(grid)[1, :], r1, r2)
    +    plot(grid, sol, exact, Plotter)
    +    norm(sol[1, :] - exact, Inf) / h^2 < 0.04
    +end

    Called by unit test

    using Test#
    +function runtests()
    +    @test maindisk(; assembly = :edgewise) &&
    +          mainring(; assembly = :edgewise) &&
    +          maincylinder(; assembly = :edgewise) &&
    +          maincylinder_unstruct(; assembly = :edgewise) &&
    +          maincylindershell(; assembly = :edgewise) &&
    +          mainsphere(; assembly = :edgewise) &&
    +          mainsphereshell(; assembly = :edgewise) &&
    +          maindisk(; assembly = :cellwise) &&
    +          mainring(; assembly = :cellwise) &&
    +          maincylinder(; assembly = :cellwise) &&
    +          maincylinder_unstruct(; assembly = :cellwise) &&
    +          maincylindershell(; assembly = :cellwise) &&
    +          mainsphere(; assembly = :cellwise) &&
    +          mainsphereshell(; assembly = :cellwise)
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example204_HagenPoiseuille.jl b/v1.19.1/module_examples/Example204_HagenPoiseuille.jl new file mode 100644 index 000000000..9f489e0a5 --- /dev/null +++ b/v1.19.1/module_examples/Example204_HagenPoiseuille.jl @@ -0,0 +1,79 @@ +#= + +# 204: 2D Convection in Hagen-Poiseuille flow +([source code](@__SOURCE_URL__)) + +Solve the equation + +```math +\partial_t u -\nabla ( D \nabla u - v u) = 0 +``` +in $\Omega=(0,L)\times (0,H)$ with dirichlet boundary conditions +at $x=0$ and outflow boundary condition at $x=L$. +=# + +module Example204_HagenPoiseuille +using Printf +using VoronoiFVM +using ExtendableGrids +using GridVisualize + +function main(; nref = 0, Plotter = nothing, D = 0.01, v = 1.0, tend = 100, cin = 1.0, assembly = :edgewise) + H = 1.0 + L = 5.0 + grid = simplexgrid(range(0, L; length = 20 * 2^nref), + range(0, H; length = 5 * 2^nref)) + + function fhp(x, y) + yh = y / H + return v * 4 * yh * (1.0 - yh), 0 + end + + evelo = edgevelocities(grid, fhp) + bfvelo = bfacevelocities(grid, fhp) + + function flux!(f, u, edge) + vd = evelo[edge.index] / D + bp = fbernoulli(vd) + bm = fbernoulli(-vd) + f[1] = D * (bp * u[1] - bm * u[2]) + end + + function outflow!(f, u, node) + if node.region == 2 + f[1] = bfvelo[node.ibnode, node.ibface] * u[1] + end + end + + ispec = 1 + physics = VoronoiFVM.Physics(; flux = flux!, breaction = outflow!) + sys = VoronoiFVM.System(grid, physics; assembly = assembly) + enable_species!(sys, ispec, [1]) + + boundary_dirichlet!(sys, ispec, 4, cin) + + ## Transient solution of the problem + control = VoronoiFVM.NewtonControl() + control.Δt = 0.01 * 2.0^(-nref) + control.Δt_min = 0.01 * 2.0^(-nref) + control.Δt_max = 0.1 * tend + control.force_first_step = true + tsol = solve(sys; inival = 0, times = [0, tend], control = control) + + vis = GridVisualizer(; Plotter = Plotter) + for i = 1:length(tsol.t) + scalarplot!(vis[1, 1], grid, tsol[1, :, i]; flimits = (0, cin + 1.0e-5), + title = @sprintf("time=%3f", tsol.t[i]), show = true) + end + tsol +end + +using Test +function runtests() + tsol1 = main(; assembly = :edgewise) + tsol2 = main(; assembly = :cellwise) + @test all(tsol1[end] .≈ 1) && + all(tsol1[end] .≈ 1) +end + +end diff --git a/v1.19.1/module_examples/Example204_HagenPoiseuille/index.html b/v1.19.1/module_examples/Example204_HagenPoiseuille/index.html new file mode 100644 index 000000000..c08d1630c --- /dev/null +++ b/v1.19.1/module_examples/Example204_HagenPoiseuille/index.html @@ -0,0 +1,66 @@ + +204: 2D Convection in Hagen-Poiseuille flow · VoronoiFVM.jl

    204: 2D Convection in Hagen-Poiseuille flow

    (source code)

    Solve the equation

    \[\partial_t u -\nabla ( D \nabla u - v u) = 0\]

    in $\Omega=(0,L)\times (0,H)$ with dirichlet boundary conditions at $x=0$ and outflow boundary condition at $x=L$.

    module Example204_HagenPoiseuille
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +
    +function main(; nref = 0, Plotter = nothing, D = 0.01, v = 1.0, tend = 100, cin = 1.0, assembly = :edgewise)
    +    H = 1.0
    +    L = 5.0
    +    grid = simplexgrid(range(0, L; length = 20 * 2^nref),
    +                       range(0, H; length = 5 * 2^nref))
    +
    +    function fhp(x, y)
    +        yh = y / H
    +        return v * 4 * yh * (1.0 - yh), 0
    +    end
    +
    +    evelo = edgevelocities(grid, fhp)
    +    bfvelo = bfacevelocities(grid, fhp)
    +
    +    function flux!(f, u, edge)
    +        vd = evelo[edge.index] / D
    +        bp = fbernoulli(vd)
    +        bm = fbernoulli(-vd)
    +        f[1] = D * (bp * u[1] - bm * u[2])
    +    end
    +
    +    function outflow!(f, u, node)
    +        if node.region == 2
    +            f[1] = bfvelo[node.ibnode, node.ibface] * u[1]
    +        end
    +    end
    +
    +    ispec = 1
    +    physics = VoronoiFVM.Physics(; flux = flux!, breaction = outflow!)
    +    sys = VoronoiFVM.System(grid, physics; assembly = assembly)
    +    enable_species!(sys, ispec, [1])
    +
    +    boundary_dirichlet!(sys, ispec, 4, cin)
    +
    +    # Transient solution of the problem
    +    control = VoronoiFVM.NewtonControl()
    +    control.Δt = 0.01 * 2.0^(-nref)
    +    control.Δt_min = 0.01 * 2.0^(-nref)
    +    control.Δt_max = 0.1 * tend
    +    control.force_first_step = true
    +    tsol = solve(sys; inival = 0, times = [0, tend], control = control)
    +
    +    vis = GridVisualizer(; Plotter = Plotter)
    +    for i = 1:length(tsol.t)
    +        scalarplot!(vis[1, 1], grid, tsol[1, :, i]; flimits = (0, cin + 1.0e-5),
    +                    title = @sprintf("time=%3f", tsol.t[i]), show = true)
    +    end
    +    tsol
    +end
    +
    +using Test
    +function runtests()
    +    tsol1 = main(; assembly = :edgewise)
    +    tsol2 = main(; assembly = :cellwise)
    +    @test all(tsol1[end] .≈ 1) &&
    +          all(tsol1[end] .≈ 1)
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example205_StagnationPoint.jl b/v1.19.1/module_examples/Example205_StagnationPoint.jl new file mode 100644 index 000000000..9d3fbeb33 --- /dev/null +++ b/v1.19.1/module_examples/Example205_StagnationPoint.jl @@ -0,0 +1,109 @@ +#= + +# 205: Convection in axisymmetric stagnation point flow +([source code](@__SOURCE_URL__)) + +Solve the equation + +```math +\begin{aligned} + -\nabla ( D \nabla u - \vec v u) &= 0\\ + u|_{\Gamma_1} &=1\\ + u|_{\Gamma_0} &=0\\ + (\partial_n u)|_{\Gamma_{out}} & = 0 +\end{aligned} +``` + +in ``\Omega=(0,1)\times (0,1)`` with ``\Gamma_1 = (0,0.25)\times 1``, +``\Gamma_0=(0.25,1)\times 1`` and ``\Gamma_{out} = 1\times (0,1)``. +On boundary parts not listed, no-flow boundary conditions are assumed. + +The axisymmetric stagnation point flow ``\vec v(r,z)=(vr,-2vz)`` is divergence free. + +=# + +module Example205_StagnationPoint +using Printf +using VoronoiFVM +using ExtendableGrids +using GridVisualize + +function main(; nref = 0, gridname = nothing, Plotter = nothing, D = 0.01, v = 100, cin = 1.0, assembly = :cellwise) + H = 1.0 + L = 1.0 + + Γ_1 = 5 + Γ_0 = 4 + Γ_out = 2 + + if !isnothing(gridname) + grid = simplexgrid(gridname) + else + grid = simplexgrid(range(0, L; length = 10 * 2^nref), + range(0, H; length = 10 * 2^nref)) + bfacemask!(grid, [0, H], [0.25L, H], 5) + end + circular_symmetric!(grid) + + frz(r, z) = (v * r, -2v * z) + + evelo = edgevelocities(grid, frz) + bfvelo = bfacevelocities(grid, frz) + + function flux!(f, u, edge) + vd = evelo[edge.index] / D + bp = fbernoulli(vd) + bm = fbernoulli(-vd) + f[1] = D * (bp * u[1] - bm * u[2]) + end + + function outflow!(f, u, node) + if node.region == Γ_out + f[1] = bfvelo[node.ibnode, node.ibface] * u[1] + end + end + + ispec = 1 + physics = VoronoiFVM.Physics(; flux = flux!, breaction = outflow!) + sys = VoronoiFVM.System(grid, physics; assembly = assembly) + enable_species!(sys, ispec, [1]) + boundary_dirichlet!(sys, ispec, Γ_1, cin) + boundary_dirichlet!(sys, ispec, Γ_0, 0) + + tf = TestFunctionFactory(sys) + tf_in = testfunction(tf, [Γ_out], [Γ_1]) + tf_out = testfunction(tf, [Γ_1], [Γ_out]) + + sol = solve(sys) + + I_in = integrate(sys, tf_in, sol) + I_out = integrate(sys, tf_out, sol) + + scalarplot(sys, sol; Plotter = Plotter) + + ## Test if inflow=outflow + test1 = isapprox(I_in, -I_out; rtol = 1.0e-5) + + ## Test global maximum principle + test2 = isapprox(maximum(sol), cin; rtol = 1.0e-10) + test3 = isapprox(minimum(sol), 0; atol = 1.0e-10) + + ## test zero divergence of fvm velocities + div = VoronoiFVM.calc_divergences(sys, evelo, bfvelo) + test4 = all(x -> abs(x) < 1.0e-12, div) + + test1 && test2 && test3 && test4 +end + +using Test +function runtests() + test0 = true + if VERSION > v"1.6" + # test on unstructured grid + gridname = joinpath(pkgdir(VoronoiFVM), "assets", "rz2d.sg") + test0 = test0 && main(; assembly = :edgewise, gridname) && main(; assembly = :cellwise, gridname) + end + @test test0 && main(; assembly = :edgewise) && main(; assembly = :cellwise) +end + +end diff --git a/v1.19.1/module_examples/Example205_StagnationPoint/index.html b/v1.19.1/module_examples/Example205_StagnationPoint/index.html new file mode 100644 index 000000000..72a3b8377 --- /dev/null +++ b/v1.19.1/module_examples/Example205_StagnationPoint/index.html @@ -0,0 +1,89 @@ + +205: Convection in axisymmetric stagnation point flow · VoronoiFVM.jl

    205: Convection in axisymmetric stagnation point flow

    (source code)

    Solve the equation

    \[\begin{aligned} + -\nabla ( D \nabla u - \vec v u) &= 0\\ + u|_{\Gamma_1} &=1\\ + u|_{\Gamma_0} &=0\\ + (\partial_n u)|_{\Gamma_{out}} & = 0 +\end{aligned}\]

    in $\Omega=(0,1)\times (0,1)$ with $\Gamma_1 = (0,0.25)\times 1$, $\Gamma_0=(0.25,1)\times 1$ and $\Gamma_{out} = 1\times (0,1)$. On boundary parts not listed, no-flow boundary conditions are assumed.

    The axisymmetric stagnation point flow $\vec v(r,z)=(vr,-2vz)$ is divergence free.

    module Example205_StagnationPoint
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +
    +function main(; nref = 0, gridname = nothing, Plotter = nothing, D = 0.01, v = 100, cin = 1.0, assembly = :cellwise)
    +    H = 1.0
    +    L = 1.0
    +
    +    Γ_1 = 5
    +    Γ_0 = 4
    +    Γ_out = 2
    +
    +    if !isnothing(gridname)
    +        grid = simplexgrid(gridname)
    +    else
    +        grid = simplexgrid(range(0, L; length = 10 * 2^nref),
    +                           range(0, H; length = 10 * 2^nref))
    +        bfacemask!(grid, [0, H], [0.25L, H], 5)
    +    end
    +    circular_symmetric!(grid)
    +
    +    frz(r, z) = (v * r, -2v * z)
    +
    +    evelo = edgevelocities(grid, frz)
    +    bfvelo = bfacevelocities(grid, frz)
    +
    +    function flux!(f, u, edge)
    +        vd = evelo[edge.index] / D
    +        bp = fbernoulli(vd)
    +        bm = fbernoulli(-vd)
    +        f[1] = D * (bp * u[1] - bm * u[2])
    +    end
    +
    +    function outflow!(f, u, node)
    +        if node.region == Γ_out
    +            f[1] = bfvelo[node.ibnode, node.ibface] * u[1]
    +        end
    +    end
    +
    +    ispec = 1
    +    physics = VoronoiFVM.Physics(; flux = flux!, breaction = outflow!)
    +    sys = VoronoiFVM.System(grid, physics; assembly = assembly)
    +    enable_species!(sys, ispec, [1])
    +    boundary_dirichlet!(sys, ispec, Γ_1, cin)
    +    boundary_dirichlet!(sys, ispec, Γ_0, 0)
    +
    +    tf = TestFunctionFactory(sys)
    +    tf_in = testfunction(tf, [Γ_out], [Γ_1])
    +    tf_out = testfunction(tf, [Γ_1], [Γ_out])
    +
    +    sol = solve(sys)
    +
    +    I_in = integrate(sys, tf_in, sol)
    +    I_out = integrate(sys, tf_out, sol)
    +
    +    scalarplot(sys, sol; Plotter = Plotter)
    +
    +    # Test if inflow=outflow
    +    test1 = isapprox(I_in, -I_out; rtol = 1.0e-5)
    +
    +    # Test global maximum principle
    +    test2 = isapprox(maximum(sol), cin; rtol = 1.0e-10)
    +    test3 = isapprox(minimum(sol), 0; atol = 1.0e-10)
    +
    +    # test zero divergence of fvm velocities
    +    div = VoronoiFVM.calc_divergences(sys, evelo, bfvelo)
    +    test4 = all(x -> abs(x) < 1.0e-12, div)
    +
    +    test1 && test2 && test3 && test4
    +end
    +
    +using Test
    +function runtests()
    +    test0 = true
    +    if VERSION > v"1.6"

    test on unstructured grid

            gridname = joinpath(pkgdir(VoronoiFVM), "assets", "rz2d.sg")
    +        test0 = test0 && main(; assembly = :edgewise, gridname) && main(; assembly = :cellwise, gridname)
    +    end
    +    @test test0 && main(; assembly = :edgewise) && main(; assembly = :cellwise)
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example206_JouleHeat.jl b/v1.19.1/module_examples/Example206_JouleHeat.jl new file mode 100644 index 000000000..7b75ff653 --- /dev/null +++ b/v1.19.1/module_examples/Example206_JouleHeat.jl @@ -0,0 +1,107 @@ +# # 206: 2D Joule heating +# ([source code](@__SOURCE_URL__)) +#= + +```math +\begin{aligned} +-\nabla \left\cdot (\kappa(T) \nabla \phi\right) &= 0\\ +\partial_t (cT) - \nabla\cdot \left(\lambda \nabla T\right) &= \kappa(T) |\nabla \phi|^2\\ +\kappa(T)&= \kappa_0 exp(\alpha(T-T0)) +\end{aligned} +``` +The discretization uses the approach developed in +A. Bradji, R. Herbin, [DOI 10.1093/imanum/drm030](https://doi.org/10.1093/imanum/drm030). +=# + +module Example206_JouleHeat + +using Printf +using VoronoiFVM +using ExtendableGrids +using ExtendableSparse +using GridVisualize +using LinearAlgebra +using SimplexGridFactory +using Triangulate + +function main(; nref = 0, Plotter = nothing, verbose = "and", unknown_storage = :sparse, assembly = :edgewise, + ythin = 0.25) + + ## Create grid + b = SimplexGridBuilder(; Generator = Triangulate) + p00 = point!(b, 0, 0) + p30 = point!(b, 3, 0) + p32 = point!(b, 3, 1) + p21 = point!(b, 2, ythin) + p11 = point!(b, 1, ythin) + p02 = point!(b, 0, 1) + + facetregion!(b, 4) + facet!(b, p00, p30) + facetregion!(b, 2) + facet!(b, p30, p32) + facetregion!(b, 3) + facet!(b, p32, p21) + facet!(b, p21, p11) + facet!(b, p11, p02) + facetregion!(b, 1) + facet!(b, p02, p00) + + grid = simplexgrid(b; maxvolume = 0.01 * 4.0^(-nref)) + + ## Describe problem + iϕ::Int = 1 + iT::Int = 2 + κ0::Float64 = 1 + α::Float64 = 1 + T0::Float64 = 0.5 + λ::Float64 = 1 + c::Float64 = 1 + + function storage!(y, u, node) + y[iT] = c * u[iT] + end + + κ(T) = κ0 * exp(α * (T - T0)) + + function flux!(y, u, edge) + y[iϕ] = κ(y[iT]) * (u[iϕ, 1] - u[iϕ, 2]) + y[iT] = λ * (u[iT, 1] - u[iT, 2]) + end + + ## The convention in VoronoiFVM.jl is to have all terms depending on the solution + ## on the left hand side of the equation. That is why we have the minus sign here. + function jouleheat!(y, u, edge) + y[iT] = -κ(y[iT]) * (u[iϕ, 1] - u[iϕ, 2]) * (u[iϕ, 1] - u[iϕ, 2]) + end + + function bcondition!(y, u, node) + boundary_dirichlet!(y, u, node; species = iϕ, region = 1, value = -10) + boundary_dirichlet!(y, u, node; species = iϕ, region = 2, value = 10) + + boundary_robin!(y, u, node; species = iT, region = 1, value = T0, factor = 0.5) + boundary_robin!(y, u, node; species = iT, region = 2, value = T0, factor = 0.5) + boundary_robin!(y, u, node; species = iT, region = 3, value = T0, factor = 0.5) + boundary_robin!(y, u, node; species = iT, region = 4, value = T0, factor = 0.5) + end + + sys = VoronoiFVM.System(grid; bcondition = bcondition!, flux = flux!, + edgereaction = jouleheat!, storage = storage!, + species = [iϕ, iT], assembly = assembly) + + sol = solve(sys; verbose) + + vis = GridVisualizer(; Plotter, layout = (2, 1)) + scalarplot!(vis[1, 1], grid, sol[iϕ, :]; title = "ϕ", colormap = :bwr) + scalarplot!(vis[2, 1], grid, sol[iT, :]; title = "T", colormap = :hot) + reveal(vis) + norm(sol, Inf) +end + +using Test +function runtests() + testval = 24.639120035942938 + @test main(; assembly = :edgewise) ≈ testval && + main(; assembly = :cellwise) ≈ testval +end +end diff --git a/v1.19.1/module_examples/Example206_JouleHeat/index.html b/v1.19.1/module_examples/Example206_JouleHeat/index.html new file mode 100644 index 000000000..f9ae3efac --- /dev/null +++ b/v1.19.1/module_examples/Example206_JouleHeat/index.html @@ -0,0 +1,97 @@ + +206: 2D Joule heating · VoronoiFVM.jl

    206: 2D Joule heating

    (source code)

    \[\begin{aligned} +-\nabla \left\cdot (\kappa(T) \nabla \phi\right) &= 0\\ +\partial_t (cT) - \nabla\cdot \left(\lambda \nabla T\right) &= \kappa(T) |\nabla \phi|^2\\ +\kappa(T)&= \kappa_0 exp(\alpha(T-T0)) +\end{aligned}\]

    The discretization uses the approach developed in A. Bradji, R. Herbin, DOI 10.1093/imanum/drm030.

    module Example206_JouleHeat
    +
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +using ExtendableSparse
    +using GridVisualize
    +using LinearAlgebra
    +using SimplexGridFactory
    +using Triangulate
    +
    +function main(; nref = 0, Plotter = nothing, verbose = "and", unknown_storage = :sparse, assembly = :edgewise,
    +              ythin = 0.25)
    +
    +    # Create grid
    +    b = SimplexGridBuilder(; Generator = Triangulate)
    +    p00 = point!(b, 0, 0)
    +    p30 = point!(b, 3, 0)
    +    p32 = point!(b, 3, 1)
    +    p21 = point!(b, 2, ythin)
    +    p11 = point!(b, 1, ythin)
    +    p02 = point!(b, 0, 1)
    +
    +    facetregion!(b, 4)
    +    facet!(b, p00, p30)
    +    facetregion!(b, 2)
    +    facet!(b, p30, p32)
    +    facetregion!(b, 3)
    +    facet!(b, p32, p21)
    +    facet!(b, p21, p11)
    +    facet!(b, p11, p02)
    +    facetregion!(b, 1)
    +    facet!(b, p02, p00)
    +
    +    grid = simplexgrid(b; maxvolume = 0.01 * 4.0^(-nref))
    +
    +    # Describe problem
    +    iϕ::Int = 1
    +    iT::Int = 2
    +    κ0::Float64 = 1
    +    α::Float64 = 1
    +    T0::Float64 = 0.5
    +    λ::Float64 = 1
    +    c::Float64 = 1
    +
    +    function storage!(y, u, node)
    +        y[iT] = c * u[iT]
    +    end
    +
    +    κ(T) = κ0 * exp(α * (T - T0))
    +
    +    function flux!(y, u, edge)
    +        y[iϕ] = κ(y[iT]) * (u[iϕ, 1] - u[iϕ, 2])
    +        y[iT] = λ * (u[iT, 1] - u[iT, 2])
    +    end
    +
    +    # The convention in VoronoiFVM.jl is to have all terms depending on the solution
    +    # on the left hand side of the equation. That is why we have the minus sign here.
    +    function jouleheat!(y, u, edge)
    +        y[iT] = -κ(y[iT]) * (u[iϕ, 1] - u[iϕ, 2]) * (u[iϕ, 1] - u[iϕ, 2])
    +    end
    +
    +    function bcondition!(y, u, node)
    +        boundary_dirichlet!(y, u, node; species = iϕ, region = 1, value = -10)
    +        boundary_dirichlet!(y, u, node; species = iϕ, region = 2, value = 10)
    +
    +        boundary_robin!(y, u, node; species = iT, region = 1, value = T0, factor = 0.5)
    +        boundary_robin!(y, u, node; species = iT, region = 2, value = T0, factor = 0.5)
    +        boundary_robin!(y, u, node; species = iT, region = 3, value = T0, factor = 0.5)
    +        boundary_robin!(y, u, node; species = iT, region = 4, value = T0, factor = 0.5)
    +    end
    +
    +    sys = VoronoiFVM.System(grid; bcondition = bcondition!, flux = flux!,
    +                            edgereaction = jouleheat!, storage = storage!,
    +                            species = [iϕ, iT], assembly = assembly)
    +
    +    sol = solve(sys; verbose)
    +
    +    vis = GridVisualizer(; Plotter, layout = (2, 1))
    +    scalarplot!(vis[1, 1], grid, sol[iϕ, :]; title = "ϕ", colormap = :bwr)
    +    scalarplot!(vis[2, 1], grid, sol[iT, :]; title = "T", colormap = :hot)
    +    reveal(vis)
    +    norm(sol, Inf)
    +end
    +
    +using Test
    +function runtests()
    +    testval = 24.639120035942938
    +    @test main(; assembly = :edgewise) ≈ testval &&
    +          main(; assembly = :cellwise) ≈ testval
    +end
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example207_NonlinearPoisson2D.jl b/v1.19.1/module_examples/Example207_NonlinearPoisson2D.jl new file mode 100644 index 000000000..de9c34071 --- /dev/null +++ b/v1.19.1/module_examples/Example207_NonlinearPoisson2D.jl @@ -0,0 +1,83 @@ +# # 207: 2D Nonlinear Poisson equation +# ([source code](@__SOURCE_URL__)) + +module Example207_NonlinearPoisson2D + +using Printf +using VoronoiFVM +using ExtendableGrids +using ExtendableSparse +using GridVisualize +using LinearSolve +using ILUZero + +function main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse, + method_linear = nothing, assembly = :edgewise, + precon_linear = A -> VoronoiFVM.Identity()) + h = 1.0 / convert(Float64, n) + X = collect(0.0:h:1.0) + Y = collect(0.0:h:1.0) + + grid = VoronoiFVM.Grid(X, Y) + + eps = 1.0e-2 + + physics = VoronoiFVM.Physics(; reaction = function (f, u, node) + f[1] = u[1]^2 + end, flux = function (f, u, edge) + f[1] = eps * (u[1, 1]^2 - u[1, 2]^2) + end, source = function (f, node) + x1 = node[1] - 0.5 + x2 = node[2] - 0.5 + f[1] = exp(-20.0 * (x1^2 + x2^2)) + end, storage = function (f, u, node) + f[1] = u[1] + end) + sys = VoronoiFVM.System(grid, physics; unknown_storage, assembly = assembly) + enable_species!(sys, 1, [1]) + + boundary_dirichlet!(sys, 1, 2, 0.1) + boundary_dirichlet!(sys, 1, 4, 0.1) + + inival = unknowns(sys) + inival .= 0.5 + + control = VoronoiFVM.NewtonControl() + control.verbose = verbose + control.reltol_linear = 1.0e-5 + control.method_linear = method_linear + control.precon_linear = precon_linear + tstep = 0.01 + time = 0.0 + u15 = 0 + p = GridVisualizer(; Plotter = Plotter) + while time < 1.0 + time = time + tstep + U = solve(sys; inival, control, tstep) + u15 = U[15] + inival .= U + + scalarplot!(p[1, 1], grid, U[1, :]; Plotter = Plotter, clear = true, show = true) + tstep *= 1.0 + end + return u15 +end + +using Test +function runtests() + # test at once for iterative solution here + testval = 0.3554284760906605 + @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :sparse, method_linear = KrylovJL_CG(), precon_linear = ILUZeroPreconditioner, + assembly = :edgewise) ≈ testval && + main(; unknown_storage = :dense, method_linear = KrylovJL_CG(), precon_linear = ILUZeroPreconditioner, + assembly = :edgewise) ≈ testval && + main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval && + main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval && + main(; unknown_storage = :sparse, method_linear = KrylovJL_CG(), precon_linear = ILUZeroPreconditioner, + assembly = :cellwise) ≈ testval && + main(; unknown_storage = :dense, method_linear = KrylovJL_CG(), precon_linear = ILUZeroPreconditioner, + assembly = :cellwise) ≈ testval +end +end diff --git a/v1.19.1/module_examples/Example207_NonlinearPoisson2D/index.html b/v1.19.1/module_examples/Example207_NonlinearPoisson2D/index.html new file mode 100644 index 000000000..26e0d23c2 --- /dev/null +++ b/v1.19.1/module_examples/Example207_NonlinearPoisson2D/index.html @@ -0,0 +1,79 @@ + +207: 2D Nonlinear Poisson equation · VoronoiFVM.jl

    207: 2D Nonlinear Poisson equation

    (source code)

    module Example207_NonlinearPoisson2D
    +
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +using ExtendableSparse
    +using GridVisualize
    +using LinearSolve
    +using ILUZero
    +
    +function main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse,
    +              method_linear = nothing, assembly = :edgewise,
    +              precon_linear = A -> VoronoiFVM.Identity())
    +    h = 1.0 / convert(Float64, n)
    +    X = collect(0.0:h:1.0)
    +    Y = collect(0.0:h:1.0)
    +
    +    grid = VoronoiFVM.Grid(X, Y)
    +
    +    eps = 1.0e-2
    +
    +    physics = VoronoiFVM.Physics(; reaction = function (f, u, node)
    +                                     f[1] = u[1]^2
    +                                 end, flux = function (f, u, edge)
    +                                     f[1] = eps * (u[1, 1]^2 - u[1, 2]^2)
    +                                 end, source = function (f, node)
    +                                     x1 = node[1] - 0.5
    +                                     x2 = node[2] - 0.5
    +                                     f[1] = exp(-20.0 * (x1^2 + x2^2))
    +                                 end, storage = function (f, u, node)
    +                                     f[1] = u[1]
    +                                 end)
    +    sys = VoronoiFVM.System(grid, physics; unknown_storage, assembly = assembly)
    +    enable_species!(sys, 1, [1])
    +
    +    boundary_dirichlet!(sys, 1, 2, 0.1)
    +    boundary_dirichlet!(sys, 1, 4, 0.1)
    +
    +    inival = unknowns(sys)
    +    inival .= 0.5
    +
    +    control = VoronoiFVM.NewtonControl()
    +    control.verbose = verbose
    +    control.reltol_linear = 1.0e-5
    +    control.method_linear = method_linear
    +    control.precon_linear = precon_linear
    +    tstep = 0.01
    +    time = 0.0
    +    u15 = 0
    +    p = GridVisualizer(; Plotter = Plotter)
    +    while time < 1.0
    +        time = time + tstep
    +        U = solve(sys; inival, control, tstep)
    +        u15 = U[15]
    +        inival .= U
    +
    +        scalarplot!(p[1, 1], grid, U[1, :]; Plotter = Plotter, clear = true, show = true)
    +        tstep *= 1.0
    +    end
    +    return u15
    +end
    +
    +using Test
    +function runtests()

    test at once for iterative solution here

        testval = 0.3554284760906605
    +    @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :sparse, method_linear = KrylovJL_CG(), precon_linear = ILUZeroPreconditioner,
    +               assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :dense, method_linear = KrylovJL_CG(), precon_linear = ILUZeroPreconditioner,
    +               assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval &&
    +          main(; unknown_storage = :sparse, method_linear = KrylovJL_CG(), precon_linear = ILUZeroPreconditioner,
    +               assembly = :cellwise) ≈ testval &&
    +          main(; unknown_storage = :dense, method_linear = KrylovJL_CG(), precon_linear = ILUZeroPreconditioner,
    +               assembly = :cellwise) ≈ testval
    +end
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example210_NonlinearPoisson2D_Reaction.jl b/v1.19.1/module_examples/Example210_NonlinearPoisson2D_Reaction.jl new file mode 100644 index 000000000..76d3ea823 --- /dev/null +++ b/v1.19.1/module_examples/Example210_NonlinearPoisson2D_Reaction.jl @@ -0,0 +1,86 @@ +# # 210: 2D Nonlinear Poisson with reaction +# ([source code](@__SOURCE_URL__)) + +module Example210_NonlinearPoisson2D_Reaction + +using Printf +using VoronoiFVM +using ExtendableGrids +using GridVisualize + +function main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise) + h = 1.0 / convert(Float64, n) + X = collect(0.0:h:1.0) + Y = collect(0.0:h:1.0) + + grid = VoronoiFVM.Grid(X, Y) + data = (eps = 1.0e-2, k = 1.0) + + function reaction!(f, u, node, data) + f[1] = data.k * (u[1] - u[2]) + f[2] = data.k * (u[2] - u[1]) + end + + function flux!(f, u, edge, data) + f[1] = data.eps * (u[1, 1] - u[1, 2]) + f[2] = data.eps * (u[2, 1] - u[2, 2]) + end + + function source!(f, node, data) + x1 = node[1] - 0.5 + x2 = node[2] - 0.5 + f[1] = exp(-20 * (x1^2 + x2^2)) + end + + function storage!(f, u, node, data) + f[1] = u[1] + f[2] = u[2] + end + + physics = VoronoiFVM.Physics(; data = data, + flux = flux!, + storage = storage!, + reaction = reaction!, + source = source!) + + sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly) + + enable_species!(sys, 1, [1]) + enable_species!(sys, 2, [1]) + + inival = unknowns(sys) + inival .= 0.0 + + control = VoronoiFVM.NewtonControl() + control.verbose = verbose + tstep = 0.01 + time = 0.0 + istep = 0 + u15 = 0 + p = GridVisualizer(; Plotter = Plotter, layout = (2, 1)) + while time < 1 + time = time + tstep + U = solve(sys; inival, control, tstep) + inival .= U + if verbose + @printf("time=%g\n", time) + end + u15 = U[15] + tstep *= 1.0 + istep = istep + 1 + scalarplot!(p[1, 1], grid, U[1, :]; clear = true) + scalarplot!(p[2, 1], grid, U[2, :]; show = true) + end + return u15 +end + +using Test +function runtests() + testval = 0.014566189535134827 + @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval && + main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval +end + +end diff --git a/v1.19.1/module_examples/Example210_NonlinearPoisson2D_Reaction/index.html b/v1.19.1/module_examples/Example210_NonlinearPoisson2D_Reaction/index.html new file mode 100644 index 000000000..1c0cfce16 --- /dev/null +++ b/v1.19.1/module_examples/Example210_NonlinearPoisson2D_Reaction/index.html @@ -0,0 +1,84 @@ + +210: 2D Nonlinear Poisson with reaction · VoronoiFVM.jl

    210: 2D Nonlinear Poisson with reaction

    (source code)

    module Example210_NonlinearPoisson2D_Reaction
    +
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +
    +function main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise)
    +    h = 1.0 / convert(Float64, n)
    +    X = collect(0.0:h:1.0)
    +    Y = collect(0.0:h:1.0)
    +
    +    grid = VoronoiFVM.Grid(X, Y)
    +    data = (eps = 1.0e-2, k = 1.0)
    +
    +    function reaction!(f, u, node, data)
    +        f[1] = data.k * (u[1] - u[2])
    +        f[2] = data.k * (u[2] - u[1])
    +    end
    +
    +    function flux!(f, u, edge, data)
    +        f[1] = data.eps * (u[1, 1] - u[1, 2])
    +        f[2] = data.eps * (u[2, 1] - u[2, 2])
    +    end
    +
    +    function source!(f, node, data)
    +        x1 = node[1] - 0.5
    +        x2 = node[2] - 0.5
    +        f[1] = exp(-20 * (x1^2 + x2^2))
    +    end
    +
    +    function storage!(f, u, node, data)
    +        f[1] = u[1]
    +        f[2] = u[2]
    +    end
    +
    +    physics = VoronoiFVM.Physics(; data = data,
    +                                 flux = flux!,
    +                                 storage = storage!,
    +                                 reaction = reaction!,
    +                                 source = source!)
    +
    +    sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly)
    +
    +    enable_species!(sys, 1, [1])
    +    enable_species!(sys, 2, [1])
    +
    +    inival = unknowns(sys)
    +    inival .= 0.0
    +
    +    control = VoronoiFVM.NewtonControl()
    +    control.verbose = verbose
    +    tstep = 0.01
    +    time = 0.0
    +    istep = 0
    +    u15 = 0
    +    p = GridVisualizer(; Plotter = Plotter, layout = (2, 1))
    +    while time < 1
    +        time = time + tstep
    +        U = solve(sys; inival, control, tstep)
    +        inival .= U
    +        if verbose
    +            @printf("time=%g\n", time)
    +        end
    +        u15 = U[15]
    +        tstep *= 1.0
    +        istep = istep + 1
    +        scalarplot!(p[1, 1], grid, U[1, :]; clear = true)
    +        scalarplot!(p[2, 1], grid, U[2, :]; show = true)
    +    end
    +    return u15
    +end
    +
    +using Test
    +function runtests()
    +    testval = 0.014566189535134827
    +    @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example215_NonlinearPoisson2D_BoundaryReaction.jl b/v1.19.1/module_examples/Example215_NonlinearPoisson2D_BoundaryReaction.jl new file mode 100644 index 000000000..e621532ba --- /dev/null +++ b/v1.19.1/module_examples/Example215_NonlinearPoisson2D_BoundaryReaction.jl @@ -0,0 +1,85 @@ +# # 215: 2D Nonlinear Poisson with boundary reaction +# ([source code](@__SOURCE_URL__)) + +module Example215_NonlinearPoisson2D_BoundaryReaction + +using Printf +using VoronoiFVM +using ExtendableGrids +using GridVisualize +using ExtendableSparse + +function main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise, + tend = 100, max_lureuse = 0) + h = 1.0 / convert(Float64, n) + X = collect(0.0:h:1.0) + Y = collect(0.0:h:1.0) + + grid = VoronoiFVM.Grid(X, Y) + + eps = 1.0e-2 + physics = VoronoiFVM.Physics(; breaction = function (f, u, node) + if node.region == 2 + f[1] = 1 * (u[1] - u[2]) + f[2] = 1 * (u[2] - u[1]) + else + f[1] = 0 + f[2] = 0 + end + end, flux = function (f, u, edge) + f[1] = eps * (u[1, 1] - u[1, 2]) + f[2] = eps * (u[2, 1] - u[2, 2]) + end, storage = function (f, u, node) + f[1] = u[1] + f[2] = u[2] + end) + + sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly) + enable_species!(sys, 1, [1]) + enable_species!(sys, 2, [1]) + + inival = unknowns(sys) + inival[1, :] .= map((x, y) -> exp(-5.0 * ((x - 0.5)^2 + (y - 0.5)^2)), grid) + inival[2, :] .= 0 + + control = VoronoiFVM.NewtonControl() + control.verbose = verbose + control.reltol_linear = 1.0e-5 + control.max_lureuse = max_lureuse + + tstep = 0.01 + time = 0.0 + istep = 0 + u25 = 0 + + p = GridVisualizer(; Plotter = Plotter, layout = (2, 1)) + while time < tend + time = time + tstep + U = solve(sys; inival, control, tstep) + inival .= U + if verbose + @printf("time=%g\n", time) + end + I = integrate(sys, physics.storage, U) + Uall = sum(I) + tstep *= 1.2 + istep = istep + 1 + u25 = U[25] + scalarplot!(p[1, 1], grid, U[1, :]; + title = @sprintf("U1: %.3g U1+U2:%8.3g", I[1, 1], Uall), + flimits = (0, 1)) + scalarplot!(p[2, 1], grid, U[2, :]; title = @sprintf("U2: %.3g", I[2, 1]), + flimits = (0, 1)) + reveal(p) + end + return u25 +end + +using Test +function runtests() + testval = 0.2760603343272377 + @test main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval && + main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval +end +end diff --git a/v1.19.1/module_examples/Example215_NonlinearPoisson2D_BoundaryReaction/index.html b/v1.19.1/module_examples/Example215_NonlinearPoisson2D_BoundaryReaction/index.html new file mode 100644 index 000000000..17de87f3e --- /dev/null +++ b/v1.19.1/module_examples/Example215_NonlinearPoisson2D_BoundaryReaction/index.html @@ -0,0 +1,83 @@ + +215: 2D Nonlinear Poisson with boundary reaction · VoronoiFVM.jl

    215: 2D Nonlinear Poisson with boundary reaction

    (source code)

    module Example215_NonlinearPoisson2D_BoundaryReaction
    +
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +using ExtendableSparse
    +
    +function main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise,
    +              tend = 100, max_lureuse = 0)
    +    h = 1.0 / convert(Float64, n)
    +    X = collect(0.0:h:1.0)
    +    Y = collect(0.0:h:1.0)
    +
    +    grid = VoronoiFVM.Grid(X, Y)
    +
    +    eps = 1.0e-2
    +    physics = VoronoiFVM.Physics(; breaction = function (f, u, node)
    +                                     if node.region == 2
    +                                         f[1] = 1 * (u[1] - u[2])
    +                                         f[2] = 1 * (u[2] - u[1])
    +                                     else
    +                                         f[1] = 0
    +                                         f[2] = 0
    +                                     end
    +                                 end, flux = function (f, u, edge)
    +                                     f[1] = eps * (u[1, 1] - u[1, 2])
    +                                     f[2] = eps * (u[2, 1] - u[2, 2])
    +                                 end, storage = function (f, u, node)
    +                                     f[1] = u[1]
    +                                     f[2] = u[2]
    +                                 end)
    +
    +    sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly)
    +    enable_species!(sys, 1, [1])
    +    enable_species!(sys, 2, [1])
    +
    +    inival = unknowns(sys)
    +    inival[1, :] .= map((x, y) -> exp(-5.0 * ((x - 0.5)^2 + (y - 0.5)^2)), grid)
    +    inival[2, :] .= 0
    +
    +    control = VoronoiFVM.NewtonControl()
    +    control.verbose = verbose
    +    control.reltol_linear = 1.0e-5
    +    control.max_lureuse = max_lureuse
    +
    +    tstep = 0.01
    +    time = 0.0
    +    istep = 0
    +    u25 = 0
    +
    +    p = GridVisualizer(; Plotter = Plotter, layout = (2, 1))
    +    while time < tend
    +        time = time + tstep
    +        U = solve(sys; inival, control, tstep)
    +        inival .= U
    +        if verbose
    +            @printf("time=%g\n", time)
    +        end
    +        I = integrate(sys, physics.storage, U)
    +        Uall = sum(I)
    +        tstep *= 1.2
    +        istep = istep + 1
    +        u25 = U[25]
    +        scalarplot!(p[1, 1], grid, U[1, :];
    +                    title = @sprintf("U1: %.3g U1+U2:%8.3g", I[1, 1], Uall),
    +                    flimits = (0, 1))
    +        scalarplot!(p[2, 1], grid, U[2, :]; title = @sprintf("U2: %.3g", I[2, 1]),
    +                    flimits = (0, 1))
    +        reveal(p)
    +    end
    +    return u25
    +end
    +
    +using Test
    +function runtests()
    +    testval = 0.2760603343272377
    +    @test main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval
    +end
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example220_NonlinearPoisson2D_BoundarySpecies.jl b/v1.19.1/module_examples/Example220_NonlinearPoisson2D_BoundarySpecies.jl new file mode 100644 index 000000000..1890e5c76 --- /dev/null +++ b/v1.19.1/module_examples/Example220_NonlinearPoisson2D_BoundarySpecies.jl @@ -0,0 +1,93 @@ +# # 220: 2D Nonlinear Poisson with boundary reaction and boundary species +# ([source code](@__SOURCE_URL__)) + +module Example220_NonlinearPoisson2D_BoundarySpecies + +using Printf +using VoronoiFVM +using ExtendableGrids +using GridVisualize + +function main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse) + h = 1.0 / convert(Float64, n) + X = collect(0.0:h:1.0) + Y = collect(0.0:h:1.0) + + grid = VoronoiFVM.Grid(X, Y) + + k = 1.0 + eps::Float64 = 1.0 + physics = VoronoiFVM.Physics(; + breaction = function (f, u, node) + if node.region == 2 + f[1] = k * (u[1] - u[3]) + f[3] = k * (u[3] - u[1]) + k * (u[3] - u[2]) + f[2] = k * (u[2] - u[3]) + end + end, bstorage = function (f, u, node) + if node.region == 2 + f[3] = u[3] + end + end, flux = function (f, u, edge) + f[1] = eps * (u[1, 1] - u[1, 2]) + f[2] = eps * (u[2, 1] - u[2, 2]) + end, source = function (f, node) + x1 = node[1] - 0.5 + x2 = node[2] - 0.5 + f[1] = exp(-20.0 * (x1^2 + x2^2)) + end, storage = function (f, u, node) + f[1] = u[1] + f[2] = u[2] + end) + + sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage) + + enable_species!(sys, 1, [1]) + enable_species!(sys, 2, [1]) + enable_boundary_species!(sys, 3, [2]) + + function tran32!(a, b) + a[1] = b[2] + end + + bgrid2 = subgrid(grid, [2]; boundary = true, transform = tran32!) + + inival = unknowns(sys) + inival .= 0.0 + + eps = 1.0e-2 + + control = VoronoiFVM.NewtonControl() + control.verbose = verbose + control.reltol_linear = 1.0e-5 + control.reltol = 1.0e-5 + control.max_lureuse = 0 + tstep = 0.01 + time = 0.0 + istep = 0 + u5 = 0 + p = GridVisualizer(; Plotter = Plotter, layout = (3, 1)) + while time < 1 + time = time + tstep + U = solve(sys; inival, control, tstep) + inival .= U + if verbose + @printf("time=%g\n", time) + end + tstep *= 1.0 + istep = istep + 1 + U_bound = view(U[3, :], bgrid2) + u5 = U_bound[5] + scalarplot!(p[1, 1], grid, U[1, :]; clear = true) + scalarplot!(p[2, 1], grid, U[2, :]) + scalarplot!(p[3, 1], bgrid2, U_bound; show = true, flimits = (0, 0.0025)) + end + return u5 +end + +using Test +function runtests() + @test main(; unknown_storage = :sparse) ≈ 0.0020781361856598 + main(; unknown_storage = :dense) ≈ 0.0020781361856598 +end +end diff --git a/v1.19.1/module_examples/Example220_NonlinearPoisson2D_BoundarySpecies/index.html b/v1.19.1/module_examples/Example220_NonlinearPoisson2D_BoundarySpecies/index.html new file mode 100644 index 000000000..c24ad5f31 --- /dev/null +++ b/v1.19.1/module_examples/Example220_NonlinearPoisson2D_BoundarySpecies/index.html @@ -0,0 +1,91 @@ + +220: 2D Nonlinear Poisson with boundary reaction and boundary species · VoronoiFVM.jl

    220: 2D Nonlinear Poisson with boundary reaction and boundary species

    (source code)

    module Example220_NonlinearPoisson2D_BoundarySpecies
    +
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +
    +function main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse)
    +    h = 1.0 / convert(Float64, n)
    +    X = collect(0.0:h:1.0)
    +    Y = collect(0.0:h:1.0)
    +
    +    grid = VoronoiFVM.Grid(X, Y)
    +
    +    k = 1.0
    +    eps::Float64 = 1.0
    +    physics = VoronoiFVM.Physics(;
    +                                 breaction = function (f, u, node)
    +                                     if node.region == 2
    +                                         f[1] = k * (u[1] - u[3])
    +                                         f[3] = k * (u[3] - u[1]) + k * (u[3] - u[2])
    +                                         f[2] = k * (u[2] - u[3])
    +                                     end
    +                                 end, bstorage = function (f, u, node)
    +                                     if node.region == 2
    +                                         f[3] = u[3]
    +                                     end
    +                                 end, flux = function (f, u, edge)
    +                                     f[1] = eps * (u[1, 1] - u[1, 2])
    +                                     f[2] = eps * (u[2, 1] - u[2, 2])
    +                                 end, source = function (f, node)
    +                                     x1 = node[1] - 0.5
    +                                     x2 = node[2] - 0.5
    +                                     f[1] = exp(-20.0 * (x1^2 + x2^2))
    +                                 end, storage = function (f, u, node)
    +                                     f[1] = u[1]
    +                                     f[2] = u[2]
    +                                 end)
    +
    +    sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage)
    +
    +    enable_species!(sys, 1, [1])
    +    enable_species!(sys, 2, [1])
    +    enable_boundary_species!(sys, 3, [2])
    +
    +    function tran32!(a, b)
    +        a[1] = b[2]
    +    end
    +
    +    bgrid2 = subgrid(grid, [2]; boundary = true, transform = tran32!)
    +
    +    inival = unknowns(sys)
    +    inival .= 0.0
    +
    +    eps = 1.0e-2
    +
    +    control = VoronoiFVM.NewtonControl()
    +    control.verbose = verbose
    +    control.reltol_linear = 1.0e-5
    +    control.reltol = 1.0e-5
    +    control.max_lureuse = 0
    +    tstep = 0.01
    +    time = 0.0
    +    istep = 0
    +    u5 = 0
    +    p = GridVisualizer(; Plotter = Plotter, layout = (3, 1))
    +    while time < 1
    +        time = time + tstep
    +        U = solve(sys; inival, control, tstep)
    +        inival .= U
    +        if verbose
    +            @printf("time=%g\n", time)
    +        end
    +        tstep *= 1.0
    +        istep = istep + 1
    +        U_bound = view(U[3, :], bgrid2)
    +        u5 = U_bound[5]
    +        scalarplot!(p[1, 1], grid, U[1, :]; clear = true)
    +        scalarplot!(p[2, 1], grid, U[2, :])
    +        scalarplot!(p[3, 1], bgrid2, U_bound; show = true, flimits = (0, 0.0025))
    +    end
    +    return u5
    +end
    +
    +using Test
    +function runtests()
    +    @test main(; unknown_storage = :sparse) ≈ 0.0020781361856598
    +    main(; unknown_storage = :dense) ≈ 0.0020781361856598
    +end
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example221_EquationBlockPrecon.jl b/v1.19.1/module_examples/Example221_EquationBlockPrecon.jl new file mode 100644 index 000000000..8b5daeb65 --- /dev/null +++ b/v1.19.1/module_examples/Example221_EquationBlockPrecon.jl @@ -0,0 +1,131 @@ +#= + +# 221: Equation block preconditioning +([source code](@__SOURCE_URL__)) + +=# + +module Example221_EquationBlockPrecon + +using VoronoiFVM, GridVisualize, ExtendableGrids, Printf +using AMGCLWrap, ExtendableSparse +using Test + +function main(;dim=1, nref=0, Plotter = nothing, plot_grid = false, verbose = false, + unknown_storage = :sparse, assembly = :edgewise,strategy = nothing) + + nx=30*2^nref+1 + ny=9*2^nref + + X = range(0,3,length=nx) + Y = range(-0.5,0.5,length=ny) + if dim==1 + grid = VoronoiFVM.Grid(X) + Γ_in=1 + Γ_out=2 + elseif dim==2 + grid = VoronoiFVM.Grid(X,Y) + Γ_in=4 + Γ_out=2 + else + grid = VoronoiFVM.Grid(X,Y,Y) + Γ_in=4 + Γ_out=2 + end + cellmask!(grid, [0.0,-0.5,-0.5], [1.0,0.5,0.5], 1) + cellmask!(grid, [1.0,-0.5,-0.5], [2.0,0.5,0.5], 2) + cellmask!(grid, [2.0,-0.5,-0.5], [3.0,0.5,0.5], 3) + + + subgrid1 = subgrid(grid, [1]) + subgrid2 = subgrid(grid, [1, 2, 3]) + subgrid3 = subgrid(grid, [3]) + + if plot_grid + return gridplot(grid; Plotter = Plotter) + end + + eps = [1, 1, 1] + k = [1, 1, 1] + + function reaction(f, u, node) + if node.region == 1 + f[1] = k[1] * u[1] + f[2] = -k[1] * u[1] + elseif node.region == 3 + f[2] = k[3] * u[2] + f[3] = -k[3] * u[2] + else + f[1] = 0 + end + end + + function source(f, node) + if node.region == 1 + f[1] = 1.0e-4 * (3.0 - node[1]) + end + end + + flux = function (f, u, edge) + if edge.region == 1 + f[1] = eps[1] * (u[1, 1] - u[1, 2]) + f[2] = eps[2] * (u[2, 1] - u[2, 2]) + elseif edge.region == 2 + f[2] = eps[2] * (u[2, 1] - u[2, 2]) + elseif edge.region == 3 + f[2] = eps[2] * (u[2, 1] - u[2, 2]) + f[3] = eps[3] * (u[3, 1] - u[3, 2]) + end + end + + storage = function (f, u, node) + if node.region == 1 + f[1] = u[1] + f[2] = u[2] + elseif node.region == 2 + f[2] = u[2] + elseif node.region == 3 + f[2] = u[2] + f[3] = u[3] + end + end + + sys = VoronoiFVM.System(grid; flux, reaction, storage, source, + unknown_storage , assembly, is_linear=true) + + enable_species!(sys, 1, [1]) + enable_species!(sys, 2, [1, 2, 3]) + enable_species!(sys, 3, [3]) + + boundary_dirichlet!(sys, 3, 2, 0.0) + + control = SolverControl(strategy, sys, verbose="l") + U=solve(sys;control) + @info num_dof(U) + + p = GridVisualizer(; Plotter = Plotter, layout = (3,1), + limits = (0, 1e-3), + xlimits=(0,3)) + + U1 = view(U[1, :], subgrid1) + U2 = view(U[2, :], subgrid2) + U3 = view(U[3, :], subgrid3) + + scalarplot!(p[1, 1], subgrid1, U1; title = "spec1", color = (0.5, 0, 0)) + scalarplot!(p[2, 1], subgrid2, U2; title = "spec2", color = (0.0, 0.5, 0)) + scalarplot!(p[3, 1], subgrid3, U3; title = "spec3", color = (0.0, 0.0, 0.5)) + reveal(p) + U +end + +function runtests() + strategy=BICGstabIteration(AMGCL_AMGPreconditioner()) + @test sum(main(;dim=1,strategy, unknown_storage=:dense)[2,:]) ≈ 0.014100861021046823 + @test sum(main(;dim=1,strategy, unknown_storage=:sparse)[2,:]) ≈ 0.014100861021046823 + @test sum(main(;dim=2,strategy, unknown_storage=:dense)[2,:]) ≈ 0.12690774918944653 + @test sum(main(;dim=2,strategy, unknown_storage=:sparse)[2,:]) ≈ 0.12690774918944653 + @test sum(main(;dim=3,strategy, unknown_storage=:dense)[2,:]) ≈ 1.1423244466533444 + @test sum(main(;dim=3,strategy, unknown_storage=:sparse)[2,:]) ≈ 1.1423244466533444 +end + +end diff --git a/v1.19.1/module_examples/Example221_EquationBlockPrecon/index.html b/v1.19.1/module_examples/Example221_EquationBlockPrecon/index.html new file mode 100644 index 000000000..090ea140a --- /dev/null +++ b/v1.19.1/module_examples/Example221_EquationBlockPrecon/index.html @@ -0,0 +1,125 @@ + +221: Equation block preconditioning · VoronoiFVM.jl

    221: Equation block preconditioning

    (source code)

    module Example221_EquationBlockPrecon
    +
    +using VoronoiFVM, GridVisualize, ExtendableGrids, Printf
    +using AMGCLWrap, ExtendableSparse
    +using Test
    +
    +function main(;dim=1, nref=0, Plotter = nothing, plot_grid = false, verbose = false,
    +              unknown_storage = :sparse, assembly = :edgewise,strategy = nothing)
    +
    +    nx=30*2^nref+1
    +    ny=9*2^nref
    +
    +    X = range(0,3,length=nx)
    +    Y = range(-0.5,0.5,length=ny)
    +    if dim==1
    +        grid = VoronoiFVM.Grid(X)
    +        Γ_in=1
    +        Γ_out=2
    +    elseif dim==2
    +        grid = VoronoiFVM.Grid(X,Y)
    +        Γ_in=4
    +        Γ_out=2
    +    else
    +        grid = VoronoiFVM.Grid(X,Y,Y)
    +        Γ_in=4
    +        Γ_out=2
    +    end
    +    cellmask!(grid, [0.0,-0.5,-0.5], [1.0,0.5,0.5], 1)
    +    cellmask!(grid, [1.0,-0.5,-0.5], [2.0,0.5,0.5], 2)
    +    cellmask!(grid, [2.0,-0.5,-0.5], [3.0,0.5,0.5], 3)
    +
    +
    +    subgrid1 = subgrid(grid, [1])
    +    subgrid2 = subgrid(grid, [1, 2, 3])
    +    subgrid3 = subgrid(grid, [3])
    +
    +    if plot_grid
    +        return gridplot(grid; Plotter = Plotter)
    +    end
    +
    +    eps = [1, 1, 1]
    +    k = [1, 1, 1]
    +
    +    function reaction(f, u, node)
    +        if node.region == 1
    +            f[1] = k[1] * u[1]
    +            f[2] = -k[1] * u[1]
    +        elseif node.region == 3
    +            f[2] = k[3] * u[2]
    +            f[3] = -k[3] * u[2]
    +        else
    +            f[1] = 0
    +        end
    +    end
    +
    +    function source(f, node)
    +        if node.region == 1
    +            f[1] = 1.0e-4 * (3.0 - node[1])
    +        end
    +    end
    +
    +    flux = function (f, u, edge)
    +        if edge.region == 1
    +            f[1] = eps[1] * (u[1, 1] - u[1, 2])
    +            f[2] = eps[2] * (u[2, 1] - u[2, 2])
    +        elseif edge.region == 2
    +            f[2] = eps[2] * (u[2, 1] - u[2, 2])
    +        elseif edge.region == 3
    +            f[2] = eps[2] * (u[2, 1] - u[2, 2])
    +            f[3] = eps[3] * (u[3, 1] - u[3, 2])
    +        end
    +    end
    +
    +    storage = function (f, u, node)
    +        if node.region == 1
    +            f[1] = u[1]
    +            f[2] = u[2]
    +        elseif node.region == 2
    +            f[2] = u[2]
    +        elseif node.region == 3
    +            f[2] = u[2]
    +            f[3] = u[3]
    +        end
    +    end
    +
    +    sys = VoronoiFVM.System(grid; flux, reaction, storage, source,
    +                            unknown_storage , assembly, is_linear=true)
    +
    +    enable_species!(sys, 1, [1])
    +    enable_species!(sys, 2, [1, 2, 3])
    +    enable_species!(sys, 3, [3])
    +
    +    boundary_dirichlet!(sys, 3, 2, 0.0)
    +
    +    control = SolverControl(strategy, sys, verbose="l")
    +    U=solve(sys;control)
    +    @info num_dof(U)
    +
    +    p = GridVisualizer(; Plotter = Plotter, layout = (3,1),
    +                       limits = (0, 1e-3),
    +                       xlimits=(0,3))
    +
    +    U1 = view(U[1, :], subgrid1)
    +    U2 = view(U[2, :], subgrid2)
    +    U3 = view(U[3, :], subgrid3)
    +
    +    scalarplot!(p[1, 1], subgrid1, U1; title = "spec1", color = (0.5, 0, 0))
    +    scalarplot!(p[2, 1], subgrid2, U2; title = "spec2", color = (0.0, 0.5, 0))
    +    scalarplot!(p[3, 1], subgrid3, U3; title = "spec3", color = (0.0, 0.0, 0.5))
    +    reveal(p)
    +    U
    +end
    +
    +function runtests()
    +    strategy=BICGstabIteration(AMGCL_AMGPreconditioner())
    +    @test sum(main(;dim=1,strategy, unknown_storage=:dense)[2,:]) ≈ 0.014100861021046823
    +    @test sum(main(;dim=1,strategy, unknown_storage=:sparse)[2,:]) ≈ 0.014100861021046823
    +    @test sum(main(;dim=2,strategy, unknown_storage=:dense)[2,:]) ≈ 0.12690774918944653
    +    @test sum(main(;dim=2,strategy, unknown_storage=:sparse)[2,:]) ≈ 0.12690774918944653
    +    @test sum(main(;dim=3,strategy, unknown_storage=:dense)[2,:]) ≈ 1.1423244466533444
    +    @test sum(main(;dim=3,strategy, unknown_storage=:sparse)[2,:]) ≈ 1.1423244466533444
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example225_TestFunctions2D.jl b/v1.19.1/module_examples/Example225_TestFunctions2D.jl new file mode 100644 index 000000000..611a46ddb --- /dev/null +++ b/v1.19.1/module_examples/Example225_TestFunctions2D.jl @@ -0,0 +1,250 @@ +#= + +# 225: Terminal flux calculation via test functions, nD +([source code](@__SOURCE_URL__)) + +After calculating solutions based on the finite volume method, it +may be interesting to obtain information about the solution besides of the graphical representation. + +Here, we focus on the following data: +- integrals of the solution +- flux through parts of the boundary + +Let us define the following reaction - diffusion system in a domain $\Omega$: + +```math +\begin{aligned} +\partial_t u_1 - \nabla \cdot \nabla u_1 + r(u_1, u_2) &= f=1.0\\ +\partial_t u_2 - \nabla \cdot \nabla u_1 - r(u_1, u_2) &= 0 +\end{aligned} +``` + +with boundary conditions $u_2=0$ on $\Gamma_2\subset\partial\Omega$ and +$r(u_1,u_2)=u_1 + 0.1 u_2$ + +The source $f$ creates species $u_1$ which reacts to $u_2$, $u_2$ then leaves +the domain at boundary $\Gamma_2$. + +### Stationary problem + +For the stationary problem, we have the following flux balances derived from the equations and from Gauss theorem: + +```math +\begin{aligned} +\int_\Omega r(u_1,u_2) d\omega &= \int_\Omega f d\omega \\ +\int_\Omega -r(u_1,u_2) d\omega &= \int_{\Gamma_2} \nabla u \cdot \vec n ds \\ +\end{aligned} +``` + +The volume integrals can be approximated based on the finite volume subdivision +$\Omega=\cup_{i\in \mathcal N} \omega_i$: + +```math +\begin{aligned} +\int_\Omega r(u_1,u_2) d\omega &\approx \sum_{i\in \mathcal N} |\omega_i| r(u_{1,i},u_{2,i})\\ +\int_\Omega f d\omega &\approx \sum_{i\in \mathcal N} |\omega_i| f_i +\end{aligned} +``` + +But what about the boundary integral ? Here, we use a trick to cast +the surface integral to the integral to a volume integral with the +help of a test function: + +Let $T(x)$ be the solution of the Laplace problem $-\nabla^2 T =0$ in $\Omega$ and the boundary conditions + +```math +\begin{aligned} +T &=0 \quad \text{at}\; \Gamma_4\\ +T &=1 \quad \text{at}\; \Gamma_2\\ +\partial_n T &=0\quad \text{at}\; \Gamma_1,\Gamma_3 +\end{aligned} +``` + +Write ``\vec j=-\nabla u``. and assume $\nabla\cdot \vec j + r =f$. + +```math +\begin{aligned} +\int_{\Gamma_2} \vec j \cdot \vec n ds&=\int_{\Gamma_2} T\vec j \cdot \vec n ds \quad \text{due to}\; T=1\; \text{on}\; \Gamma_2\\ + &=\int_{\partial\Omega} T\vec j \cdot \vec n ds\quad \text{due to}\; T=0\; \text{on}\; \Gamma_4, \quad\vec j\cdot \vec n=0\; \text{on}\; \Gamma_1, \Gamma_3\\ +&= \int_\Omega \nabla \cdot (T \vec j) d\omega \quad \text{(Gauss)}\\ +&= \int_\Omega \nabla T \cdot \vec j d\omega + \int_\Omega T \nabla\cdot j d\omega\\ +&= \int_\Omega \nabla T \cdot \vec j d\omega + \int_\Omega T(f-r)dω\\ +\end{aligned} +``` + +and we approximate + +```math +\begin{aligned} +\int_\Omega \nabla T \cdot \vec j d\omega \approx \sum_{k,l} +\frac{|\omega_k\cap\omega_l|}{h_{k,l}}g(u_k, u_l) (T_k-T_l) +\end{aligned} +``` + +where the sum runs over pairs of neighboring control volumes. + +The `integrate` method with a test function parameter returns a value +for each species, the sign convention assumes that species leaving the +domain lead to negative values. + +### Transient problem + +The amount of species created via the source term (measured in `F`) +integrated over time should be equal to the sum of the amount of +species left in the domain at the very end of the evolution and the +amount of species which left the domain: + +$\int_{t_0}^{t_{end}} \int_\Omega f d\omega dt= \int_\Omega (u_1+u_2)dω + \int_{t_0}^{t_{end}} \int_{\Gamma_2} \nabla u_2 \cdot \vec n ds$ + +Literature references: + +- H. Gajewski "Analysis und Numerik von Ladungstransport in Halbleitern", WIAS Berlin, Report No.6 +- Yoder, P. D., K. Gärtner, and W. Fichtner. "A generalized Ramo–Shockley theorem for classical to quantum transport at arbitrary frequencies." Journal of Applied Physics 79.4 (1996): 1951-1954. +- P. Farrell, N. Rotundo, D. H. Doan, M. Kantner, J. Fuhrmann, and T. Koprucki, "Numerical methods for drift-diffusion models", in Handbook of optoelectronic device modeling and simulation: Lasers, modulators, photodetectors, solar cells, and numerical methods, vol. 2, J. Piprek, Ed. Boca Raton: CRC Press, 2017, pp. 733–771. + +=# + +module Example225_TestFunctions2D + +using VoronoiFVM, GridVisualize, ExtendableGrids + +function main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise, + dim = 2, tend = 5, dt = 0.2) + n = [101, 21, 5] + X = collect(range(0.0, 1; length = n[dim])) + if dim == 1 + grid = simplexgrid(X) + Γ_where_T_equal_1 = [2] + Γ_where_T_equal_0 = [1] + elseif dim == 2 + grid = simplexgrid(X, X) + Γ_where_T_equal_1 = [2] + Γ_where_T_equal_0 = [4] + elseif dim == 3 + grid = simplexgrid(X, X, X) + Γ_where_T_equal_1 = [2] + Γ_where_T_equal_0 = [4] + end + + function storage(f, u, node) + f .= u + end + + function flux(f, u, edge) + f[1] = u[1, 1] - u[1, 2] + f[2] = u[2, 1] - u[2, 2] + end + + r(u1, u2) = u1 - 0.1 * u2 + + function reaction(f, u, node) + f[1] = r(u[1], u[2]) + f[2] = -r(u[1], u[2]) + end + + function source(f, node) + f[1] = 1.0 + end + + physics = VoronoiFVM.Physics(; flux = flux, + storage = storage, + reaction = reaction, + source = source) + + system = VoronoiFVM.System(grid, physics; assembly = assembly) + + enable_species!(system, 1, [1]) + enable_species!(system, 2, [1]) + boundary_dirichlet!(system, 2, 2, 0.0) + + sol = solve(system; inival = 0.0) + + vis = GridVisualizer(; Plotter = Plotter, layout = (1, 2), resolution = (600, 300), + fignumber = 1) + scalarplot!(vis[1, 1], grid, sol[1, :]; flimits = (0, 1.5), title = "u_1") + scalarplot!(vis[1, 2], grid, sol[2, :]; flimits = (0, 1.5), title = "u_2", show = true) + + """ + The `integrate` method of `VoronoiFVM` provides a possibility to calculate + the volume integral of a function of a solution as described above. + It returns a `num_specie` x `num_regions` matrix of the integrals + of the function of the unknowns over the different subdomains (here, we have only one): + """ + + """ + Amount of u_1 and u_2 in the domain aka integral over identity storage function: + """ + U = integrate(system, storage, sol) + + """ + Amount of species created by source term per unit time: + """ + F = integrate(system, (f, u, node) -> source(f, node), sol) + + """ + Amount of reaction per unit time: + """ + R = integrate(system, reaction, sol) + + tf = TestFunctionFactory(system) + T = testfunction(tf, Γ_where_T_equal_0, Γ_where_T_equal_1) + + I = integrate(system, T, sol) + + t0 = 0.0 + + control = fixed_timesteps!(VoronoiFVM.NewtonControl(), dt) + + tsol = solve(system; inival = 0.0, times = [t0, tend], control) + + vis1 = GridVisualizer(; Plotter = Plotter, layout = (1, 2), resolution = (600, 300), + fignumber = 4) + + for i = 1:length(tsol) + sol = tsol[i] + scalarplot!(vis1[1, 1], grid, sol[1, :]; flimits = (0, 1.5), clear = true) + scalarplot!(vis1[1, 2], grid, sol[2, :]; flimits = (0, 1.5), show = true) + end + + outflow_rate = Float64[] + for i = 2:length(tsol) + ofr = integrate(system, T, tsol[i], tsol[i - 1], tsol.t[i] - tsol.t[i - 1]) + push!(outflow_rate, ofr[2]) + end + + vis2 = GridVisualizer(; Plotter = Plotter, layout = (1, 1), resolution = (600, 300), + fignumber = 2) + scalarplot!(vis2[1, 1], [0, tend], -[I[2], I[2]]; label = "stationary", clear = true) + scalarplot!(vis2[1, 1], tsol.t[2:end], -outflow_rate; label = "transient", show = true) + + all_outflow = 0.0 + for i = 1:(length(tsol) - 1) + all_outflow -= outflow_rate[i] * (tsol.t[i + 1] - tsol.t[i]) + end + + Uend = integrate(system, storage, tsol[end]) + isapprox(F[1], R[1]; rtol = 1.0e-12) ? true : return false + isapprox(I[1], 0.0; atol = 1.0e-12) ? true : return false + isapprox(R[2], I[2]; rtol = 1.0e-12) ? true : return false + isapprox(F[1] * (tend - t0), (Uend[1] + Uend[2] + all_outflow); rtol = 1.0e-12) ? true : + return false +end + +using Test +function runtests() + @test main(; dim = 1, unknown_storage = :sparse, assembly = :edgewise) + @test main(; dim = 1, unknown_storage = :dense, assembly = :edgewise) + @test main(; dim = 2, unknown_storage = :sparse, assembly = :edgewise) + @test main(; dim = 2, unknown_storage = :dense, assembly = :edgewise) + @test main(; dim = 3, unknown_storage = :sparse, assembly = :edgewise) + @test main(; dim = 3, unknown_storage = :dense, assembly = :edgewise) + + @test main(; dim = 1, unknown_storage = :sparse, assembly = :cellwise) + @test main(; dim = 1, unknown_storage = :dense, assembly = :cellwise) + @test main(; dim = 2, unknown_storage = :sparse, assembly = :cellwise) + @test main(; dim = 2, unknown_storage = :dense, assembly = :cellwise) + @test main(; dim = 3, unknown_storage = :sparse, assembly = :cellwise) + @test main(; dim = 3, unknown_storage = :dense, assembly = :cellwise) +end + +end diff --git a/v1.19.1/module_examples/Example225_TestFunctions2D/index.html b/v1.19.1/module_examples/Example225_TestFunctions2D/index.html new file mode 100644 index 000000000..ef5694fcb --- /dev/null +++ b/v1.19.1/module_examples/Example225_TestFunctions2D/index.html @@ -0,0 +1,167 @@ + +225: Terminal flux calculation via test functions, nD · VoronoiFVM.jl

    225: Terminal flux calculation via test functions, nD

    (source code)

    After calculating solutions based on the finite volume method, it may be interesting to obtain information about the solution besides of the graphical representation.

    Here, we focus on the following data:

    • integrals of the solution
    • flux through parts of the boundary

    Let us define the following reaction - diffusion system in a domain $\Omega$:

    \[\begin{aligned} +\partial_t u_1 - \nabla \cdot \nabla u_1 + r(u_1, u_2) &= f=1.0\\ +\partial_t u_2 - \nabla \cdot \nabla u_1 - r(u_1, u_2) &= 0 +\end{aligned}\]

    with boundary conditions $u_2=0$ on $\Gamma_2\subset\partial\Omega$ and $r(u_1,u_2)=u_1 + 0.1 u_2$

    The source $f$ creates species $u_1$ which reacts to $u_2$, $u_2$ then leaves the domain at boundary $\Gamma_2$.

    Stationary problem

    For the stationary problem, we have the following flux balances derived from the equations and from Gauss theorem:

    \[\begin{aligned} +\int_\Omega r(u_1,u_2) d\omega &= \int_\Omega f d\omega \\ +\int_\Omega -r(u_1,u_2) d\omega &= \int_{\Gamma_2} \nabla u \cdot \vec n ds \\ +\end{aligned}\]

    The volume integrals can be approximated based on the finite volume subdivision $\Omega=\cup_{i\in \mathcal N} \omega_i$:

    \[\begin{aligned} +\int_\Omega r(u_1,u_2) d\omega &\approx \sum_{i\in \mathcal N} |\omega_i| r(u_{1,i},u_{2,i})\\ +\int_\Omega f d\omega &\approx \sum_{i\in \mathcal N} |\omega_i| f_i +\end{aligned}\]

    But what about the boundary integral ? Here, we use a trick to cast the surface integral to the integral to a volume integral with the help of a test function:

    Let $T(x)$ be the solution of the Laplace problem $-\nabla^2 T =0$ in $\Omega$ and the boundary conditions

    \[\begin{aligned} +T &=0 \quad \text{at}\; \Gamma_4\\ +T &=1 \quad \text{at}\; \Gamma_2\\ +\partial_n T &=0\quad \text{at}\; \Gamma_1,\Gamma_3 +\end{aligned}\]

    Write $\vec j=-\nabla u$. and assume $\nabla\cdot \vec j + r =f$.

    \[\begin{aligned} +\int_{\Gamma_2} \vec j \cdot \vec n ds&=\int_{\Gamma_2} T\vec j \cdot \vec n ds \quad \text{due to}\; T=1\; \text{on}\; \Gamma_2\\ + &=\int_{\partial\Omega} T\vec j \cdot \vec n ds\quad \text{due to}\; T=0\; \text{on}\; \Gamma_4, \quad\vec j\cdot \vec n=0\; \text{on}\; \Gamma_1, \Gamma_3\\ +&= \int_\Omega \nabla \cdot (T \vec j) d\omega \quad \text{(Gauss)}\\ +&= \int_\Omega \nabla T \cdot \vec j d\omega + \int_\Omega T \nabla\cdot j d\omega\\ +&= \int_\Omega \nabla T \cdot \vec j d\omega + \int_\Omega T(f-r)dω\\ +\end{aligned}\]

    and we approximate

    \[\begin{aligned} +\int_\Omega \nabla T \cdot \vec j d\omega \approx \sum_{k,l} +\frac{|\omega_k\cap\omega_l|}{h_{k,l}}g(u_k, u_l) (T_k-T_l) +\end{aligned}\]

    where the sum runs over pairs of neighboring control volumes.

    The integrate method with a test function parameter returns a value for each species, the sign convention assumes that species leaving the domain lead to negative values.

    Transient problem

    The amount of species created via the source term (measured in F) integrated over time should be equal to the sum of the amount of species left in the domain at the very end of the evolution and the amount of species which left the domain:

    \[\int_{t_0}^{t_{end}} \int_\Omega f d\omega dt= \int_\Omega (u_1+u_2)dω + \int_{t_0}^{t_{end}} \int_{\Gamma_2} \nabla u_2 \cdot \vec n ds\]

    Literature references:

    • H. Gajewski "Analysis und Numerik von Ladungstransport in Halbleitern", WIAS Berlin, Report No.6
    • Yoder, P. D., K. Gärtner, and W. Fichtner. "A generalized Ramo–Shockley theorem for classical to quantum transport at arbitrary frequencies." Journal of Applied Physics 79.4 (1996): 1951-1954.
    • P. Farrell, N. Rotundo, D. H. Doan, M. Kantner, J. Fuhrmann, and T. Koprucki, "Numerical methods for drift-diffusion models", in Handbook of optoelectronic device modeling and simulation: Lasers, modulators, photodetectors, solar cells, and numerical methods, vol. 2, J. Piprek, Ed. Boca Raton: CRC Press, 2017, pp. 733–771.
    module Example225_TestFunctions2D
    +
    +using VoronoiFVM, GridVisualize, ExtendableGrids
    +
    +function main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise,
    +              dim = 2, tend = 5, dt = 0.2)
    +    n = [101, 21, 5]
    +    X = collect(range(0.0, 1; length = n[dim]))
    +    if dim == 1
    +        grid = simplexgrid(X)
    +        Γ_where_T_equal_1 = [2]
    +        Γ_where_T_equal_0 = [1]
    +    elseif dim == 2
    +        grid = simplexgrid(X, X)
    +        Γ_where_T_equal_1 = [2]
    +        Γ_where_T_equal_0 = [4]
    +    elseif dim == 3
    +        grid = simplexgrid(X, X, X)
    +        Γ_where_T_equal_1 = [2]
    +        Γ_where_T_equal_0 = [4]
    +    end
    +
    +    function storage(f, u, node)
    +        f .= u
    +    end
    +
    +    function flux(f, u, edge)
    +        f[1] = u[1, 1] - u[1, 2]
    +        f[2] = u[2, 1] - u[2, 2]
    +    end
    +
    +    r(u1, u2) = u1 - 0.1 * u2
    +
    +    function reaction(f, u, node)
    +        f[1] = r(u[1], u[2])
    +        f[2] = -r(u[1], u[2])
    +    end
    +
    +    function source(f, node)
    +        f[1] = 1.0
    +    end
    +
    +    physics = VoronoiFVM.Physics(; flux = flux,
    +                                 storage = storage,
    +                                 reaction = reaction,
    +                                 source = source)
    +
    +    system = VoronoiFVM.System(grid, physics; assembly = assembly)
    +
    +    enable_species!(system, 1, [1])
    +    enable_species!(system, 2, [1])
    +    boundary_dirichlet!(system, 2, 2, 0.0)
    +
    +    sol = solve(system; inival = 0.0)
    +
    +    vis = GridVisualizer(; Plotter = Plotter, layout = (1, 2), resolution = (600, 300),
    +                         fignumber = 1)
    +    scalarplot!(vis[1, 1], grid, sol[1, :]; flimits = (0, 1.5), title = "u_1")
    +    scalarplot!(vis[1, 2], grid, sol[2, :]; flimits = (0, 1.5), title = "u_2", show = true)
    +
    +    """
    +        The `integrate` method of `VoronoiFVM`  provides a possibility to calculate
    +        the volume integral of a function of a solution as described above.
    +        It returns a `num_specie` x `num_regions` matrix of the integrals
    +        of the function of the unknowns over the different subdomains (here, we have only one):
    +    """
    +
    +    """
    +        Amount of u_1 and u_2 in the domain aka integral over identity storage function:
    +    """
    +    U = integrate(system, storage, sol)
    +
    +    """
    +    Amount of species created by source term per unit time:
    +    """
    +    F = integrate(system, (f, u, node) -> source(f, node), sol)
    +
    +    """
    +    Amount of  reaction per unit time:
    +    """
    +    R = integrate(system, reaction, sol)
    +
    +    tf = TestFunctionFactory(system)
    +    T = testfunction(tf, Γ_where_T_equal_0, Γ_where_T_equal_1)
    +
    +    I = integrate(system, T, sol)
    +
    +    t0 = 0.0
    +
    +    control = fixed_timesteps!(VoronoiFVM.NewtonControl(), dt)
    +
    +    tsol = solve(system; inival = 0.0, times = [t0, tend], control)
    +
    +    vis1 = GridVisualizer(; Plotter = Plotter, layout = (1, 2), resolution = (600, 300),
    +                          fignumber = 4)
    +
    +    for i = 1:length(tsol)
    +        sol = tsol[i]
    +        scalarplot!(vis1[1, 1], grid, sol[1, :]; flimits = (0, 1.5), clear = true)
    +        scalarplot!(vis1[1, 2], grid, sol[2, :]; flimits = (0, 1.5), show = true)
    +    end
    +
    +    outflow_rate = Float64[]
    +    for i = 2:length(tsol)
    +        ofr = integrate(system, T, tsol[i], tsol[i - 1], tsol.t[i] - tsol.t[i - 1])
    +        push!(outflow_rate, ofr[2])
    +    end
    +
    +    vis2 = GridVisualizer(; Plotter = Plotter, layout = (1, 1), resolution = (600, 300),
    +                          fignumber = 2)
    +    scalarplot!(vis2[1, 1], [0, tend], -[I[2], I[2]]; label = "stationary", clear = true)
    +    scalarplot!(vis2[1, 1], tsol.t[2:end], -outflow_rate; label = "transient", show = true)
    +
    +    all_outflow = 0.0
    +    for i = 1:(length(tsol) - 1)
    +        all_outflow -= outflow_rate[i] * (tsol.t[i + 1] - tsol.t[i])
    +    end
    +
    +    Uend = integrate(system, storage, tsol[end])
    +    isapprox(F[1], R[1]; rtol = 1.0e-12) ? true : return false
    +    isapprox(I[1], 0.0; atol = 1.0e-12) ? true : return false
    +    isapprox(R[2], I[2]; rtol = 1.0e-12) ? true : return false
    +    isapprox(F[1] * (tend - t0), (Uend[1] + Uend[2] + all_outflow); rtol = 1.0e-12) ? true :
    +    return false
    +end
    +
    +using Test
    +function runtests()
    +    @test main(; dim = 1, unknown_storage = :sparse, assembly = :edgewise)
    +    @test main(; dim = 1, unknown_storage = :dense, assembly = :edgewise)
    +    @test main(; dim = 2, unknown_storage = :sparse, assembly = :edgewise)
    +    @test main(; dim = 2, unknown_storage = :dense, assembly = :edgewise)
    +    @test main(; dim = 3, unknown_storage = :sparse, assembly = :edgewise)
    +    @test main(; dim = 3, unknown_storage = :dense, assembly = :edgewise)
    +
    +    @test main(; dim = 1, unknown_storage = :sparse, assembly = :cellwise)
    +    @test main(; dim = 1, unknown_storage = :dense, assembly = :cellwise)
    +    @test main(; dim = 2, unknown_storage = :sparse, assembly = :cellwise)
    +    @test main(; dim = 2, unknown_storage = :dense, assembly = :cellwise)
    +    @test main(; dim = 3, unknown_storage = :sparse, assembly = :cellwise)
    +    @test main(; dim = 3, unknown_storage = :dense, assembly = :cellwise)
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example226_BoundaryIntegral.jl b/v1.19.1/module_examples/Example226_BoundaryIntegral.jl new file mode 100644 index 000000000..b96d19d79 --- /dev/null +++ b/v1.19.1/module_examples/Example226_BoundaryIntegral.jl @@ -0,0 +1,80 @@ +#= + +# 226: Terminal flux calculation via test functions, nD, boundary reaction +([source code](@__SOURCE_URL__)) + +=# + +module Example226_BoundaryIntegral + +using VoronoiFVM, GridVisualize, ExtendableGrids + +function main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse, + dim = 2, assembly = :edgewise) + n = [101, 21, 5] + X = collect(range(0.0, 1; length = n[dim])) + if dim == 1 + grid = simplexgrid(X) + Γ_where_T_equal_1 = [2] + Γ_where_T_equal_0 = [1] + elseif dim == 2 + grid = simplexgrid(X, X) + Γ_where_T_equal_1 = [2] + Γ_where_T_equal_0 = [4] + elseif dim == 3 + grid = simplexgrid(X, X, X) + Γ_where_T_equal_1 = [2] + Γ_where_T_equal_0 = [4] + end + + function storage(f, u, node) + f .= u + end + + function flux(f, u, edge) + f[1] = u[1, 1] - u[1, 2] + end + + function breaction(f, u, node) + if node.region == Γ_where_T_equal_1[1] + f[1] = u[1]^2 + end + end + + physics = VoronoiFVM.Physics(; flux = flux, + storage = storage, + breaction = breaction) + + system = VoronoiFVM.System(grid, physics; assembly = assembly) + enable_species!(system, 1, [1]) + boundary_dirichlet!(system, 1, Γ_where_T_equal_0[1], 1.0) + + U = solve(system; inival = 0) + + tf = TestFunctionFactory(system) + T = testfunction(tf, Γ_where_T_equal_0, Γ_where_T_equal_1) + + scalarplot(grid, U[1, :]; Plotter = Plotter, zplane = 0.50001) + I = integrate(system, T, U) + B = integrate(system, breaction, U; boundary = true) + isapprox(-I[1], B[Γ_where_T_equal_1[1]]; rtol = 1.0e-12) +end + +using Test +function runtests() + @test main(; dim = 1, unknown_storage = :sparse, assembly = :edgewise) + @test main(; dim = 1, unknown_storage = :dense, assembly = :edgewise) + @test main(; dim = 2, unknown_storage = :sparse, assembly = :edgewise) + @test main(; dim = 2, unknown_storage = :dense, assembly = :edgewise) + @test main(; dim = 3, unknown_storage = :sparse, assembly = :edgewise) + @test main(; dim = 3, unknown_storage = :dense, assembly = :edgewise) + + @test main(; dim = 1, unknown_storage = :sparse, assembly = :cellwise) + @test main(; dim = 1, unknown_storage = :dense, assembly = :cellwise) + @test main(; dim = 2, unknown_storage = :sparse, assembly = :cellwise) + @test main(; dim = 2, unknown_storage = :dense, assembly = :cellwise) + @test main(; dim = 3, unknown_storage = :sparse, assembly = :cellwise) + @test main(; dim = 3, unknown_storage = :dense, assembly = :cellwise) +end + +end diff --git a/v1.19.1/module_examples/Example226_BoundaryIntegral/index.html b/v1.19.1/module_examples/Example226_BoundaryIntegral/index.html new file mode 100644 index 000000000..5ad079261 --- /dev/null +++ b/v1.19.1/module_examples/Example226_BoundaryIntegral/index.html @@ -0,0 +1,74 @@ + +226: Terminal flux calculation via test functions, nD, boundary reaction · VoronoiFVM.jl

    226: Terminal flux calculation via test functions, nD, boundary reaction

    (source code)

    module Example226_BoundaryIntegral
    +
    +using VoronoiFVM, GridVisualize, ExtendableGrids
    +
    +function main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse,
    +              dim = 2, assembly = :edgewise)
    +    n = [101, 21, 5]
    +    X = collect(range(0.0, 1; length = n[dim]))
    +    if dim == 1
    +        grid = simplexgrid(X)
    +        Γ_where_T_equal_1 = [2]
    +        Γ_where_T_equal_0 = [1]
    +    elseif dim == 2
    +        grid = simplexgrid(X, X)
    +        Γ_where_T_equal_1 = [2]
    +        Γ_where_T_equal_0 = [4]
    +    elseif dim == 3
    +        grid = simplexgrid(X, X, X)
    +        Γ_where_T_equal_1 = [2]
    +        Γ_where_T_equal_0 = [4]
    +    end
    +
    +    function storage(f, u, node)
    +        f .= u
    +    end
    +
    +    function flux(f, u, edge)
    +        f[1] = u[1, 1] - u[1, 2]
    +    end
    +
    +    function breaction(f, u, node)
    +        if node.region == Γ_where_T_equal_1[1]
    +            f[1] = u[1]^2
    +        end
    +    end
    +
    +    physics = VoronoiFVM.Physics(; flux = flux,
    +                                 storage = storage,
    +                                 breaction = breaction)
    +
    +    system = VoronoiFVM.System(grid, physics; assembly = assembly)
    +    enable_species!(system, 1, [1])
    +    boundary_dirichlet!(system, 1, Γ_where_T_equal_0[1], 1.0)
    +
    +    U = solve(system; inival = 0)
    +
    +    tf = TestFunctionFactory(system)
    +    T = testfunction(tf, Γ_where_T_equal_0, Γ_where_T_equal_1)
    +
    +    scalarplot(grid, U[1, :]; Plotter = Plotter, zplane = 0.50001)
    +    I = integrate(system, T, U)
    +    B = integrate(system, breaction, U; boundary = true)
    +    isapprox(-I[1], B[Γ_where_T_equal_1[1]]; rtol = 1.0e-12)
    +end
    +
    +using Test
    +function runtests()
    +    @test main(; dim = 1, unknown_storage = :sparse, assembly = :edgewise)
    +    @test main(; dim = 1, unknown_storage = :dense, assembly = :edgewise)
    +    @test main(; dim = 2, unknown_storage = :sparse, assembly = :edgewise)
    +    @test main(; dim = 2, unknown_storage = :dense, assembly = :edgewise)
    +    @test main(; dim = 3, unknown_storage = :sparse, assembly = :edgewise)
    +    @test main(; dim = 3, unknown_storage = :dense, assembly = :edgewise)
    +
    +    @test main(; dim = 1, unknown_storage = :sparse, assembly = :cellwise)
    +    @test main(; dim = 1, unknown_storage = :dense, assembly = :cellwise)
    +    @test main(; dim = 2, unknown_storage = :sparse, assembly = :cellwise)
    +    @test main(; dim = 2, unknown_storage = :dense, assembly = :cellwise)
    +    @test main(; dim = 3, unknown_storage = :sparse, assembly = :cellwise)
    +    @test main(; dim = 3, unknown_storage = :dense, assembly = :cellwise)
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example230_BoundaryFlux.jl b/v1.19.1/module_examples/Example230_BoundaryFlux.jl new file mode 100644 index 000000000..c85b07414 --- /dev/null +++ b/v1.19.1/module_examples/Example230_BoundaryFlux.jl @@ -0,0 +1,187 @@ +#= + +# 103: Boundary flux +([source code](@__SOURCE_URL__)) + +We consider two test problems. + +Testproblem A: Consider in $\Omega_1=(0,1)$ +```math +- d_1 \Delta u_1 + k_1 u_1 = c_1 +``` +in with homogeneous Neumann boundary conditions. + +Testproblem B: Consider in $\Omega_2=(0,1) x (0, 1) $ +```math +- d_2 \Delta u_2 + k_2 u_2 = c_2 +``` +in with homogeneous Neumann boundary conditions +and at the right boundary, i.e. $ {1} x (0, 1) $ +```math +- d_b \Delta v + k_b v = c_b. +``` +If d_1 = d_b, k_1 = k_b and c_1 = c_b, then u and v should coincide. +=# + +module Example230_BoundaryFlux + +using VoronoiFVM +using ExtendableGrids +using GridVisualize + +function main(; n = 2 * 10, # n musst be an even number + d1 = 5.0, db = 5.0, # prefactors (before diffusive part) + kmax = 2.0, cmax = 3.0, + Plotter = nothing, + unknown_storage = :sparse, assembly = :edgewise) + + ########################################################################### + ###################### 1D problem ###################### + ########################################################################### + + ispec_1D = 1 + bulk_1D = 1 + + X = range(0.0; stop = 1.0, length = n) + length_x = length(X) + length_x_half = Int(length_x / 2) + + grid_1D = simplexgrid(X) + + k1 = zeros(length_x) + c1 = zeros(length_x) + + k1[1:length_x_half] .= kmax + k1[(length_x_half + 1):length_x] .= 0.0 # prefactor before reactive part + c1[1:length_x_half] .= 0.0 + c1[(length_x_half + 1):length_x] .= cmax # source term + + #### discretization functions #### + + function flux!(f, u, edge) + f[1] = d1 * (u[1, 1] - u[1, 2]) + end + + function reaction!(f, u, node) + f[1] = k1[node.index] * u[1] + end + + function source!(f, node::VoronoiFVM.Node) + f[1] = c1[node.index] + end + + sys_1D = VoronoiFVM.System(grid_1D, + VoronoiFVM.Physics(; flux = flux!, reaction = reaction!, + source = source!)) + + # enable species in only region + enable_species!(sys_1D, ispec_1D, [bulk_1D]) + + ## Stationary solution of both problems + sol_1D = solve(sys_1D; inival = 0) + + p = GridVisualizer(; Plotter = Plotter, layout = (2, 1), clear = true, + resolution = (800, 500)) + + scalarplot!(p[1, 1], grid_1D, sol_1D[1, :]; show = true, + title = "1D calculation (d1 = $d1, kmax = $kmax, cmax = $cmax)") + + ########################################################################### + ###################### 2D problem ###################### + ########################################################################### + + grid_2D = simplexgrid(X, X) + + ispec_2D = 1 + ispec_boundary = 2 + bulk_2D = 1 + active_boundary = 2 + + # parameters for the bulk problem + d2 = 1.0 + k2 = 1.0 + c2 = 1.0 + + #### discretization functions for bulk species #### + function flux2D!(f, u, edge) + f[ispec_2D] = d2 * (u[ispec_2D, 1] - u[ispec_2D, 2]) + end + + function reaction2D!(f, u, node) + f[ispec_2D] = k2 * u[ispec_2D] + end + + function source2D!(f, node) + f[ispec_2D] = c2 + end + + #### discretization functions for boundary species at active boundary #### + function bflux!(f, u, bedge) + if bedge.region == active_boundary + f[ispec_boundary] = db * (u[ispec_boundary, 1] - u[ispec_boundary, 2]) + end + end + + function breaction!(f, u, bnode) + if bnode.region == active_boundary + if bnode.coord[2, bnode.index] <= 0.5 + kb = kmax + else + kb = 0.0 + end + + f[ispec_boundary] = kb * u[ispec_boundary] + end + end + + function bsource!(f, bnode) + if bnode.region == active_boundary + if bnode.coord[2, bnode.index] <= 0.5 + cb = 0.0 + else + cb = cmax + end + + f[ispec_boundary] = cb + end + end + + sys_2D = VoronoiFVM.System(grid_2D, + VoronoiFVM.Physics(; flux = flux2D!, reaction = reaction2D!, + source = source2D!, + bflux = bflux!, breaction = breaction!, + bsource = bsource!); + unknown_storage = unknown_storage, assembly = assembly) + + # enable species in only region + enable_species!(sys_2D, ispec_2D, [bulk_2D]) + enable_boundary_species!(sys_2D, ispec_boundary, [active_boundary]) + + sol_2D = solve(sys_2D; inival = 0) + + # this is for variable transformation, since we consider right outer boundary and want to transform to x-axis. + function tran32!(a, b) + a[1] = b[2] + end + + # note that if adjusting active_boundary to 3 or 4, then transform needs to be deleted. + bgrid_2D = subgrid(grid_2D, [active_boundary]; boundary = true, transform = tran32!) + sol_bound = view(sol_2D[ispec_boundary, :], bgrid_2D) + + scalarplot!(p[2, 1], bgrid_2D, sol_bound; show = true, cellwise = true, + title = "Active boundary in 2D (db = $db, kb = $kmax, cb = $cmax)") + + errorsol = VoronoiFVM.norm(sys_1D, sol_bound - sol_1D', 2) + + return errorsol +end # main + +using Test +function runtests() + @test main(; unknown_storage = :dense, assembly = :edgewise) < 1.0e-14 && + main(; unknown_storage = :sparse, assembly = :edgewise) < 1.0e-14 && + main(; unknown_storage = :dense, assembly = :cellwise) < 1.0e-14 && + main(; unknown_storage = :sparse, assembly = :cellwise) < 1.0e-14 +end + +end # module diff --git a/v1.19.1/module_examples/Example230_BoundaryFlux/index.html b/v1.19.1/module_examples/Example230_BoundaryFlux/index.html new file mode 100644 index 000000000..2156df875 --- /dev/null +++ b/v1.19.1/module_examples/Example230_BoundaryFlux/index.html @@ -0,0 +1,148 @@ + +103: Boundary flux · VoronoiFVM.jl

    103: Boundary flux

    (source code)

    We consider two test problems.

    Testproblem A: Consider in $\Omega_1=(0,1)$

    \[- d_1 \Delta u_1 + k_1 u_1 = c_1\]

    in with homogeneous Neumann boundary conditions.

    Testproblem B: Consider in \Omega_2=(0,1) x (0, 1) $

    \[- d_2 \Delta u_2 + k_2 u_2 = c_2\]

    in with homogeneous Neumann boundary conditions and at the right boundary, i.e. $ {1} x (0, 1) $

    \[- d_b \Delta v + k_b v = c_b.\]

    If d1 = db, k1 = kb and c1 = cb, then u and v should coincide.

    module Example230_BoundaryFlux
    +
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +
    +function main(; n = 2 * 10, # n musst be an even number
    +              d1 = 5.0, db = 5.0, # prefactors (before diffusive part)
    +              kmax = 2.0, cmax = 3.0,
    +              Plotter = nothing,
    +              unknown_storage = :sparse, assembly = :edgewise)
    +
    +    ###########################################################################
    +    ######################          1D problem           ######################
    +    ###########################################################################
    +
    +    ispec_1D = 1
    +    bulk_1D = 1
    +
    +    X = range(0.0; stop = 1.0, length = n)
    +    length_x = length(X)
    +    length_x_half = Int(length_x / 2)
    +
    +    grid_1D = simplexgrid(X)
    +
    +    k1 = zeros(length_x)
    +    c1 = zeros(length_x)
    +
    +    k1[1:length_x_half] .= kmax
    +    k1[(length_x_half + 1):length_x] .= 0.0  # prefactor before reactive part
    +    c1[1:length_x_half] .= 0.0
    +    c1[(length_x_half + 1):length_x] .= cmax # source term
    +
    +    #### discretization functions ####
    +
    +    function flux!(f, u, edge)
    +        f[1] = d1 * (u[1, 1] - u[1, 2])
    +    end
    +
    +    function reaction!(f, u, node)
    +        f[1] = k1[node.index] * u[1]
    +    end
    +
    +    function source!(f, node::VoronoiFVM.Node)
    +        f[1] = c1[node.index]
    +    end
    +
    +    sys_1D = VoronoiFVM.System(grid_1D,
    +                               VoronoiFVM.Physics(; flux = flux!, reaction = reaction!,
    +                                                  source = source!))

    enable species in only region

        enable_species!(sys_1D, ispec_1D, [bulk_1D])
    +
    +    # Stationary solution of both problems
    +    sol_1D = solve(sys_1D; inival = 0)
    +
    +    p = GridVisualizer(; Plotter = Plotter, layout = (2, 1), clear = true,
    +                       resolution = (800, 500))
    +
    +    scalarplot!(p[1, 1], grid_1D, sol_1D[1, :]; show = true,
    +                title = "1D calculation (d1 = $d1, kmax = $kmax, cmax = $cmax)")
    +
    +    ###########################################################################
    +    ######################          2D problem           ######################
    +    ###########################################################################
    +
    +    grid_2D = simplexgrid(X, X)
    +
    +    ispec_2D = 1
    +    ispec_boundary = 2
    +    bulk_2D = 1
    +    active_boundary = 2

    parameters for the bulk problem

        d2 = 1.0
    +    k2 = 1.0
    +    c2 = 1.0
    +
    +    #### discretization functions for bulk species ####
    +    function flux2D!(f, u, edge)
    +        f[ispec_2D] = d2 * (u[ispec_2D, 1] - u[ispec_2D, 2])
    +    end
    +
    +    function reaction2D!(f, u, node)
    +        f[ispec_2D] = k2 * u[ispec_2D]
    +    end
    +
    +    function source2D!(f, node)
    +        f[ispec_2D] = c2
    +    end
    +
    +    #### discretization functions for boundary species at active boundary ####
    +    function bflux!(f, u, bedge)
    +        if bedge.region == active_boundary
    +            f[ispec_boundary] = db * (u[ispec_boundary, 1] - u[ispec_boundary, 2])
    +        end
    +    end
    +
    +    function breaction!(f, u, bnode)
    +        if bnode.region == active_boundary
    +            if bnode.coord[2, bnode.index] <= 0.5
    +                kb = kmax
    +            else
    +                kb = 0.0
    +            end
    +
    +            f[ispec_boundary] = kb * u[ispec_boundary]
    +        end
    +    end
    +
    +    function bsource!(f, bnode)
    +        if bnode.region == active_boundary
    +            if bnode.coord[2, bnode.index] <= 0.5
    +                cb = 0.0
    +            else
    +                cb = cmax
    +            end
    +
    +            f[ispec_boundary] = cb
    +        end
    +    end
    +
    +    sys_2D = VoronoiFVM.System(grid_2D,
    +                               VoronoiFVM.Physics(; flux = flux2D!, reaction = reaction2D!,
    +                                                  source = source2D!,
    +                                                  bflux = bflux!, breaction = breaction!,
    +                                                  bsource = bsource!);
    +                               unknown_storage = unknown_storage, assembly = assembly)

    enable species in only region

        enable_species!(sys_2D, ispec_2D, [bulk_2D])
    +    enable_boundary_species!(sys_2D, ispec_boundary, [active_boundary])
    +
    +    sol_2D = solve(sys_2D; inival = 0)

    this is for variable transformation, since we consider right outer boundary and want to transform to x-axis.

        function tran32!(a, b)
    +        a[1] = b[2]
    +    end

    note that if adjusting active_boundary to 3 or 4, then transform needs to be deleted.

        bgrid_2D = subgrid(grid_2D, [active_boundary]; boundary = true, transform = tran32!)
    +    sol_bound = view(sol_2D[ispec_boundary, :], bgrid_2D)
    +
    +    scalarplot!(p[2, 1], bgrid_2D, sol_bound; show = true, cellwise = true,
    +                title = "Active boundary in 2D (db = $db, kb = $kmax, cb = $cmax)")
    +
    +    errorsol = VoronoiFVM.norm(sys_1D, sol_bound - sol_1D', 2)
    +
    +    return errorsol
    +end # main
    +
    +using Test
    +function runtests()
    +    @test main(; unknown_storage = :dense, assembly = :edgewise) < 1.0e-14 &&
    +          main(; unknown_storage = :sparse, assembly = :edgewise) < 1.0e-14 &&
    +          main(; unknown_storage = :dense, assembly = :cellwise) < 1.0e-14 &&
    +          main(; unknown_storage = :sparse, assembly = :cellwise) < 1.0e-14
    +end
    +
    +end # module

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example301_Laplace3D.jl b/v1.19.1/module_examples/Example301_Laplace3D.jl new file mode 100644 index 000000000..d77becde0 --- /dev/null +++ b/v1.19.1/module_examples/Example301_Laplace3D.jl @@ -0,0 +1,48 @@ +#= + +# 301: 3D Laplace equation +([source code](@__SOURCE_URL__)) + +=# + +module Example301_Laplace3D + +using VoronoiFVM, ExtendableGrids +using GridVisualize + +## Flux function which describes the flux +## between neighboring control volumes +function g!(f, u, edge) + f[1] = u[1, 1] - u[1, 2] +end + +function s(f, node) + n = view(node.coord, :, node.index) + f[1] = n[1] * sin(5.0 * n[2]) * exp(n[3]) +end + +function main(; Plotter = nothing, n = 5, assembly = :edgewise) + nspecies = 1 + ispec = 1 + X = collect(0:(1 / n):1) + grid = VoronoiFVM.Grid(X, X, X) + physics = VoronoiFVM.Physics(; flux = g!, source = s) + sys = VoronoiFVM.System(grid, physics; assembly = assembly) + enable_species!(sys, ispec, [1]) + boundary_dirichlet!(sys, ispec, 5, 0.0) + boundary_dirichlet!(sys, ispec, 6, 0.0) + solution = solve(sys) + scalarplot(grid, solution[1, :]; Plotter = Plotter) + return solution[43] +end + +## Called by unit test + +using Test +function runtests() + testval = 0.012234524449380824 + @test main(; assembly = :edgewise) ≈ testval && + main(; assembly = :cellwise) ≈ testval +end + +end diff --git a/v1.19.1/module_examples/Example301_Laplace3D/index.html b/v1.19.1/module_examples/Example301_Laplace3D/index.html new file mode 100644 index 000000000..cf4c30f62 --- /dev/null +++ b/v1.19.1/module_examples/Example301_Laplace3D/index.html @@ -0,0 +1,42 @@ + +301: 3D Laplace equation · VoronoiFVM.jl

    301: 3D Laplace equation

    (source code)

    module Example301_Laplace3D
    +
    +using VoronoiFVM, ExtendableGrids
    +using GridVisualize
    +
    +# Flux function which describes the flux
    +# between neighboring control volumes
    +function g!(f, u, edge)
    +    f[1] = u[1, 1] - u[1, 2]
    +end
    +
    +function s(f, node)
    +    n = view(node.coord, :, node.index)
    +    f[1] = n[1] * sin(5.0 * n[2]) * exp(n[3])
    +end
    +
    +function main(; Plotter = nothing, n = 5, assembly = :edgewise)
    +    nspecies = 1
    +    ispec = 1
    +    X = collect(0:(1 / n):1)
    +    grid = VoronoiFVM.Grid(X, X, X)
    +    physics = VoronoiFVM.Physics(; flux = g!, source = s)
    +    sys = VoronoiFVM.System(grid, physics; assembly = assembly)
    +    enable_species!(sys, ispec, [1])
    +    boundary_dirichlet!(sys, ispec, 5, 0.0)
    +    boundary_dirichlet!(sys, ispec, 6, 0.0)
    +    solution = solve(sys)
    +    scalarplot(grid, solution[1, :]; Plotter = Plotter)
    +    return solution[43]
    +end
    +
    +# Called by unit test
    +
    +using Test
    +function runtests()
    +    testval = 0.012234524449380824
    +    @test main(; assembly = :edgewise) ≈ testval &&
    +          main(; assembly = :cellwise) ≈ testval
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example311_HeatEquation_BoundaryDiffusion.jl b/v1.19.1/module_examples/Example311_HeatEquation_BoundaryDiffusion.jl new file mode 100644 index 000000000..917a3cfb6 --- /dev/null +++ b/v1.19.1/module_examples/Example311_HeatEquation_BoundaryDiffusion.jl @@ -0,0 +1,114 @@ +#= + +# 311: Heat Equation with boundary diffusion +([source code](@__SOURCE_URL__)) + +=# + +module Example311_HeatEquation_BoundaryDiffusion +using Printf +using VoronoiFVM +using ExtendableGrids + +""" + We solve the following system + + ∂_tu - εΔu = 0 in [0,T] × Ω> + ε∇u⋅ν = k(u-v) on [0,T] × Γ_1 + ε∇u⋅ν = 0 on [0,T] × (∂Ω ∖ Γ_1) + ∂_tv -ε_ΓΔ_Γ v = f(x) +k(u-v) on [0,T] × Γ_1 + u(0) = 0.5 in {0} × Ω + v(0) = 0.5 on {0} × Γ_1 +""" + +function main(n = 1; assembly = :edgewise) + breg = 5 # boundary region number for surface diffusion + + hmin = 0.05 * 2.0^(-n + 1) + hmax = 0.2 * 2.0^(-n + 1) + XLeft = geomspace(0.0, 0.5, hmax, hmin) + XRight = geomspace(0.5, 1.0, hmin, hmax) + X = glue(XLeft, XRight) + + Z = geomspace(0.0, 1.0, hmin, 2 * hmax) + + grid = VoronoiFVM.Grid(X, X, Z) + + # parameters + eps = 1.0e0 # bulk heat conduction coefficient + eps_surf = 1.0e-2 # surface diffusion coefficient + k = 1.0 # transmission coefficient + physics = VoronoiFVM.Physics(; flux = function (f, u, edge) + f[1] = eps * (u[1, 1] - u[1, 2]) + end, + bflux = function (f, u, edge) + if edge.region == breg + f[2] = eps_surf * (u[2, 1] - u[2, 2]) + else + f[2] = 0.0 + end + end, + breaction = function (f, u, node) + if node.region == breg + f[1] = k * (u[1] - u[2]) + f[2] = k * (u[2] - u[1]) + else + f[1] = 0.0 + f[2] = 0.0 + end + end, + bsource = function (f, bnode) + x1 = bnode[1] - 0.5 + x2 = bnode[2] - 0.5 + x3 = bnode[3] - 0.5 + f[2] = 1.0e4 * exp(-20.0 * (x1^2 + x2^2 + x3^2)) + end, bstorage = function (f, u, node) + if node.region == breg + f[2] = u[2] + end + end, storage = function (f, u, node) + f[1] = u[1] + end) + + sys = VoronoiFVM.System(grid, physics; unknown_storage = :sparse, assembly) + enable_species!(sys, 1, [1]) + enable_boundary_species!(sys, 2, [breg]) + + function tran32!(a, b) + a[1] = b[2] + end + + bgrid2 = subgrid(grid, [breg]; boundary = true, transform = tran32!) + + U = unknowns(sys) + U .= 0.5 + + control = VoronoiFVM.NewtonControl() + control.verbose = false + control.reltol_linear = 1.0e-5 + control.max_lureuse = 10 + + tstep = 0.1 + time = 0.0 + step = 0 + T = 1.0 + while time < T + time = time + tstep + U = solve(sys; inival = U, control, tstep) + tstep *= 1.0 + step += 1 + end + + U_surf = view(U[2, :], bgrid2) + sum(U_surf) +end + +using Test +function runtests() + testval = 1509.8109057757858 + testval = 1508.582565216869 + @test isapprox(main(; assembly = :edgewise), testval; rtol = 1.0e-12) + @test isapprox(main(; assembly = :cellwise), testval; rtol = 1.0e-12) +end + +end diff --git a/v1.19.1/module_examples/Example311_HeatEquation_BoundaryDiffusion/index.html b/v1.19.1/module_examples/Example311_HeatEquation_BoundaryDiffusion/index.html new file mode 100644 index 000000000..fa4084f1d --- /dev/null +++ b/v1.19.1/module_examples/Example311_HeatEquation_BoundaryDiffusion/index.html @@ -0,0 +1,105 @@ + +311: Heat Equation with boundary diffusion · VoronoiFVM.jl

    311: Heat Equation with boundary diffusion

    (source code)

    module Example311_HeatEquation_BoundaryDiffusion
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +
    +"""
    +  We solve the following system
    +
    +      ∂_tu - εΔu = 0            in [0,T] × Ω>
    +           ε∇u⋅ν = k(u-v)       on [0,T] × Γ_1
    +           ε∇u⋅ν = 0            on [0,T] × (∂Ω ∖ Γ_1)
    +  ∂_tv -ε_ΓΔ_Γ v = f(x) +k(u-v) on [0,T] × Γ_1
    +          u(0)   = 0.5          in   {0} × Ω
    +          v(0)   = 0.5          on   {0} × Γ_1
    +"""
    +
    +function main(n = 1; assembly = :edgewise)
    +    breg = 5 # boundary region number for surface diffusion
    +
    +    hmin = 0.05 * 2.0^(-n + 1)
    +    hmax = 0.2 * 2.0^(-n + 1)
    +    XLeft = geomspace(0.0, 0.5, hmax, hmin)
    +    XRight = geomspace(0.5, 1.0, hmin, hmax)
    +    X = glue(XLeft, XRight)
    +
    +    Z = geomspace(0.0, 1.0, hmin, 2 * hmax)
    +
    +    grid = VoronoiFVM.Grid(X, X, Z)

    parameters

        eps = 1.0e0  # bulk heat conduction coefficient
    +    eps_surf = 1.0e-2 # surface diffusion coefficient
    +    k = 1.0    # transmission coefficient
    +    physics = VoronoiFVM.Physics(; flux = function (f, u, edge)
    +                                     f[1] = eps * (u[1, 1] - u[1, 2])
    +                                 end,
    +                                 bflux = function (f, u, edge)
    +                                     if edge.region == breg
    +                                         f[2] = eps_surf * (u[2, 1] - u[2, 2])
    +                                     else
    +                                         f[2] = 0.0
    +                                     end
    +                                 end,
    +                                 breaction = function (f, u, node)
    +                                     if node.region == breg
    +                                         f[1] = k * (u[1] - u[2])
    +                                         f[2] = k * (u[2] - u[1])
    +                                     else
    +                                         f[1] = 0.0
    +                                         f[2] = 0.0
    +                                     end
    +                                 end,
    +                                 bsource = function (f, bnode)
    +                                     x1 = bnode[1] - 0.5
    +                                     x2 = bnode[2] - 0.5
    +                                     x3 = bnode[3] - 0.5
    +                                     f[2] = 1.0e4 * exp(-20.0 * (x1^2 + x2^2 + x3^2))
    +                                 end, bstorage = function (f, u, node)
    +                                     if node.region == breg
    +                                         f[2] = u[2]
    +                                     end
    +                                 end, storage = function (f, u, node)
    +                                     f[1] = u[1]
    +                                 end)
    +
    +    sys = VoronoiFVM.System(grid, physics; unknown_storage = :sparse, assembly)
    +    enable_species!(sys, 1, [1])
    +    enable_boundary_species!(sys, 2, [breg])
    +
    +    function tran32!(a, b)
    +        a[1] = b[2]
    +    end
    +
    +    bgrid2 = subgrid(grid, [breg]; boundary = true, transform = tran32!)
    +
    +    U = unknowns(sys)
    +    U .= 0.5
    +
    +    control = VoronoiFVM.NewtonControl()
    +    control.verbose = false
    +    control.reltol_linear = 1.0e-5
    +    control.max_lureuse = 10
    +
    +    tstep = 0.1
    +    time = 0.0
    +    step = 0
    +    T = 1.0
    +    while time < T
    +        time = time + tstep
    +        U = solve(sys; inival = U, control, tstep)
    +        tstep *= 1.0
    +        step += 1
    +    end
    +
    +    U_surf = view(U[2, :], bgrid2)
    +    sum(U_surf)
    +end
    +
    +using Test
    +function runtests()
    +    testval = 1509.8109057757858
    +    testval = 1508.582565216869
    +    @test isapprox(main(; assembly = :edgewise), testval; rtol = 1.0e-12)
    +    @test isapprox(main(; assembly = :cellwise), testval; rtol = 1.0e-12)
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example405_GenericOperator.jl b/v1.19.1/module_examples/Example405_GenericOperator.jl new file mode 100644 index 000000000..21d33e092 --- /dev/null +++ b/v1.19.1/module_examples/Example405_GenericOperator.jl @@ -0,0 +1,81 @@ +#= + +# 405: Generic operator + ([source code](@__SOURCE_URL__)) + +Handle an operator which does not fit into the storage/flux/reaction API. +This uses automatic sparsity detection. +=# + +module Example405_GenericOperator +using Printf +using VoronoiFVM +using ExtendableGrids +using GridVisualize + +function main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse) + ## Same as Example102 with upwind + + ## Create a one-dimensional discretization + h = 1.0 / convert(Float64, n) + X = collect(0:h:1) + grid = VoronoiFVM.Grid(X) + + ## A parameter which is "passed" to the flux function via scope + D = 1.0e-2 + v = 1.0 + + ## This generic operator works on the full solution seen as linear vector, and indexing + ## into it needs to be performed with the help of idx (defined below for a solution vector) + ## Here, instead of the flux function we provide a "generic operator" + ## which provides the stiffness part of the problem. Its sparsity is detected automatically + ## using Symbolics.jl + function generic_operator!(f, u, sys) + f .= 0.0 + for i = 1:(length(X) - 1) + du = D * (u[idx[1, i]] - u[idx[1, i + 1]]) / (X[i + 1] - X[i]) + + v * (v > 0 ? u[idx[1, i]] : u[idx[1, i + 1]]) + f[idx[1, i]] += du + f[idx[1, i + 1]] -= du + end + end + + ## Create a physics structure + physics = VoronoiFVM.Physics(; generic = generic_operator!) + + ## Create a finite volume system - either + ## in the dense or the sparse version. + ## The difference is in the way the solution object + ## is stored - as dense or as sparse matrix + + sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage) + + ## Add species 1 to region 1 + enable_species!(sys, 1, [1]) + + ## Set boundary conditions + boundary_dirichlet!(sys, 1, 1, 0.0) + boundary_dirichlet!(sys, 1, 2, 1.0) + + ## Create a solution array + inival = unknowns(sys; inival = 0.5) + solution = unknowns(sys) + + idx = unknown_indices(solution) + + ## Stationary solution of the problem + solution = solve(sys; inival = 0.5, verbose) + + scalarplot(grid, solution[1, :]; title = "Nonlinear Poisson", Plotter = Plotter, + resolution = (300, 300)) + return sum(solution) +end + +using Test +function runtests() + testval = 1.099999999614456 + @test main(; unknown_storage = :sparse) ≈ testval && + main(; unknown_storage = :dense) ≈ testval +end + +end diff --git a/v1.19.1/module_examples/Example405_GenericOperator/index.html b/v1.19.1/module_examples/Example405_GenericOperator/index.html new file mode 100644 index 000000000..93cca3144 --- /dev/null +++ b/v1.19.1/module_examples/Example405_GenericOperator/index.html @@ -0,0 +1,73 @@ + +405: Generic operator · VoronoiFVM.jl

    405: Generic operator

    (source code)

    Handle an operator which does not fit into the storage/flux/reaction API. This uses automatic sparsity detection.

    module Example405_GenericOperator
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +
    +function main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse)
    +    # Same as Example102 with upwind
    +
    +    # Create a one-dimensional discretization
    +    h = 1.0 / convert(Float64, n)
    +    X = collect(0:h:1)
    +    grid = VoronoiFVM.Grid(X)
    +
    +    # A parameter which is "passed" to the flux function via scope
    +    D = 1.0e-2
    +    v = 1.0
    +
    +    # This generic operator works on the full solution seen as linear vector, and indexing
    +    # into it needs to be performed with the help of idx (defined below for a solution vector)
    +    # Here, instead of the flux function we provide a "generic operator"
    +    # which provides the stiffness part of the problem. Its sparsity is detected automatically
    +    # using Symbolics.jl
    +    function generic_operator!(f, u, sys)
    +        f .= 0.0
    +        for i = 1:(length(X) - 1)
    +            du = D * (u[idx[1, i]] - u[idx[1, i + 1]]) / (X[i + 1] - X[i]) +
    +                 v * (v > 0 ? u[idx[1, i]] : u[idx[1, i + 1]])
    +            f[idx[1, i]] += du
    +            f[idx[1, i + 1]] -= du
    +        end
    +    end
    +
    +    # Create a physics structure
    +    physics = VoronoiFVM.Physics(; generic = generic_operator!)
    +
    +    # Create a finite volume system - either
    +    # in the dense or  the sparse version.
    +    # The difference is in the way the solution object
    +    # is stored - as dense or as sparse matrix
    +
    +    sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage)
    +
    +    # Add species 1 to region 1
    +    enable_species!(sys, 1, [1])
    +
    +    # Set boundary conditions
    +    boundary_dirichlet!(sys, 1, 1, 0.0)
    +    boundary_dirichlet!(sys, 1, 2, 1.0)
    +
    +    # Create a solution array
    +    inival = unknowns(sys; inival = 0.5)
    +    solution = unknowns(sys)
    +
    +    idx = unknown_indices(solution)
    +
    +    # Stationary solution of the problem
    +    solution = solve(sys; inival = 0.5, verbose)
    +
    +    scalarplot(grid, solution[1, :]; title = "Nonlinear Poisson", Plotter = Plotter,
    +               resolution = (300, 300))
    +    return sum(solution)
    +end
    +
    +using Test
    +function runtests()
    +    testval = 1.099999999614456
    +    @test main(; unknown_storage = :sparse) ≈ testval &&
    +          main(; unknown_storage = :dense) ≈ testval
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example406_WeirdReaction.jl b/v1.19.1/module_examples/Example406_WeirdReaction.jl new file mode 100644 index 000000000..e1ed3a90f --- /dev/null +++ b/v1.19.1/module_examples/Example406_WeirdReaction.jl @@ -0,0 +1,197 @@ +#= + +# 406: 1D Weird Surface Reaction + ([source code](@__SOURCE_URL__)) + +Species $A$ and $B$ exist in the interior of the domain, species $C$ +lives a the boundary $\Gamma_1$. We assume a heterogeneous reaction scheme +where $A$ reacts to $B$ with a rate depending on $\nabla A$ near the surface + +```math +\begin{aligned} + A &\leftrightarrow B\\ +\end{aligned} +``` + +In $\Omega$, both $A$ and $B$ are transported through diffusion: + +```math +\begin{aligned} +\partial_t u_B - \nabla\cdot D_A \nabla u_A & = f_A\\ +\partial_t u_B - \nabla\cdot D_B \nabla u_B & = 0\\ +\end{aligned} +``` +Here, $f(x)$ is a source term creating $A$. +On $\Gamma_2$, we set boundary conditions +```math +\begin{aligned} +D_A \nabla u_A & = 0\\ +u_B&=0 +\end{aligned} +``` +describing no normal flux for $A$ and zero concentration of $B$. +On $\Gamma_1$, we use the mass action law to describe the boundary reaction and +the evolution of the boundary concentration $C$. We assume that there is a limited +amount of surface sites $S$ for species C, so in fact A has to react with a free +surface site in order to become $C$ which reflected by the factor $1-u_C$. The same +is true for $B$. +```math +\begin{aligned} +R_{AB}(u_A, u_B)&=k_{AB}^+exp(u_A'(0))u_A - k_{AB}^-exp(-u_A'(0))u_B\\ +- D_A \nabla u_A + R_{AB}(u_A, u_B)& =0 \\ +- D_B \nabla u_B - R_{AB}(u_A, u_B)& =0 \\ +\end{aligned} +``` + +=# +module Example406_WeirdReaction +using Printf +using VoronoiFVM +using SparseArrays +using ExtendableGrids +using GridVisualize + +function main(; n = 10, + Plotter = nothing, + verbose = false, + tend = 1, + unknown_storage = :sparse, + autodetect_sparsity = true) + h = 1.0 / convert(Float64, n) + X = collect(0.0:h:1.0) + N = length(X) + + grid = VoronoiFVM.Grid(X) + ## By default, \Gamma_1 at X[1] and \Gamma_2 is at X[end] + + ## Species numbers + iA = 1 + iB = 2 + iC = 3 + + ## Diffusion flux for species A and B + D_A = 1.0 + D_B = 1.0e-2 + function flux!(f, u, edge) + f[iA] = D_A * (u[iA, 1] - u[iA, 2]) + f[iB] = D_B * (u[iB, 1] - u[iB, 2]) + end + + ## Storage term of species A and B + function storage!(f, u, node) + f[iA] = u[iA] + f[iB] = u[iB] + end + + ## Source term for species a around 0.5 + function source!(f, node) + x1 = node[1] - 0.5 + f[iA] = exp(-100 * x1^2) + end + + ## Reaction constants (p = + , m = -) + ## Chosen to prefer path A-> B + kp_AB = 1.0 + km_AB = 0.1 + + function breaction!(f, u, node) + if node.region == 1 + R = kp_AB * exp(u[iC]) * u[iA] - exp(-u[iC]) * km_AB * u[iB] + f[iA] += R + f[iB] -= R + end + end + + ## This generic operator works on the full solution seen as linear vector, and indexing + ## into it needs to be performed with the help of idx (defined below for a solution vector) + ## Its sparsity is detected automatically using SparsityDetection.jl + ## Here, we calculate the gradient of u_A at the boundary and store the value in u_C which + ## is then used as a parameter in the boundary reaction + function generic_operator!(f, u, sys) + f .= 0 + f[idx[iC, 1]] = u[idx[iC, 1]] + + 0.1 * (u[idx[iA, 1]] - u[idx[iA, 2]]) / (X[2] - X[1]) + end + + # If we know the sparsity pattern, we can here create a + # sparse matrix with values set to 1 in the nonzero + # slots. This allows to circumvent the + # autodetection which may takes some time. + function generic_operator_sparsity(sys) + idx = unknown_indices(unknowns(sys)) + sparsity = spzeros(num_dof(sys), num_dof(sys)) + sparsity[idx[iC, 1], idx[iC, 1]] = 1 + sparsity[idx[iC, 1], idx[iA, 1]] = 1 + sparsity[idx[iC, 1], idx[iA, 2]] = 1 + sparsity + end + + if autodetect_sparsity + physics = VoronoiFVM.Physics(; breaction = breaction!, + generic = generic_operator!, + flux = flux!, + storage = storage!, + source = source!) + else + physics = VoronoiFVM.Physics(; breaction = breaction!, + generic = generic_operator!, + generic_sparsity = generic_operator_sparsity, + flux = flux!, + storage = storage!, + source = source!) + end + + sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage) + + ## Enable species in bulk resp + enable_species!(sys, iA, [1]) + enable_species!(sys, iB, [1]) + + ## Enable surface species + enable_boundary_species!(sys, iC, [1]) + + ## Set Dirichlet bc for species B on \Gamma_2 + boundary_dirichlet!(sys, iB, 2, 0.0) + + ## Initial values + U = unknowns(sys) + U .= 0.0 + idx = unknown_indices(U) + + tstep = 0.01 + time = 0.0 + T = Float64[] + u_C = Float64[] + + control = VoronoiFVM.NewtonControl() + control.verbose = verbose + p = GridVisualizer(; Plotter = Plotter, layout = (2, 1)) + while time < tend + time = time + tstep + U = solve(sys; inival = U, time, tstep, control) + if verbose + @printf("time=%g\n", time) + end + ## Record boundary pecies + push!(T, time) + push!(u_C, U[iC, 1]) + + scalarplot!(p[1, 1], grid, U[iA, :]; label = "[A]", + title = @sprintf("max_A=%.5f max_B=%.5f u_C=%.5f", maximum(U[iA, :]), + maximum(U[iB, :]), u_C[end]), color = :red) + scalarplot!(p[1, 1], grid, U[iB, :]; label = "[B]", clear = false, color = :blue) + scalarplot!(p[2, 1], copy(T), copy(u_C); label = "[C]", clear = true, show = true) + end + return U[iC, 1] +end + +using Test +function runtests() + testval = 0.007027597470502758 + @test main(; unknown_storage = :sparse) ≈ testval && + main(; unknown_storage = :dense) ≈ testval && + main(; unknown_storage = :sparse, autodetect_sparsity = false) ≈ testval && + main(; unknown_storage = :dense, autodetect_sparsity = false) ≈ testval +end + +end diff --git a/v1.19.1/module_examples/Example406_WeirdReaction/index.html b/v1.19.1/module_examples/Example406_WeirdReaction/index.html new file mode 100644 index 000000000..c58c7d225 --- /dev/null +++ b/v1.19.1/module_examples/Example406_WeirdReaction/index.html @@ -0,0 +1,158 @@ + +406: 1D Weird Surface Reaction · VoronoiFVM.jl

    406: 1D Weird Surface Reaction

    (source code)

    Species $A$ and $B$ exist in the interior of the domain, species $C$ lives a the boundary $\Gamma_1$. We assume a heterogeneous reaction scheme where $A$ reacts to $B$ with a rate depending on $\nabla A$ near the surface

    \[\begin{aligned} + A &\leftrightarrow B\\ +\end{aligned}\]

    In $\Omega$, both $A$ and $B$ are transported through diffusion:

    \[\begin{aligned} +\partial_t u_B - \nabla\cdot D_A \nabla u_A & = f_A\\ +\partial_t u_B - \nabla\cdot D_B \nabla u_B & = 0\\ +\end{aligned}\]

    Here, $f(x)$ is a source term creating $A$. On $\Gamma_2$, we set boundary conditions

    \[\begin{aligned} +D_A \nabla u_A & = 0\\ +u_B&=0 +\end{aligned}\]

    describing no normal flux for $A$ and zero concentration of $B$. On $\Gamma_1$, we use the mass action law to describe the boundary reaction and the evolution of the boundary concentration $C$. We assume that there is a limited amount of surface sites $S$ for species C, so in fact A has to react with a free surface site in order to become $C$ which reflected by the factor $1-u_C$. The same is true for $B$.

    \[\begin{aligned} +R_{AB}(u_A, u_B)&=k_{AB}^+exp(u_A'(0))u_A - k_{AB}^-exp(-u_A'(0))u_B\\ +- D_A \nabla u_A + R_{AB}(u_A, u_B)& =0 \\ +- D_B \nabla u_B - R_{AB}(u_A, u_B)& =0 \\ +\end{aligned}\]

    module Example406_WeirdReaction
    +using Printf
    +using VoronoiFVM
    +using SparseArrays
    +using ExtendableGrids
    +using GridVisualize
    +
    +function main(; n = 10,
    +              Plotter = nothing,
    +              verbose = false,
    +              tend = 1,
    +              unknown_storage = :sparse,
    +              autodetect_sparsity = true)
    +    h = 1.0 / convert(Float64, n)
    +    X = collect(0.0:h:1.0)
    +    N = length(X)
    +
    +    grid = VoronoiFVM.Grid(X)
    +    # By default, \Gamma_1 at X[1] and \Gamma_2 is at X[end]
    +
    +    # Species numbers
    +    iA = 1
    +    iB = 2
    +    iC = 3
    +
    +    # Diffusion flux for species A and B
    +    D_A = 1.0
    +    D_B = 1.0e-2
    +    function flux!(f, u, edge)
    +        f[iA] = D_A * (u[iA, 1] - u[iA, 2])
    +        f[iB] = D_B * (u[iB, 1] - u[iB, 2])
    +    end
    +
    +    # Storage term of species A and B
    +    function storage!(f, u, node)
    +        f[iA] = u[iA]
    +        f[iB] = u[iB]
    +    end
    +
    +    # Source term for species a around 0.5
    +    function source!(f, node)
    +        x1 = node[1] - 0.5
    +        f[iA] = exp(-100 * x1^2)
    +    end
    +
    +    # Reaction constants (p = + , m = -)
    +    # Chosen to prefer path A-> B
    +    kp_AB = 1.0
    +    km_AB = 0.1
    +
    +    function breaction!(f, u, node)
    +        if node.region == 1
    +            R = kp_AB * exp(u[iC]) * u[iA] - exp(-u[iC]) * km_AB * u[iB]
    +            f[iA] += R
    +            f[iB] -= R
    +        end
    +    end
    +
    +    # This generic operator works on the full solution seen as linear vector, and indexing
    +    # into it needs to be performed with the help of idx (defined below for a solution vector)
    +    # Its sparsity is detected automatically using SparsityDetection.jl
    +    # Here, we calculate the gradient of u_A at the boundary and store the value in u_C which
    +    # is then used as a parameter in the boundary reaction
    +    function generic_operator!(f, u, sys)
    +        f .= 0
    +        f[idx[iC, 1]] = u[idx[iC, 1]] +
    +                        0.1 * (u[idx[iA, 1]] - u[idx[iA, 2]]) / (X[2] - X[1])
    +    end

    If we know the sparsity pattern, we can here create a sparse matrix with values set to 1 in the nonzero slots. This allows to circumvent the autodetection which may takes some time.

        function generic_operator_sparsity(sys)
    +        idx = unknown_indices(unknowns(sys))
    +        sparsity = spzeros(num_dof(sys), num_dof(sys))
    +        sparsity[idx[iC, 1], idx[iC, 1]] = 1
    +        sparsity[idx[iC, 1], idx[iA, 1]] = 1
    +        sparsity[idx[iC, 1], idx[iA, 2]] = 1
    +        sparsity
    +    end
    +
    +    if autodetect_sparsity
    +        physics = VoronoiFVM.Physics(; breaction = breaction!,
    +                                     generic = generic_operator!,
    +                                     flux = flux!,
    +                                     storage = storage!,
    +                                     source = source!)
    +    else
    +        physics = VoronoiFVM.Physics(; breaction = breaction!,
    +                                     generic = generic_operator!,
    +                                     generic_sparsity = generic_operator_sparsity,
    +                                     flux = flux!,
    +                                     storage = storage!,
    +                                     source = source!)
    +    end
    +
    +    sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage)
    +
    +    # Enable species in bulk resp
    +    enable_species!(sys, iA, [1])
    +    enable_species!(sys, iB, [1])
    +
    +    # Enable surface species
    +    enable_boundary_species!(sys, iC, [1])
    +
    +    # Set Dirichlet bc for species B on \Gamma_2
    +    boundary_dirichlet!(sys, iB, 2, 0.0)
    +
    +    # Initial values
    +    U = unknowns(sys)
    +    U .= 0.0
    +    idx = unknown_indices(U)
    +
    +    tstep = 0.01
    +    time = 0.0
    +    T = Float64[]
    +    u_C = Float64[]
    +
    +    control = VoronoiFVM.NewtonControl()
    +    control.verbose = verbose
    +    p = GridVisualizer(; Plotter = Plotter, layout = (2, 1))
    +    while time < tend
    +        time = time + tstep
    +        U = solve(sys; inival = U, time, tstep, control)
    +        if verbose
    +            @printf("time=%g\n", time)
    +        end
    +        # Record  boundary pecies
    +        push!(T, time)
    +        push!(u_C, U[iC, 1])
    +
    +        scalarplot!(p[1, 1], grid, U[iA, :]; label = "[A]",
    +                    title = @sprintf("max_A=%.5f max_B=%.5f u_C=%.5f", maximum(U[iA, :]),
    +                                     maximum(U[iB, :]), u_C[end]), color = :red)
    +        scalarplot!(p[1, 1], grid, U[iB, :]; label = "[B]", clear = false, color = :blue)
    +        scalarplot!(p[2, 1], copy(T), copy(u_C); label = "[C]", clear = true, show = true)
    +    end
    +    return U[iC, 1]
    +end
    +
    +using Test
    +function runtests()
    +    testval = 0.007027597470502758
    +    @test main(; unknown_storage = :sparse) ≈ testval &&
    +          main(; unknown_storage = :dense) ≈ testval &&
    +          main(; unknown_storage = :sparse, autodetect_sparsity = false) ≈ testval &&
    +          main(; unknown_storage = :dense, autodetect_sparsity = false) ≈ testval
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example410_ManySpecies.jl b/v1.19.1/module_examples/Example410_ManySpecies.jl new file mode 100644 index 000000000..5a631d27f --- /dev/null +++ b/v1.19.1/module_examples/Example410_ManySpecies.jl @@ -0,0 +1,45 @@ +#= + +# 410: Many Species + ([source code](@__SOURCE_URL__)) + +Test stationary diffusion for 50 species. + +=# +module Example410_ManySpecies +using Printf +using VoronoiFVM +using SparseArrays +using ExtendableGrids +using GridVisualize +using LinearAlgebra + +function main(; n = 11, nspec = 50, Plotter = nothing, unknown_storage = :dense, assembly = :edgewise) + grid = simplexgrid(range(0, 1; length = n)) + + function flux(f, u, edge) + for ispec = 1:nspec + f[ispec] = u[ispec, 1] - u[ispec, 2] + end + end + physics = VoronoiFVM.Physics(; flux = flux) + sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly) + for ispec = 1:nspec + enable_species!(sys, ispec, [1]) + boundary_dirichlet!(sys, ispec, 1, 0) + boundary_dirichlet!(sys, ispec, 2, 1) + end + sol = solve(sys) + norm(sol) +end + +using Test +function runtests() + testval = 13.874436925511608 + @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval && + main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval +end + +end diff --git a/v1.19.1/module_examples/Example410_ManySpecies/index.html b/v1.19.1/module_examples/Example410_ManySpecies/index.html new file mode 100644 index 000000000..e34c52ace --- /dev/null +++ b/v1.19.1/module_examples/Example410_ManySpecies/index.html @@ -0,0 +1,38 @@ + +410: Many Species · VoronoiFVM.jl

    410: Many Species

    (source code)

    Test stationary diffusion for 50 species.

    module Example410_ManySpecies
    +using Printf
    +using VoronoiFVM
    +using SparseArrays
    +using ExtendableGrids
    +using GridVisualize
    +using LinearAlgebra
    +
    +function main(; n = 11, nspec = 50, Plotter = nothing, unknown_storage = :dense, assembly = :edgewise)
    +    grid = simplexgrid(range(0, 1; length = n))
    +
    +    function flux(f, u, edge)
    +        for ispec = 1:nspec
    +            f[ispec] = u[ispec, 1] - u[ispec, 2]
    +        end
    +    end
    +    physics = VoronoiFVM.Physics(; flux = flux)
    +    sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly)
    +    for ispec = 1:nspec
    +        enable_species!(sys, ispec, [1])
    +        boundary_dirichlet!(sys, ispec, 1, 0)
    +        boundary_dirichlet!(sys, ispec, 2, 1)
    +    end
    +    sol = solve(sys)
    +    norm(sol)
    +end
    +
    +using Test
    +function runtests()
    +    testval = 13.874436925511608
    +    @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example420_DiscontinuousQuantities.jl b/v1.19.1/module_examples/Example420_DiscontinuousQuantities.jl new file mode 100644 index 000000000..113c42b33 --- /dev/null +++ b/v1.19.1/module_examples/Example420_DiscontinuousQuantities.jl @@ -0,0 +1,135 @@ +#= + +# 420: Discontinuous Quantities + ([source code](@__SOURCE_URL__)) + +Test jumping species and quantity handling + +=# +module Example420_DiscontinuousQuantities +using Printf +using VoronoiFVM +using SparseArrays +using ExtendableGrids +using GridVisualize +using LinearAlgebra + +function main(; N = 5, Plotter = nothing, unknown_storage = :sparse, assembly = :edgewise) + XX = collect(0:0.1:1) + xcoord = XX + for i = 1:(N - 1) + xcoord = glue(xcoord, XX .+ i) + end + grid2 = simplexgrid(xcoord) + for i = 1:N + cellmask!(grid2, [i - 1], [i], i) + end + for i = 1:(N - 1) + bfacemask!(grid2, [i], [i], i + 2) + end + + params = zeros(2, num_cellregions(grid2)) + for i = 1:num_cellregions(grid2) + params[1, i] = i + params[2, i] = 10 * i + end + + system = VoronoiFVM.System(grid2; unknown_storage = unknown_storage, assembly = assembly) + + ## First, we introduce a continuous quantity which we name "cspec". Note that the "species number" can be assigned automatically if not given explicitly. + cspec = ContinuousQuantity(system, 1:N; ispec = 1, id = 1) + + ## A discontinuous quantity can be introduced as well. by default, each reagion gets a new species number. This can be overwritten by the user. + dspec = DiscontinuousQuantity(system, 1:N; regionspec = [2 + i % 2 for i = 1:N], id = 2) + + # check 1D array access with quantities + carrierList = [cspec dspec] + numberCarriers = length(carrierList) + + params2 = zeros(1, numberCarriers) + + for icc ∈ carrierList + params2[icc] = 2 + end + + for i = 1:numberCarriers + @assert params2[i] == 2 + end + + # check 2D array access with quantities + for i = 1:num_cellregions(grid2) + @assert params[cspec, i] == i + @assert params[dspec, i] == 10 * i + end + + for i = 1:num_cellregions(grid2) + params[cspec, i] = -i + params[dspec, i] = -10 * i + end + + for i = 1:num_cellregions(grid2) + @assert params[1, i] == -i + @assert params[2, i] == -10 * i + end + + ##For both quantities, we define simple diffusion fluxes: + + function flux(f, u, edge) + f[dspec] = u[dspec, 1] - u[dspec, 2] + f[cspec] = u[cspec, 1] - u[cspec, 2] + end + + d1 = 1 + q1 = 0.2 + + function breaction(f, u, bnode) + + # left outer boundary value for dspec + if bnode.region == 1 + f[dspec] = u[dspec] + 0.5 + end + + ## Define a thin layer interface condition for `dspec` and an interface source for `cspec`. + if bnode.region > 2 + react = (u[dspec, 1] - u[dspec, 2]) / d1 + f[dspec, 1] = react + f[dspec, 2] = -react + f[cspec] = -q1 * u[cspec] + end + end + + physics!(system, VoronoiFVM.Physics(; flux = flux, + breaction = breaction)) + + ## Set boundary conditions + boundary_dirichlet!(system, dspec, 2, 0.1) + boundary_dirichlet!(system, cspec, 1, 0.1) + boundary_dirichlet!(system, cspec, 2, 1.0) + subgrids = VoronoiFVM.subgrids(dspec, system) + + U = solve(system) + + dvws = views(U, dspec, subgrids, system) + cvws = views(U, cspec, subgrids, system) + vis = GridVisualizer(; resolution = (600, 300), Plotter = Plotter) + for i in eachindex(dvws) + scalarplot!(vis, subgrids[i], dvws[i]; flimits = (-0.5, 1.5), clear = false, + color = :red) + scalarplot!(vis, subgrids[i], cvws[i]; flimits = (-0.5, 1.5), clear = false, + color = :green) + end + reveal(vis) + I = integrate(system, system.physics.storage, U) + sum(I[dspec, :]) + sum(I[cspec, :]) +end + +using Test +function runtests() + testval = 4.2 + @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval && + main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval +end + +end diff --git a/v1.19.1/module_examples/Example420_DiscontinuousQuantities/index.html b/v1.19.1/module_examples/Example420_DiscontinuousQuantities/index.html new file mode 100644 index 000000000..b3dd908b1 --- /dev/null +++ b/v1.19.1/module_examples/Example420_DiscontinuousQuantities/index.html @@ -0,0 +1,119 @@ + +420: Discontinuous Quantities · VoronoiFVM.jl

    420: Discontinuous Quantities

    (source code)

    Test jumping species and quantity handling

    module Example420_DiscontinuousQuantities
    +using Printf
    +using VoronoiFVM
    +using SparseArrays
    +using ExtendableGrids
    +using GridVisualize
    +using LinearAlgebra
    +
    +function main(; N = 5, Plotter = nothing, unknown_storage = :sparse, assembly = :edgewise)
    +    XX = collect(0:0.1:1)
    +    xcoord = XX
    +    for i = 1:(N - 1)
    +        xcoord = glue(xcoord, XX .+ i)
    +    end
    +    grid2 = simplexgrid(xcoord)
    +    for i = 1:N
    +        cellmask!(grid2, [i - 1], [i], i)
    +    end
    +    for i = 1:(N - 1)
    +        bfacemask!(grid2, [i], [i], i + 2)
    +    end
    +
    +    params = zeros(2, num_cellregions(grid2))
    +    for i = 1:num_cellregions(grid2)
    +        params[1, i] = i
    +        params[2, i] = 10 * i
    +    end
    +
    +    system = VoronoiFVM.System(grid2; unknown_storage = unknown_storage, assembly = assembly)
    +
    +    # First, we introduce a continuous quantity which we name "cspec". Note that the "species number" can be assigned automatically if not given explicitly.
    +    cspec = ContinuousQuantity(system, 1:N; ispec = 1, id = 1)
    +
    +    # A discontinuous quantity can be introduced as well. by default, each reagion gets a new species number. This can be overwritten by the user.
    +    dspec = DiscontinuousQuantity(system, 1:N; regionspec = [2 + i % 2 for i = 1:N], id = 2)

    check 1D array access with quantities

        carrierList = [cspec dspec]
    +    numberCarriers = length(carrierList)
    +
    +    params2 = zeros(1, numberCarriers)
    +
    +    for icc ∈ carrierList
    +        params2[icc] = 2
    +    end
    +
    +    for i = 1:numberCarriers
    +        @assert params2[i] == 2
    +    end

    check 2D array access with quantities

        for i = 1:num_cellregions(grid2)
    +        @assert params[cspec, i] == i
    +        @assert params[dspec, i] == 10 * i
    +    end
    +
    +    for i = 1:num_cellregions(grid2)
    +        params[cspec, i] = -i
    +        params[dspec, i] = -10 * i
    +    end
    +
    +    for i = 1:num_cellregions(grid2)
    +        @assert params[1, i] == -i
    +        @assert params[2, i] == -10 * i
    +    end
    +
    +    ##For both quantities, we define simple diffusion fluxes:
    +
    +    function flux(f, u, edge)
    +        f[dspec] = u[dspec, 1] - u[dspec, 2]
    +        f[cspec] = u[cspec, 1] - u[cspec, 2]
    +    end
    +
    +    d1 = 1
    +    q1 = 0.2
    +
    +    function breaction(f, u, bnode)

    left outer boundary value for dspec

            if bnode.region == 1
    +            f[dspec] = u[dspec] + 0.5
    +        end
    +
    +        # Define a thin layer interface condition for `dspec` and an interface source for `cspec`.
    +        if bnode.region > 2
    +            react = (u[dspec, 1] - u[dspec, 2]) / d1
    +            f[dspec, 1] = react
    +            f[dspec, 2] = -react
    +            f[cspec] = -q1 * u[cspec]
    +        end
    +    end
    +
    +    physics!(system, VoronoiFVM.Physics(; flux = flux,
    +                                        breaction = breaction))
    +
    +    # Set boundary conditions
    +    boundary_dirichlet!(system, dspec, 2, 0.1)
    +    boundary_dirichlet!(system, cspec, 1, 0.1)
    +    boundary_dirichlet!(system, cspec, 2, 1.0)
    +    subgrids = VoronoiFVM.subgrids(dspec, system)
    +
    +    U = solve(system)
    +
    +    dvws = views(U, dspec, subgrids, system)
    +    cvws = views(U, cspec, subgrids, system)
    +    vis = GridVisualizer(; resolution = (600, 300), Plotter = Plotter)
    +    for i in eachindex(dvws)
    +        scalarplot!(vis, subgrids[i], dvws[i]; flimits = (-0.5, 1.5), clear = false,
    +                    color = :red)
    +        scalarplot!(vis, subgrids[i], cvws[i]; flimits = (-0.5, 1.5), clear = false,
    +                    color = :green)
    +    end
    +    reveal(vis)
    +    I = integrate(system, system.physics.storage, U)
    +    sum(I[dspec, :]) + sum(I[cspec, :])
    +end
    +
    +using Test
    +function runtests()
    +    testval = 4.2
    +    @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example421_AbstractQuantities_TestFunctions.jl b/v1.19.1/module_examples/Example421_AbstractQuantities_TestFunctions.jl new file mode 100644 index 000000000..61dea7003 --- /dev/null +++ b/v1.19.1/module_examples/Example421_AbstractQuantities_TestFunctions.jl @@ -0,0 +1,173 @@ +#= + +# 421: Current Calculation for AbstractQuantities + ([source code](@__SOURCE_URL__)) + +Test current calculation for jumping species. Here, we have three cases: + a. Problem initialized as usual + b. Problem initialized with Continuousquantity + c. Problem initialized with Discontinuousquantity with adjusted reaction rate +We see that the resulting current coincides for all three cases when adjusting the +reaction rate. + +=# +module Example421_AbstractQuantities_TestFunctions + +using Printf +using VoronoiFVM +using ExtendableGrids +using GridVisualize +using LinearAlgebra + +mutable struct Data + rate::Float64 # rate which is within DiscontinuousQuantities + Data() = new() +end + +function main(; N = 3, Plotter = nothing, unknown_storage = :sparse, assembly = :edgewise) + XX = collect(0:0.1:1) + xcoord = XX + for i = 1:(N - 1) + xcoord = glue(xcoord, XX .+ i) + end + grid = simplexgrid(xcoord) + for i = 1:N + cellmask!(grid, [i - 1], [i], i) + end + for i = 1:(N - 1) + bfacemask!(grid, [i], [i], i + 2) + end + + sysQ = VoronoiFVM.System(grid; unknown_storage = unknown_storage) + cspec = ContinuousQuantity(sysQ, 1:N; id = 1) # continuous quantity + dspec = DiscontinuousQuantity(sysQ, 1:N; id = 2) # discontinuous quantity + + data = Data() + rate = 0.0 + data.rate = rate + + function fluxQ(f, u, edge, data) # For both quantities, we define simple diffusion fluxes + f[dspec] = u[dspec, 1] - u[dspec, 2] + f[cspec] = u[cspec, 1] - u[cspec, 2] + end + + function breactionQ(f, u, bnode, data) + ## Define a thin layer interface condition for `dspec`. + if bnode.region > 2 + react = (u[dspec, 1] - u[dspec, 2]) / data.rate + f[dspec, 1] = react + f[dspec, 2] = -react + end + end + + physics!(sysQ, VoronoiFVM.Physics(; data = data, + flux = fluxQ, + breaction = breactionQ)) + + ########################################################## + icc = 1 # for system without AbstractQuantities + + function flux!(f, u, edge) # analogous as for other system + f[icc] = u[icc, 1] - u[icc, 2] + end + + # other system to which we compare current calculation + sys = VoronoiFVM.System(grid; flux = flux!, species = icc, + unknown_storage = unknown_storage) + + ## Set left boundary conditions + boundary_dirichlet!(sysQ, dspec, 1, 0.0) + boundary_dirichlet!(sysQ, cspec, 1, 0.0) + boundary_dirichlet!(sys, icc, 1, 0.0) + + subgrids = VoronoiFVM.subgrids(dspec, sysQ) + + # solve + UQ = unknowns(sysQ) + U = unknowns(sys) + UQ .= 0.0 + U .= 0.0 + biasval = range(0; stop = 2.0, length = 5) + + Icspec = zeros(length(biasval)) + Idspec = zeros(length(biasval)) + Iicc = zeros(length(biasval)) + + for data.rate in [1.0e2, 1.0e0, 1.0e-2, 1.0e-4, 1.0e-6] + count = 1 + for Δu in biasval + # first problem + boundary_dirichlet!(sysQ, dspec, 2, Δu) + boundary_dirichlet!(sysQ, cspec, 2, Δu) + + UQ = solve(sysQ; inival = UQ) + + ## get current + factoryQ = TestFunctionFactory(sysQ) + tfQ = testfunction(factoryQ, [1], [2]) + IQ = integrate(sysQ, tfQ, UQ) + + val = 0.0 + for ii in dspec.regionspec # current is calculated regionwise + val = val + IQ[ii] + end + Icspec[count] = IQ[cspec] + Idspec[count] = val + + # second problem + boundary_dirichlet!(sys, icc, 2, Δu) + + U = solve(sys; inival = U) + + factory = TestFunctionFactory(sys) + tf = testfunction(factory, [1], [2]) + I = integrate(sys, tf, U) + + Iicc[count] = I[icc] + + count = count + 1 + end # bias loop + + # plot + dvws = views(UQ, dspec, subgrids, sysQ) + cvws = views(UQ, cspec, subgrids, sysQ) + + vis = GridVisualizer(; layout = (2, 1), resolution = (600, 300), Plotter = Plotter) + + for i in eachindex(dvws) + scalarplot!(vis[1, 1], subgrids[i], dvws[i]; flimits = (-0.5, 1.5), + title = @sprintf("Solution with rate=%.2f", data.rate), + label = "discont quantity", clear = false, color = :red) + scalarplot!(vis[1, 1], subgrids[i], cvws[i]; label = "cont quantity", + clear = false, color = :green) + end + scalarplot!(vis[1, 1], grid, U[icc, :]; label = "without quantity", clear = false, + linestyle = :dot, color = :blue) + + scalarplot!(vis[2, 1], biasval, Idspec; clear = false, + title = @sprintf("IV with rate=%.2f", data.rate), + label = "discont quantity", color = :red) + scalarplot!(vis[2, 1], biasval, Icspec; clear = false, title = "Current", + label = "cont quantity", color = :green) + scalarplot!(vis[2, 1], biasval, Iicc; clear = false, label = "discont quantity", + linestyle = :dot, color = :blue, show = true) + + reveal(vis) + sleep(0.2) + end # rate loop + + errorIV = norm(Idspec - Icspec, 2) + + return errorIV +end + +using Test +function runtests() + testval = 6.085802139465579e-7 + @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval && + main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval +end + +end diff --git a/v1.19.1/module_examples/Example421_AbstractQuantities_TestFunctions/index.html b/v1.19.1/module_examples/Example421_AbstractQuantities_TestFunctions/index.html new file mode 100644 index 000000000..b9b8d0d50 --- /dev/null +++ b/v1.19.1/module_examples/Example421_AbstractQuantities_TestFunctions/index.html @@ -0,0 +1,147 @@ + +421: Current Calculation for AbstractQuantities · VoronoiFVM.jl

    421: Current Calculation for AbstractQuantities

    (source code)

    Test current calculation for jumping species. Here, we have three cases: a. Problem initialized as usual b. Problem initialized with Continuousquantity c. Problem initialized with Discontinuousquantity with adjusted reaction rate We see that the resulting current coincides for all three cases when adjusting the reaction rate.

    module Example421_AbstractQuantities_TestFunctions
    +
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +using LinearAlgebra
    +
    +mutable struct Data
    +    rate::Float64 # rate which is within DiscontinuousQuantities
    +    Data() = new()
    +end
    +
    +function main(; N = 3, Plotter = nothing, unknown_storage = :sparse, assembly = :edgewise)
    +    XX = collect(0:0.1:1)
    +    xcoord = XX
    +    for i = 1:(N - 1)
    +        xcoord = glue(xcoord, XX .+ i)
    +    end
    +    grid = simplexgrid(xcoord)
    +    for i = 1:N
    +        cellmask!(grid, [i - 1], [i], i)
    +    end
    +    for i = 1:(N - 1)
    +        bfacemask!(grid, [i], [i], i + 2)
    +    end
    +
    +    sysQ = VoronoiFVM.System(grid; unknown_storage = unknown_storage)
    +    cspec = ContinuousQuantity(sysQ, 1:N; id = 1)                    # continuous quantity
    +    dspec = DiscontinuousQuantity(sysQ, 1:N; id = 2)                 # discontinuous quantity
    +
    +    data = Data()
    +    rate = 0.0
    +    data.rate = rate
    +
    +    function fluxQ(f, u, edge, data) # For both quantities, we define simple diffusion fluxes
    +        f[dspec] = u[dspec, 1] - u[dspec, 2]
    +        f[cspec] = u[cspec, 1] - u[cspec, 2]
    +    end
    +
    +    function breactionQ(f, u, bnode, data)
    +        # Define a thin layer interface condition for `dspec`.
    +        if bnode.region > 2
    +            react = (u[dspec, 1] - u[dspec, 2]) / data.rate
    +            f[dspec, 1] = react
    +            f[dspec, 2] = -react
    +        end
    +    end
    +
    +    physics!(sysQ, VoronoiFVM.Physics(; data = data,
    +                                      flux = fluxQ,
    +                                      breaction = breactionQ))
    +
    +    ##########################################################
    +    icc = 1 # for system without AbstractQuantities
    +
    +    function flux!(f, u, edge) # analogous as for other system
    +        f[icc] = u[icc, 1] - u[icc, 2]
    +    end

    other system to which we compare current calculation

        sys = VoronoiFVM.System(grid; flux = flux!, species = icc,
    +                            unknown_storage = unknown_storage)
    +
    +    # Set left boundary conditions
    +    boundary_dirichlet!(sysQ, dspec, 1, 0.0)
    +    boundary_dirichlet!(sysQ, cspec, 1, 0.0)
    +    boundary_dirichlet!(sys, icc, 1, 0.0)
    +
    +    subgrids = VoronoiFVM.subgrids(dspec, sysQ)

    solve

        UQ = unknowns(sysQ)
    +    U = unknowns(sys)
    +    UQ .= 0.0
    +    U .= 0.0
    +    biasval = range(0; stop = 2.0, length = 5)
    +
    +    Icspec = zeros(length(biasval))
    +    Idspec = zeros(length(biasval))
    +    Iicc = zeros(length(biasval))
    +
    +    for data.rate in [1.0e2, 1.0e0, 1.0e-2, 1.0e-4, 1.0e-6]
    +        count = 1
    +        for Δu in biasval

    first problem

                boundary_dirichlet!(sysQ, dspec, 2, Δu)
    +            boundary_dirichlet!(sysQ, cspec, 2, Δu)
    +
    +            UQ = solve(sysQ; inival = UQ)
    +
    +            # get current
    +            factoryQ = TestFunctionFactory(sysQ)
    +            tfQ = testfunction(factoryQ, [1], [2])
    +            IQ = integrate(sysQ, tfQ, UQ)
    +
    +            val = 0.0
    +            for ii in dspec.regionspec # current is calculated regionwise
    +                val = val + IQ[ii]
    +            end
    +            Icspec[count] = IQ[cspec]
    +            Idspec[count] = val

    second problem

                boundary_dirichlet!(sys, icc, 2, Δu)
    +
    +            U = solve(sys; inival = U)
    +
    +            factory = TestFunctionFactory(sys)
    +            tf = testfunction(factory, [1], [2])
    +            I = integrate(sys, tf, U)
    +
    +            Iicc[count] = I[icc]
    +
    +            count = count + 1
    +        end # bias loop

    plot

            dvws = views(UQ, dspec, subgrids, sysQ)
    +        cvws = views(UQ, cspec, subgrids, sysQ)
    +
    +        vis = GridVisualizer(; layout = (2, 1), resolution = (600, 300), Plotter = Plotter)
    +
    +        for i in eachindex(dvws)
    +            scalarplot!(vis[1, 1], subgrids[i], dvws[i]; flimits = (-0.5, 1.5),
    +                        title = @sprintf("Solution with rate=%.2f", data.rate),
    +                        label = "discont quantity", clear = false, color = :red)
    +            scalarplot!(vis[1, 1], subgrids[i], cvws[i]; label = "cont quantity",
    +                        clear = false, color = :green)
    +        end
    +        scalarplot!(vis[1, 1], grid, U[icc, :]; label = "without quantity", clear = false,
    +                    linestyle = :dot, color = :blue)
    +
    +        scalarplot!(vis[2, 1], biasval, Idspec; clear = false,
    +                    title = @sprintf("IV with rate=%.2f", data.rate),
    +                    label = "discont quantity", color = :red)
    +        scalarplot!(vis[2, 1], biasval, Icspec; clear = false, title = "Current",
    +                    label = "cont quantity", color = :green)
    +        scalarplot!(vis[2, 1], biasval, Iicc; clear = false, label = "discont quantity",
    +                    linestyle = :dot, color = :blue, show = true)
    +
    +        reveal(vis)
    +        sleep(0.2)
    +    end # rate loop
    +
    +    errorIV = norm(Idspec - Icspec, 2)
    +
    +    return errorIV
    +end
    +
    +using Test
    +function runtests()
    +    testval = 6.085802139465579e-7
    +    @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example422_InterfaceQuantities.jl b/v1.19.1/module_examples/Example422_InterfaceQuantities.jl new file mode 100644 index 000000000..b1e4fa2d5 --- /dev/null +++ b/v1.19.1/module_examples/Example422_InterfaceQuantities.jl @@ -0,0 +1,247 @@ +#= + +# 422: Drift-Diffusion with Discontinuous and Interface Potentials +([source code](@__SOURCE_URL__)) + +Nondimensionalized semiconductor device equations (with artificial doping) +with Discontinuousquantities and additional Interfacequantities. + +=# + +module Example422_InterfaceQuantities + +using VoronoiFVM +using ExtendableGrids +using GridVisualize + +function main(; n = 5, Plotter = nothing, tend = 20.0, unknown_storage = :sparse, + reactionN = 5.0e0, reactionP = 5.0e0, assembly = :edgewise) + + ################################################################################ + #### grid + ################################################################################ + h1 = 1.0 + h2 = 1.0 + h_total = h1 + h2 + + # region numbers + region1 = 1 + region2 = 2 + regions = [region1, region2] + numberOfRegions = length(regions) + + # boundary region numbers + bregion1 = 1 + bregion2 = 2 + bjunction = 3 + + coord_1 = collect(range(0.0; stop = h1, length = n)) + coord_2 = collect(range(h1; stop = h1 + h2, length = n)) + coord = glue(coord_1, coord_2) + + grid = simplexgrid(coord) + + # specify inner regions + cellmask!(grid, [0.0], [h1], region1) + cellmask!(grid, [h1], [h1 + h2], region2) + + # specify outer regions + bfacemask!(grid, [0.0], [0.0], bregion1) + bfacemask!(grid, [h_total], [h_total], bregion2) + + # inner interfaces + bfacemask!(grid, [h1], [h1], bjunction) + + #gridplot(grid, Plotter = nothing, legend=:rt) + + ################################################################################ + ######### system + ################################################################################ + + sys = VoronoiFVM.System(grid; unknown_storage = unknown_storage, assembly = assembly) + iphin = DiscontinuousQuantity(sys, 1:numberOfRegions; id = 1) + iphip = DiscontinuousQuantity(sys, 1:numberOfRegions; id = 2) + iphinb = InterfaceQuantity(sys, [bjunction]; id = 3) + iphipb = InterfaceQuantity(sys, [bjunction]; id = 4) + ipsi = ContinuousQuantity(sys, 1:numberOfRegions; id = 5) + + NA = [10.0, 0.0] + ND = [0.0, 10.0] + + function storage!(f, u, node) + etan = -((u[iphin] - u[ipsi])) + etap = ((u[iphip] - u[ipsi])) + + f[iphin] = -exp(etan) + f[iphip] = exp(etap) + + f[ipsi] = 0.0 + end + + function reaction!(f, u, node) + etan = -((u[iphin] - u[ipsi])) + etap = ((u[iphip] - u[ipsi])) + + f[ipsi] = -(ND[node.region] - exp(etan) + exp(etap) - NA[node.region]) + ######################## + r0 = 1.0e-4 + recomb = r0 * exp(etan) * exp(etap) + + f[iphin] = -recomb + f[iphip] = recomb + end + + function flux!(f, u, node) + f[ipsi] = -(u[ipsi, 2] - u[ipsi, 1]) + + ######################## + bp, bm = fbernoulli_pm(-(u[ipsi, 2] - u[ipsi, 1])) + + etan1 = -((u[iphin, 1] - u[ipsi, 1])) + etap1 = ((u[iphip, 1] - u[ipsi, 1])) + + etan2 = -((u[iphin, 2] - u[ipsi, 2])) + etap2 = ((u[iphip, 2] - u[ipsi, 2])) + + f[iphin] = (bm * exp(etan2) - bp * exp(etan1)) + f[iphip] = -(bp * exp(etap2) - bm * exp(etap1)) + end + + function breaction!(f, u, bnode) + if bnode.region == bjunction + # left values + nleft = exp(-((u[iphin, 1] - u[ipsi]))) + pleft = exp(((u[iphip, 1] - u[ipsi]))) + + # interface species + n_interf = exp(-((u[iphinb] - u[ipsi]))) + p_interf = exp(((u[iphipb] - u[ipsi]))) + + # right values + nright = exp(-((u[iphin, 2] - u[ipsi]))) + pright = exp(((u[iphip, 2] - u[ipsi]))) + ################ + + # left and right reaction for n + f[iphin, 1] = reactionN * (nleft - n_interf) + f[iphin, 2] = reactionN * (nright - n_interf) + + # left and right reaction for p + f[iphip, 1] = reactionP * (pleft - p_interf) + f[iphip, 2] = reactionP * (pright - p_interf) + + # interface species reaction + f[iphinb] = -(f[iphin, 1] + f[iphin, 2]) + f[iphipb] = -(f[iphip, 1] + f[iphip, 2]) + end + end + + function bstorage!(f, u, bnode) + f[ipsi] = 0.0 + + if bnode.region == bjunction + etan = -((u[iphinb] - u[ipsi])) + etap = ((u[iphipb] - u[ipsi])) + + f[iphinb] = -exp(etan) + f[iphipb] = exp(etap) + end + end + + physics!(sys, + VoronoiFVM.Physics(; flux = flux!, + storage = storage!, + reaction = reaction!, + breaction = breaction!, + bstorage = bstorage!)) + + boundary_dirichlet!(sys, iphin, bregion1, 4.0) + boundary_dirichlet!(sys, iphip, bregion1, 4.0) + boundary_dirichlet!(sys, ipsi, bregion1, 0.0) + boundary_dirichlet!(sys, iphin, bregion2, 0.0) + boundary_dirichlet!(sys, iphip, bregion2, 0.0) + boundary_dirichlet!(sys, ipsi, bregion2, 5.0) + + ################################################################################ + ######### time loop + ################################################################################ + + ## Create a solution array + sol = unknowns(sys) + sol .= 0.0 + t0 = 1.0e-6 + ntsteps = 10 + tvalues = range(t0; stop = tend, length = ntsteps) + + for istep = 2:ntsteps + t = tvalues[istep] # Actual time + Δt = t - tvalues[istep - 1] # Time step size + + #println("Δt = ", t) + + sol = solve(sys; inival = sol, tstep = Δt) + end # time loop + + ################################################################################ + ######### Bias Loop + ################################################################################ + biasval = range(0; stop = 2.0, length = 10) + Idspec = zeros(0) + + for Δu in biasval + boundary_dirichlet!(sys, iphin, bregion1, 4.0 + Δu) + boundary_dirichlet!(sys, iphip, bregion1, 4.0 + Δu) + boundary_dirichlet!(sys, ipsi, bregion1, 0.0 + Δu) + + #println("Δu = ", Δu) + + sol = solve(sys; inival = sol) + + ## get current + factory = TestFunctionFactory(sys) + tf = testfunction(factory, [1], [2]) + I = integrate(sys, tf, sol) + + val = 0.0 + for ii = 1:(length(I) - 1) + val = val + I[ii] + end + + push!(Idspec, val) + end # bias loop + + ################################################################################ + ######### Plotting + ################################################################################ + + vis = GridVisualizer(; Plotter = Plotter, layout = (2, 1)) + + subgridBulk = subgrids(iphin, sys) + phin_sol = views(sol, iphin, subgridBulk, sys) + phip_sol = views(sol, iphip, subgridBulk, sys) + psi_sol = views(sol, ipsi, subgridBulk, sys) + + for i in eachindex(phin_sol) + scalarplot!(vis[1, 1], subgridBulk[i], phin_sol[i]; clear = false, color = :green) + scalarplot!(vis[1, 1], subgridBulk[i], phip_sol[i]; clear = false, color = :red) + scalarplot!(vis[1, 1], subgridBulk[i], psi_sol[i]; clear = false, color = :blue) + end + + scalarplot!(vis[2, 1], biasval, Idspec; clear = false, color = :red) + + bgrid = subgrids(iphinb, sys) + sol_bound = views(sol, iphinb, bgrid, sys) + + return sol_bound[1] +end # main + +using Test +function runtests() + testval = 0.35545473758267826 + @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval && + main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval && + main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval +end + +end # module diff --git a/v1.19.1/module_examples/Example422_InterfaceQuantities/index.html b/v1.19.1/module_examples/Example422_InterfaceQuantities/index.html new file mode 100644 index 000000000..f9a680404 --- /dev/null +++ b/v1.19.1/module_examples/Example422_InterfaceQuantities/index.html @@ -0,0 +1,206 @@ + +422: Drift-Diffusion with Discontinuous and Interface Potentials · VoronoiFVM.jl

    422: Drift-Diffusion with Discontinuous and Interface Potentials

    (source code)

    Nondimensionalized semiconductor device equations (with artificial doping) with Discontinuousquantities and additional Interfacequantities.

    module Example422_InterfaceQuantities
    +
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +
    +function main(; n = 5, Plotter = nothing, tend = 20.0, unknown_storage = :sparse,
    +              reactionN = 5.0e0, reactionP = 5.0e0, assembly = :edgewise)
    +
    +    ################################################################################
    +    #### grid
    +    ################################################################################
    +    h1 = 1.0
    +    h2 = 1.0
    +    h_total = h1 + h2

    region numbers

        region1 = 1
    +    region2 = 2
    +    regions = [region1, region2]
    +    numberOfRegions = length(regions)

    boundary region numbers

        bregion1 = 1
    +    bregion2 = 2
    +    bjunction = 3
    +
    +    coord_1 = collect(range(0.0; stop = h1, length = n))
    +    coord_2 = collect(range(h1; stop = h1 + h2, length = n))
    +    coord = glue(coord_1, coord_2)
    +
    +    grid = simplexgrid(coord)

    specify inner regions

        cellmask!(grid, [0.0], [h1], region1)
    +    cellmask!(grid, [h1], [h1 + h2], region2)

    specify outer regions

        bfacemask!(grid, [0.0], [0.0], bregion1)
    +    bfacemask!(grid, [h_total], [h_total], bregion2)

    inner interfaces

        bfacemask!(grid, [h1], [h1], bjunction)
    +
    +    #gridplot(grid, Plotter = nothing, legend=:rt)
    +
    +    ################################################################################
    +    #########  system
    +    ################################################################################
    +
    +    sys = VoronoiFVM.System(grid; unknown_storage = unknown_storage, assembly = assembly)
    +    iphin = DiscontinuousQuantity(sys, 1:numberOfRegions; id = 1)
    +    iphip = DiscontinuousQuantity(sys, 1:numberOfRegions; id = 2)
    +    iphinb = InterfaceQuantity(sys, [bjunction]; id = 3)
    +    iphipb = InterfaceQuantity(sys, [bjunction]; id = 4)
    +    ipsi = ContinuousQuantity(sys, 1:numberOfRegions; id = 5)
    +
    +    NA = [10.0, 0.0]
    +    ND = [0.0, 10.0]
    +
    +    function storage!(f, u, node)
    +        etan = -((u[iphin] - u[ipsi]))
    +        etap = ((u[iphip] - u[ipsi]))
    +
    +        f[iphin] = -exp(etan)
    +        f[iphip] = exp(etap)
    +
    +        f[ipsi] = 0.0
    +    end
    +
    +    function reaction!(f, u, node)
    +        etan = -((u[iphin] - u[ipsi]))
    +        etap = ((u[iphip] - u[ipsi]))
    +
    +        f[ipsi] = -(ND[node.region] - exp(etan) + exp(etap) - NA[node.region])
    +        ########################
    +        r0 = 1.0e-4
    +        recomb = r0 * exp(etan) * exp(etap)
    +
    +        f[iphin] = -recomb
    +        f[iphip] = recomb
    +    end
    +
    +    function flux!(f, u, node)
    +        f[ipsi] = -(u[ipsi, 2] - u[ipsi, 1])
    +
    +        ########################
    +        bp, bm = fbernoulli_pm(-(u[ipsi, 2] - u[ipsi, 1]))
    +
    +        etan1 = -((u[iphin, 1] - u[ipsi, 1]))
    +        etap1 = ((u[iphip, 1] - u[ipsi, 1]))
    +
    +        etan2 = -((u[iphin, 2] - u[ipsi, 2]))
    +        etap2 = ((u[iphip, 2] - u[ipsi, 2]))
    +
    +        f[iphin] = (bm * exp(etan2) - bp * exp(etan1))
    +        f[iphip] = -(bp * exp(etap2) - bm * exp(etap1))
    +    end
    +
    +    function breaction!(f, u, bnode)
    +        if bnode.region == bjunction

    left values

                nleft = exp(-((u[iphin, 1] - u[ipsi])))
    +            pleft = exp(((u[iphip, 1] - u[ipsi])))

    interface species

                n_interf = exp(-((u[iphinb] - u[ipsi])))
    +            p_interf = exp(((u[iphipb] - u[ipsi])))

    right values

                nright = exp(-((u[iphin, 2] - u[ipsi])))
    +            pright = exp(((u[iphip, 2] - u[ipsi])))
    +            ################

    left and right reaction for n

                f[iphin, 1] = reactionN * (nleft - n_interf)
    +            f[iphin, 2] = reactionN * (nright - n_interf)

    left and right reaction for p

                f[iphip, 1] = reactionP * (pleft - p_interf)
    +            f[iphip, 2] = reactionP * (pright - p_interf)

    interface species reaction

                f[iphinb] = -(f[iphin, 1] + f[iphin, 2])
    +            f[iphipb] = -(f[iphip, 1] + f[iphip, 2])
    +        end
    +    end
    +
    +    function bstorage!(f, u, bnode)
    +        f[ipsi] = 0.0
    +
    +        if bnode.region == bjunction
    +            etan = -((u[iphinb] - u[ipsi]))
    +            etap = ((u[iphipb] - u[ipsi]))
    +
    +            f[iphinb] = -exp(etan)
    +            f[iphipb] = exp(etap)
    +        end
    +    end
    +
    +    physics!(sys,
    +             VoronoiFVM.Physics(; flux = flux!,
    +                                storage = storage!,
    +                                reaction = reaction!,
    +                                breaction = breaction!,
    +                                bstorage = bstorage!))
    +
    +    boundary_dirichlet!(sys, iphin, bregion1, 4.0)
    +    boundary_dirichlet!(sys, iphip, bregion1, 4.0)
    +    boundary_dirichlet!(sys, ipsi, bregion1, 0.0)
    +    boundary_dirichlet!(sys, iphin, bregion2, 0.0)
    +    boundary_dirichlet!(sys, iphip, bregion2, 0.0)
    +    boundary_dirichlet!(sys, ipsi, bregion2, 5.0)
    +
    +    ################################################################################
    +    #########  time loop
    +    ################################################################################
    +
    +    # Create a solution array
    +    sol = unknowns(sys)
    +    sol .= 0.0
    +    t0 = 1.0e-6
    +    ntsteps = 10
    +    tvalues = range(t0; stop = tend, length = ntsteps)
    +
    +    for istep = 2:ntsteps
    +        t = tvalues[istep]       # Actual time
    +        Δt = t - tvalues[istep - 1] # Time step size
    +
    +        #println("Δt = ", t)
    +
    +        sol = solve(sys; inival = sol, tstep = Δt)
    +    end # time loop
    +
    +    ################################################################################
    +    #########  Bias Loop
    +    ################################################################################
    +    biasval = range(0; stop = 2.0, length = 10)
    +    Idspec = zeros(0)
    +
    +    for Δu in biasval
    +        boundary_dirichlet!(sys, iphin, bregion1, 4.0 + Δu)
    +        boundary_dirichlet!(sys, iphip, bregion1, 4.0 + Δu)
    +        boundary_dirichlet!(sys, ipsi, bregion1, 0.0 + Δu)
    +
    +        #println("Δu = ", Δu)
    +
    +        sol = solve(sys; inival = sol)
    +
    +        # get current
    +        factory = TestFunctionFactory(sys)
    +        tf = testfunction(factory, [1], [2])
    +        I = integrate(sys, tf, sol)
    +
    +        val = 0.0
    +        for ii = 1:(length(I) - 1)
    +            val = val + I[ii]
    +        end
    +
    +        push!(Idspec, val)
    +    end # bias loop
    +
    +    ################################################################################
    +    #########  Plotting
    +    ################################################################################
    +
    +    vis = GridVisualizer(; Plotter = Plotter, layout = (2, 1))
    +
    +    subgridBulk = subgrids(iphin, sys)
    +    phin_sol = views(sol, iphin, subgridBulk, sys)
    +    phip_sol = views(sol, iphip, subgridBulk, sys)
    +    psi_sol = views(sol, ipsi, subgridBulk, sys)
    +
    +    for i in eachindex(phin_sol)
    +        scalarplot!(vis[1, 1], subgridBulk[i], phin_sol[i]; clear = false, color = :green)
    +        scalarplot!(vis[1, 1], subgridBulk[i], phip_sol[i]; clear = false, color = :red)
    +        scalarplot!(vis[1, 1], subgridBulk[i], psi_sol[i]; clear = false, color = :blue)
    +    end
    +
    +    scalarplot!(vis[2, 1], biasval, Idspec; clear = false, color = :red)
    +
    +    bgrid = subgrids(iphinb, sys)
    +    sol_bound = views(sol, iphinb, bgrid, sys)
    +
    +    return sol_bound[1]
    +end # main
    +
    +using Test
    +function runtests()
    +    testval = 0.35545473758267826
    +    @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&
    +          main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&
    +          main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval
    +end
    +
    +end # module

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example424_AbstractQuantitiesInit.jl b/v1.19.1/module_examples/Example424_AbstractQuantitiesInit.jl new file mode 100644 index 000000000..fc9bcffab --- /dev/null +++ b/v1.19.1/module_examples/Example424_AbstractQuantitiesInit.jl @@ -0,0 +1,78 @@ +#= + +# 424: Initialization of Abstract quantities + ([source code](@__SOURCE_URL__)) + +=# +module Example424_AbstractQuantitiesInit +using Printf +using VoronoiFVM +using ExtendableGrids +using GridVisualize +using LinearAlgebra + +function main(; N = 5, Plotter = nothing, unknown_storage = :sparse, assembly = :edgewise) + if 2 * (N ÷ 2) == N + N = N + 1 + end + + xcoord = range(0, 2; length = N) |> collect + grid = simplexgrid(xcoord) + cellmask!(grid, [1], [2], 2) + system = VoronoiFVM.System(grid; unknown_storage = unknown_storage, assembly = assembly) + + ## First, we introduce a continuous quantity which we name "cspec". Note that the "species number" can be assigned automatically if not given explicitly. + cspec = ContinuousQuantity(system, 1:2) + + ## A discontinuous quantity can be introduced as well. by default, each reagion gets a new species number. This can be overwritten by the user. + dspec = DiscontinuousQuantity(system, [1, 2]) + + allsubgrids = VoronoiFVM.subgrids(dspec, system) + + function init(u, node) + ireg = node.region + if ireg == 1 + u[dspec] = 1 + u[cspec] = 10 + else + u[dspec] = 2 + u[cspec] = 20 + end + end + + function check(u) + duviews = views(u, dspec, allsubgrids, system) + cuviews = views(u, cspec, allsubgrids, system) + result = Bool[] + psh!(b) = push!(result, b) + + (duviews[1] == fill(1.0, (N - 1) ÷ 2 + 1)) |> psh! + (duviews[2] == fill(2.0, (N - 1) ÷ 2 + 1)) |> psh! + (cuviews[2] == fill(20.0, (N - 1) ÷ 2 + 1)) |> psh! + (cuviews[1][1:(end - 1)] == fill(10.0, (N - 1) ÷ 2)) |> psh! + + all(result) + end + + ## "Classical" solution creation + u = unknowns(system; inifunc = init) + + ## We can use Base.map to create an initial value + v = map(init, system) + + ## We also can map an init function onto the system + w = unknowns(system) + map!(init, w, system) + + check(u) && check(v) && check(w) +end + +using Test +function runtests() + @test main(; unknown_storage = :sparse, assembly = :edgewise) && + main(; unknown_storage = :dense, assembly = :edgewise) && + main(; unknown_storage = :sparse, assembly = :cellwise) && + main(; unknown_storage = :dense, assembly = :cellwise) +end + +end diff --git a/v1.19.1/module_examples/Example424_AbstractQuantitiesInit/index.html b/v1.19.1/module_examples/Example424_AbstractQuantitiesInit/index.html new file mode 100644 index 000000000..f3f23ea4e --- /dev/null +++ b/v1.19.1/module_examples/Example424_AbstractQuantitiesInit/index.html @@ -0,0 +1,73 @@ + +424: Initialization of Abstract quantities · VoronoiFVM.jl

    424: Initialization of Abstract quantities

    (source code)

    module Example424_AbstractQuantitiesInit
    +using Printf
    +using VoronoiFVM
    +using ExtendableGrids
    +using GridVisualize
    +using LinearAlgebra
    +
    +function main(; N = 5, Plotter = nothing, unknown_storage = :sparse, assembly = :edgewise)
    +    if 2 * (N ÷ 2) == N
    +        N = N + 1
    +    end
    +
    +    xcoord = range(0, 2; length = N) |> collect
    +    grid = simplexgrid(xcoord)
    +    cellmask!(grid, [1], [2], 2)
    +    system = VoronoiFVM.System(grid; unknown_storage = unknown_storage, assembly = assembly)
    +
    +    # First, we introduce a continuous quantity which we name "cspec". Note that the "species number" can be assigned automatically if not given explicitly.
    +    cspec = ContinuousQuantity(system, 1:2)
    +
    +    # A discontinuous quantity can be introduced as well. by default, each reagion gets a new species number. This can be overwritten by the user.
    +    dspec = DiscontinuousQuantity(system, [1, 2])
    +
    +    allsubgrids = VoronoiFVM.subgrids(dspec, system)
    +
    +    function init(u, node)
    +        ireg = node.region
    +        if ireg == 1
    +            u[dspec] = 1
    +            u[cspec] = 10
    +        else
    +            u[dspec] = 2
    +            u[cspec] = 20
    +        end
    +    end
    +
    +    function check(u)
    +        duviews = views(u, dspec, allsubgrids, system)
    +        cuviews = views(u, cspec, allsubgrids, system)
    +        result = Bool[]
    +        psh!(b) = push!(result, b)
    +
    +        (duviews[1] == fill(1.0, (N - 1) ÷ 2 + 1)) |> psh!
    +        (duviews[2] == fill(2.0, (N - 1) ÷ 2 + 1)) |> psh!
    +        (cuviews[2] == fill(20.0, (N - 1) ÷ 2 + 1)) |> psh!
    +        (cuviews[1][1:(end - 1)] == fill(10.0, (N - 1) ÷ 2)) |> psh!
    +
    +        all(result)
    +    end
    +
    +    # "Classical" solution creation
    +    u = unknowns(system; inifunc = init)
    +
    +    # We can use Base.map to create  an initial value
    +    v = map(init, system)
    +
    +    # We also can map  an init function onto the system
    +    w = unknowns(system)
    +    map!(init, w, system)
    +
    +    check(u) && check(v) && check(w)
    +end
    +
    +using Test
    +function runtests()
    +    @test main(; unknown_storage = :sparse, assembly = :edgewise) &&
    +          main(; unknown_storage = :dense, assembly = :edgewise) &&
    +          main(; unknown_storage = :sparse, assembly = :cellwise) &&
    +          main(; unknown_storage = :dense, assembly = :cellwise)
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example430_ParameterDerivativesStationary.jl b/v1.19.1/module_examples/Example430_ParameterDerivativesStationary.jl new file mode 100644 index 000000000..410e6189a --- /dev/null +++ b/v1.19.1/module_examples/Example430_ParameterDerivativesStationary.jl @@ -0,0 +1,237 @@ +#= + +# 430: Parameter Derivatives (stationary) + ([source code](@__SOURCE_URL__)) + +Explore different ways to calculate sensitivities. +This is still experimental. +=# +module Example430_ParameterDerivativesStationary + +using VoronoiFVM, ExtendableGrids +using GridVisualize +using ExtendableSparse +using ForwardDiff, DiffResults +using SparseDiffTools, SparseArrays +using ILUZero, LinearSolve + +""" + f(P) + +Parameter dependent function which creates system and solves it +""" +function f(P; n = 10) + p = P[1] + + valuetype = typeof(p) + nspecies = 1 + ispec = 1 + + function flux!(f, u, edge) + f[1] = (1 + p) * (u[1, 1]^2 - u[1, 2]^2) + end + + function r!(f, u, edge) + f[1] = p * u[1]^5 + end + + function bc!(f, u, node) + boundary_dirichlet!(f, u, node, ispec, 1, 0.0) + boundary_dirichlet!(f, u, node, ispec, 3, p) + end + + X = collect(0:(1.0 / n):1) + grid = VoronoiFVM.Grid(X, X) + sys = VoronoiFVM.System(grid; valuetype, species = [1], flux = flux!, reaction = r!, + bcondition = bc!) + tff = VoronoiFVM.TestFunctionFactory(sys) + tfc = testfunction(tff, [1], [3]) + sol = solve(sys; inival = 0.5) + [integrate(sys, tfc, sol)[1]] +end + +""" + runf(;Plotter, n=10) + +Run parameter series, plot f(p), df(p). +For each p,create a new system. Use VoronoiFVM with dual numbers. Pass parameters via closure. +""" +function runf(; Plotter = nothing, n = 10) + P = 0.1:0.05:2 + dresult = DiffResults.JacobianResult(ones(1)) + F = zeros(0) + DF = zeros(0) + ff(p) = f(p; n) + @time for p ∈ P + ForwardDiff.jacobian!(dresult, ff, [p]) + push!(F, DiffResults.value(dresult)[1]) + push!(DF, DiffResults.jacobian(dresult)[1]) + end + vis = GridVisualizer(; Plotter, legend = :lt) + scalarplot!(vis, P, F; color = :red, label = "f") + scalarplot!(vis, P, DF; color = :blue, label = "df", clear = false, show = true) + sum(DF) +end + +function fluxg!(f, u, edge, data) + f[1] = (1 + data.p) * (u[1, 1]^2 - u[1, 2]^2) +end + +function rg!(f, u, edge, data) + f[1] = data.p * u[1]^5 +end + +function bcg!(f, u, node, data) + boundary_dirichlet!(f, u, node, 1, 1, 0.0) + boundary_dirichlet!(f, u, node, 1, 3, data.p) +end + +Base.@kwdef mutable struct MyData{Tv} + p::Tv = 1.0 +end + +""" + rung(;Plotter, n=10) + +Same as runf, but keep one system pass parameters via data. +""" +function rung(; Plotter = nothing, method_linear = SparspakFactorization(), n = 10) + X = collect(0:(1.0 / n):1) + grid = VoronoiFVM.Grid(X, X) + + # ugly but simple. By KISS we should first provide this way. + + sys = nothing + data = nothing + tfc = nothing + + function g(P) + Tv = eltype(P) + if isnothing(sys) + data = MyData(one(Tv)) + sys = VoronoiFVM.System(grid; valuetype = Tv, species = [1], flux = fluxg!, + reaction = rg!, bcondition = bcg!, data, + unknown_storage = :dense) + tff = VoronoiFVM.TestFunctionFactory(sys) + tfc = testfunction(tff, [1], [3]) + end + data.p = P[1] + sol = solve(sys; inival = 0.5, method_linear, precon_linear = ILUZeroPreconditioner()) + [integrate(sys, tfc, sol)[1]] + end + + dresult = DiffResults.JacobianResult(ones(1)) + + P = 0.1:0.05:2 + G = zeros(0) + DG = zeros(0) + @time for p ∈ P + ForwardDiff.jacobian!(dresult, g, [p]) + push!(G, DiffResults.value(dresult)[1]) + push!(DG, DiffResults.jacobian(dresult)[1]) + end + + vis = GridVisualizer(; Plotter, legend = :lt) + scalarplot!(vis, P, G; color = :red, label = "g") + scalarplot!(vis, P, DG; color = :blue, label = "dg", clear = false, show = true) + sum(DG) +end + +######################################################################### + +function fluxh!(f, u, edge) + p = parameters(u)[1] + f[1] = (1 + p) * (u[1, 1]^2 - u[1, 2]^2) +end + +function rh!(f, u, edge) + p = parameters(u)[1] + f[1] = p * u[1]^5 +end + +function bch!(f, u, node) + p = parameters(u)[1] + boundary_dirichlet!(f, u, node, 1, 1, 0.0) + boundary_dirichlet!(f, u, node, 1, 3, p) +end + +""" + runh(;Plotter, n=10) + +Same as runf, but use "normal" calculation (don't solve in dual numbers), and calculate dudp during +main assembly loop. + +This needs quite a bit of additional implementation + corresponding API and still lacks local assembly of the +measurement derivative (when using testfunction based calculation) when calculating current. +""" +function runh(; Plotter = nothing, n = 10) + X = collect(0:(1.0 / n):1) + grid = VoronoiFVM.Grid(X, X) + + sys = VoronoiFVM.System(grid; species = [1], flux = fluxh!, reaction = rh!, + bcondition = bch!, unknown_storage = :dense, nparams = 1) + tff = VoronoiFVM.TestFunctionFactory(sys) + tfc = testfunction(tff, [1], [3]) + + function measp(params, u) + Tp = eltype(params) + up = Tp.(u) + integrate(sys, tfc, up; params = params)[1] + end + + params = [0.0] + + function mymeas!(meas, U) + u = reshape(U, sys) + meas[1] = integrate(sys, tfc, u; params)[1] + nothing + end + + dp = 0.05 + P = 0.1:dp:2 + U0 = solve(sys; inival = 0.5, params = [P[1]]) + + ndof = num_dof(sys) + colptr = [i for i = 1:(ndof + 1)] + rowval = [1 for i = 1:ndof] + nzval = [1.0 for in = 1:ndof] + ∂m∂u = zeros(1, ndof) + colors = matrix_colors(∂m∂u) + + H = zeros(0) + DH = zeros(0) + DHx = zeros(0) + m = zeros(1) + + @time for p ∈ P + params[1] = p + sol = solve(sys; inival = 0.5, params) + mymeas!(m, sol) + push!(H, m[1]) + + # this one is expensive - we would need to assemble this jacobian via local calls + forwarddiff_color_jacobian!(∂m∂u, mymeas!, vec(sol); colorvec = colors) + + # need to have the full derivative of m vs p + ∂m∂p = ForwardDiff.gradient(p -> measp(p, sol), params) + + dudp = sys.matrix \ vec(sys.dudp[1]) + dmdp = -∂m∂u * dudp + ∂m∂p + push!(DH, dmdp[1]) + end + + vis = GridVisualizer(; Plotter, legend = :lt) + scalarplot!(vis, P, H; color = :red, label = "h") + scalarplot!(vis, P, DH; color = :blue, label = "dh", clear = false, show = true) + sum(DH) +end + +using Test +function runtests() + testval = 489.3432830184927 + @test runf() ≈ testval + @test rung() ≈ testval + @test runh() ≈ testval +end + +end diff --git a/v1.19.1/module_examples/Example430_ParameterDerivativesStationary/index.html b/v1.19.1/module_examples/Example430_ParameterDerivativesStationary/index.html new file mode 100644 index 000000000..a8759474b --- /dev/null +++ b/v1.19.1/module_examples/Example430_ParameterDerivativesStationary/index.html @@ -0,0 +1,220 @@ + +430: Parameter Derivatives (stationary) · VoronoiFVM.jl

    430: Parameter Derivatives (stationary)

    (source code)

    Explore different ways to calculate sensitivities. This is still experimental.

    module Example430_ParameterDerivativesStationary
    +
    +using VoronoiFVM, ExtendableGrids
    +using GridVisualize
    +using ExtendableSparse
    +using ForwardDiff, DiffResults
    +using SparseDiffTools, SparseArrays
    +using ILUZero, LinearSolve
    +
    +"""
    +    f(P)
    +
    +Parameter dependent function which creates system and solves it
    +"""
    +function f(P; n = 10)
    +    p = P[1]
    +
    +    valuetype = typeof(p)
    +    nspecies = 1
    +    ispec = 1
    +
    +    function flux!(f, u, edge)
    +        f[1] = (1 + p) * (u[1, 1]^2 - u[1, 2]^2)
    +    end
    +
    +    function r!(f, u, edge)
    +        f[1] = p * u[1]^5
    +    end
    +
    +    function bc!(f, u, node)
    +        boundary_dirichlet!(f, u, node, ispec, 1, 0.0)
    +        boundary_dirichlet!(f, u, node, ispec, 3, p)
    +    end
    +
    +    X = collect(0:(1.0 / n):1)
    +    grid = VoronoiFVM.Grid(X, X)
    +    sys = VoronoiFVM.System(grid; valuetype, species = [1], flux = flux!, reaction = r!,
    +                            bcondition = bc!)
    +    tff = VoronoiFVM.TestFunctionFactory(sys)
    +    tfc = testfunction(tff, [1], [3])
    +    sol = solve(sys; inival = 0.5)
    +    [integrate(sys, tfc, sol)[1]]
    +end
    +
    +"""
    +    runf(;Plotter, n=10)
    +
    +Run parameter series, plot f(p), df(p).
    +For each p,create a new system. Use VoronoiFVM with dual numbers. Pass parameters via closure.
    +"""
    +function runf(; Plotter = nothing, n = 10)
    +    P = 0.1:0.05:2
    +    dresult = DiffResults.JacobianResult(ones(1))
    +    F = zeros(0)
    +    DF = zeros(0)
    +    ff(p) = f(p; n)
    +    @time for p ∈ P
    +        ForwardDiff.jacobian!(dresult, ff, [p])
    +        push!(F, DiffResults.value(dresult)[1])
    +        push!(DF, DiffResults.jacobian(dresult)[1])
    +    end
    +    vis = GridVisualizer(; Plotter, legend = :lt)
    +    scalarplot!(vis, P, F; color = :red, label = "f")
    +    scalarplot!(vis, P, DF; color = :blue, label = "df", clear = false, show = true)
    +    sum(DF)
    +end
    +
    +function fluxg!(f, u, edge, data)
    +    f[1] = (1 + data.p) * (u[1, 1]^2 - u[1, 2]^2)
    +end
    +
    +function rg!(f, u, edge, data)
    +    f[1] = data.p * u[1]^5
    +end
    +
    +function bcg!(f, u, node, data)
    +    boundary_dirichlet!(f, u, node, 1, 1, 0.0)
    +    boundary_dirichlet!(f, u, node, 1, 3, data.p)
    +end
    +
    +Base.@kwdef mutable struct MyData{Tv}
    +    p::Tv = 1.0
    +end
    +
    +"""
    +    rung(;Plotter, n=10)
    +
    +Same as runf, but keep one system pass parameters via data.
    +"""
    +function rung(; Plotter = nothing, method_linear = SparspakFactorization(), n = 10)
    +    X = collect(0:(1.0 / n):1)
    +    grid = VoronoiFVM.Grid(X, X)

    ugly but simple. By KISS we should first provide this way.

        sys = nothing
    +    data = nothing
    +    tfc = nothing
    +
    +    function g(P)
    +        Tv = eltype(P)
    +        if isnothing(sys)
    +            data = MyData(one(Tv))
    +            sys = VoronoiFVM.System(grid; valuetype = Tv, species = [1], flux = fluxg!,
    +                                    reaction = rg!, bcondition = bcg!, data,
    +                                    unknown_storage = :dense)
    +            tff = VoronoiFVM.TestFunctionFactory(sys)
    +            tfc = testfunction(tff, [1], [3])
    +        end
    +        data.p = P[1]
    +        sol = solve(sys; inival = 0.5, method_linear, precon_linear = ILUZeroPreconditioner())
    +        [integrate(sys, tfc, sol)[1]]
    +    end
    +
    +    dresult = DiffResults.JacobianResult(ones(1))
    +
    +    P = 0.1:0.05:2
    +    G = zeros(0)
    +    DG = zeros(0)
    +    @time for p ∈ P
    +        ForwardDiff.jacobian!(dresult, g, [p])
    +        push!(G, DiffResults.value(dresult)[1])
    +        push!(DG, DiffResults.jacobian(dresult)[1])
    +    end
    +
    +    vis = GridVisualizer(; Plotter, legend = :lt)
    +    scalarplot!(vis, P, G; color = :red, label = "g")
    +    scalarplot!(vis, P, DG; color = :blue, label = "dg", clear = false, show = true)
    +    sum(DG)
    +end
    +
    +#########################################################################
    +
    +function fluxh!(f, u, edge)
    +    p = parameters(u)[1]
    +    f[1] = (1 + p) * (u[1, 1]^2 - u[1, 2]^2)
    +end
    +
    +function rh!(f, u, edge)
    +    p = parameters(u)[1]
    +    f[1] = p * u[1]^5
    +end
    +
    +function bch!(f, u, node)
    +    p = parameters(u)[1]
    +    boundary_dirichlet!(f, u, node, 1, 1, 0.0)
    +    boundary_dirichlet!(f, u, node, 1, 3, p)
    +end
    +
    +"""
    +    runh(;Plotter, n=10)
    +
    +Same as runf, but use "normal" calculation (don't solve in dual numbers), and calculate dudp during
    +main assembly loop.
    +
    +This needs quite a bit of additional implementation + corresponding API and still lacks local assembly of the
    +measurement derivative (when using testfunction based calculation) when calculating current.
    +"""
    +function runh(; Plotter = nothing, n = 10)
    +    X = collect(0:(1.0 / n):1)
    +    grid = VoronoiFVM.Grid(X, X)
    +
    +    sys = VoronoiFVM.System(grid; species = [1], flux = fluxh!, reaction = rh!,
    +                            bcondition = bch!, unknown_storage = :dense, nparams = 1)
    +    tff = VoronoiFVM.TestFunctionFactory(sys)
    +    tfc = testfunction(tff, [1], [3])
    +
    +    function measp(params, u)
    +        Tp = eltype(params)
    +        up = Tp.(u)
    +        integrate(sys, tfc, up; params = params)[1]
    +    end
    +
    +    params = [0.0]
    +
    +    function mymeas!(meas, U)
    +        u = reshape(U, sys)
    +        meas[1] = integrate(sys, tfc, u; params)[1]
    +        nothing
    +    end
    +
    +    dp = 0.05
    +    P = 0.1:dp:2
    +    U0 = solve(sys; inival = 0.5, params = [P[1]])
    +
    +    ndof = num_dof(sys)
    +    colptr = [i for i = 1:(ndof + 1)]
    +    rowval = [1 for i = 1:ndof]
    +    nzval = [1.0 for in = 1:ndof]
    +    ∂m∂u = zeros(1, ndof)
    +    colors = matrix_colors(∂m∂u)
    +
    +    H = zeros(0)
    +    DH = zeros(0)
    +    DHx = zeros(0)
    +    m = zeros(1)
    +
    +    @time for p ∈ P
    +        params[1] = p
    +        sol = solve(sys; inival = 0.5, params)
    +        mymeas!(m, sol)
    +        push!(H, m[1])

    this one is expensive - we would need to assemble this jacobian via local calls

            forwarddiff_color_jacobian!(∂m∂u, mymeas!, vec(sol); colorvec = colors)

    need to have the full derivative of m vs p

            ∂m∂p = ForwardDiff.gradient(p -> measp(p, sol), params)
    +
    +        dudp = sys.matrix \ vec(sys.dudp[1])
    +        dmdp = -∂m∂u * dudp + ∂m∂p
    +        push!(DH, dmdp[1])
    +    end
    +
    +    vis = GridVisualizer(; Plotter, legend = :lt)
    +    scalarplot!(vis, P, H; color = :red, label = "h")
    +    scalarplot!(vis, P, DH; color = :blue, label = "dh", clear = false, show = true)
    +    sum(DH)
    +end
    +
    +using Test
    +function runtests()
    +    testval = 489.3432830184927
    +    @test runf()  ≈ testval
    +    @test rung()  ≈ testval
    +    @test runh()  ≈ testval
    +end
    +
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/module_examples/Example510_Mixture.jl b/v1.19.1/module_examples/Example510_Mixture.jl new file mode 100644 index 000000000..2cd96b9de --- /dev/null +++ b/v1.19.1/module_examples/Example510_Mixture.jl @@ -0,0 +1,246 @@ +# # 510: Mixture +# ([source code](@__SOURCE_URL__)) +#= + +Test mixture diffusion flux. The problem is here that in the flux function we need to +solve a linear system of equations which calculates the fluxes from the gradients.# +To do so without (heap) allocations can be achieved using StrideArrays, together with the +possibility to have static (compile time) information about the size of the local +arrays to be allocated. + +``u_i`` are the species partial pressures, ``\vec N_i`` are the species fluxes. +``D_i^K`` are the Knudsen diffusion coefficients, and ``D^B_{ij}`` are the binary diffusion coefficients. +```math + -\nabla \cdot \vec N_i =0 \quad (i=1\dots n)\\ + \frac{\vec N_i}{D^K_i} + \sum_{j\neq i}\frac{u_j \vec N_i - u_i \vec N_j}{D^B_{ij}} = -\vec \nabla u_i \quad (i=1\dots n) +``` +From this representation, we can derive the matrix ``M=(m_{ij})`` with +```math +m_{ii}= \frac{1}{D^K_i} + \sum_{j\neq i} \frac{u_j}{D_ij}\\ +m_{ij}= -\sum_{j\neq i} \frac{u_i}{D_ij} +``` +such that +```math + M\begin{pmatrix} +\vec N_1\\ +\vdots\\ +\vec N_n +\end{pmatrix} += +\begin{pmatrix} +\vec \nabla u_1\\ +\vdots\\ +\vec \nabla u_n +\end{pmatrix} +``` +In the two point flux finite volume discretization, this results into a corresponding linear system which calculates the discrete edge fluxes from the discrete gradients. +Here we demonstrate how to implement this in a fast, (heap) allocation free way. + +For this purpose, intermediate arrays need to be allocated on the stack with via `StrideArrays.jl` or `MArray` from `StaticArrays.jl` +They need to have the same element type as the unknowns passed to the flux function +(which could be Float64 or some dual number). +Size information must be static, e.g. a global constant, or, as demonstrated here, a type parameter. +Broadcasting probably should be avoided. + +As [documented in StrideArrays.jl](https://docs.juliahub.com/StrideArrays/Eyl7s/0.1.3/stack_allocation/), use `@gc_preserve` when passing a `StrideArray` as a function parameter. + +Alternatively, we can try to avoid StrideArrays.jl and use MArrays together with inlined code. +In this case, one should be aware of [this issue](https://github.com/JuliaArrays/StaticArrays.jl/issues/874), +which requires `@inbounds` e.g. with reverse order loops. + +See also [this Discourse thread](https://discourse.julialang.org/t/what-is-stridearrays-jl/97146). + +=# + +module Example510_Mixture + +using Printf +using VoronoiFVM +using VoronoiFVM.SolverStrategies + +using ExtendableGrids +using GridVisualize +using LinearAlgebra +using AMGCLWrap +using Random +using StrideArraysCore: @gc_preserve, StrideArray, StaticInt, PtrArray +using LinearSolve, ExtendableSparse +using StaticArrays +using ExtendableSparse + +struct MyData{NSPec} + DBinary::Symmetric{Float64, Matrix{Float64}} + DKnudsen::Vector{Float64} + diribc::Vector{Int} +end + +nspec(::MyData{NSpec}) where {NSpec} = NSpec + +function flux_strided(f, u, edge, data) + T = eltype(u) + M = StrideArray{T}(undef, StaticInt(nspec(data)), StaticInt(nspec(data))) + au = StrideArray{T}(undef, StaticInt(nspec(data))) + du = StrideArray{T}(undef, StaticInt(nspec(data))) + ipiv = StrideArray{Int}(undef, StaticInt(nspec(data))) + + for ispec = 1:nspec(data) + M[ispec, ispec] = 1.0 / data.DKnudsen[ispec] + du[ispec] = u[ispec, 1] - u[ispec, 2] + au[ispec] = 0.5 * (u[ispec, 1] + u[ispec, 2]) + end + + for ispec = 1:nspec(data) + for jspec = 1:nspec(data) + if ispec != jspec + M[ispec, ispec] += au[jspec] / data.DBinary[ispec, jspec] + M[ispec, jspec] = -au[ispec] / data.DBinary[ispec, jspec] + end + end + end + + if VERSION >= v"1.9-rc0" + ## Pivoting linear system solution via RecursiveFactorizations.jl + @gc_preserve inplace_linsolve!(M, du, ipiv) + else + ## Non-pivoting implementation currently implemented in vfvm_functions.jl + @gc_preserve inplace_linsolve!(M, du) + end + + for ispec = 1:nspec(data) + f[ispec] = du[ispec] + end +end + +function flux_marray(f, u, edge, data) + T = eltype(u) + n = nspec(data) + + M = MMatrix{nspec(data), nspec(data), T}(undef) + au = MVector{nspec(data), T}(undef) + du = MVector{nspec(data), T}(undef) + ipiv = MVector{nspec(data), Int}(undef) + + for ispec = 1:nspec(data) + M[ispec, ispec] = 1.0 / data.DKnudsen[ispec] + du[ispec] = u[ispec, 1] - u[ispec, 2] + au[ispec] = 0.5 * (u[ispec, 1] + u[ispec, 2]) + end + + for ispec = 1:nspec(data) + for jspec = 1:nspec(data) + if ispec != jspec + M[ispec, ispec] += au[jspec] / data.DBinary[ispec, jspec] + M[ispec, jspec] = -au[ispec] / data.DBinary[ispec, jspec] + end + end + end + + ## Here, we also could use @gc_preserve. + ## As this function is inlined one can avoid StrideArrays.jl + ## Starting with Julia 1.8 one also can use callsite @inline. + inplace_linsolve!(M, du) + + for ispec = 1:nspec(data) + f[ispec] = du[ispec] + end +end + +function bcondition(f, u, node, data) + for species = 1:nspec(data) + boundary_dirichlet!(f, u, node; species, region = data.diribc[1], + value = species % 2) + boundary_dirichlet!(f, u, node; species, region = data.diribc[2], + value = 1 - species % 2) + end +end + +function main(; n = 11, nspec = 5, + dim = 2, + Plotter = nothing, + verbose = false, + unknown_storage = :dense, + flux = :flux_strided, + strategy = nothing, + assembly = :cellwise) + h = 1.0 / convert(Float64, n - 1) + X = collect(0.0:h:1.0) + DBinary = Symmetric(fill(0.1, nspec, nspec)) + for ispec = 1:nspec + DBinary[ispec, ispec] = 0 + end + + DKnudsen = fill(1.0, nspec) + + if dim == 1 + grid = VoronoiFVM.Grid(X) + diribc = [1, 2] + elseif dim == 2 + grid = VoronoiFVM.Grid(X, X) + diribc = [4, 2] + else + grid = VoronoiFVM.Grid(X, X, X) + diribc = [4, 2] + end + + function storage(f, u, node, data) + f .= u + end + + _flux = flux == :flux_strided ? flux_strided : flux_marray + + data = MyData{nspec}(DBinary, DKnudsen, diribc) + sys = VoronoiFVM.System(grid; flux = _flux, storage, bcondition, species = 1:nspec, data, assembly,unknown_storage) + + @info "Strategy: $(strategy)" + control = SolverControl(strategy, sys) + control.maxiters = 500 + @info control.method_linear + u = solve(sys; verbose, control, log = true) + @show norm(u) + norm(u) +end + +using Test +function runtests() + # Legacy strategy list (only in 1.5) + strat1 = [direct_umfpack(), gmres_umfpack(), gmres_eqnblock_umfpack(), + gmres_iluzero(), + # gmres_eqnblock_iluzero(), + # gmres_pointblock_iluzero() + ] + + # Equivalent up-to-date list + strat2 = [DirectSolver(UMFPACKFactorization()), + GMRESIteration(UMFPACKFactorization()), + GMRESIteration(UMFPACKFactorization(), EquationBlock()), + GMRESIteration(AMGCL_AMGPreconditioner(),EquationBlock()), + BICGstabIteration(AMGCL_AMGPreconditioner(),EquationBlock()), + GMRESIteration(ILUZeroPreconditioner()), + # GMRESIteration(ILUZeroPreconditioner(), EquationBlock()), + # GMRESIteration(ILUZeroPreconditioner(), PointBlock()) + ] + + val1D = 4.788926530387466 + val2D = 15.883072449873742 + val3D = 52.67819183426213 + + res1 = main(; dim = 1, assembly = :edgewise) ≈ val1D && + main(; dim = 2, assembly = :edgewise) ≈ val2D && + main(; dim = 3, assembly = :edgewise) ≈ val3D && + main(; dim = 1, flux = :flux_marray, assembly = :edgewise) ≈ val1D && + main(; dim = 2, flux = :flux_marray, assembly = :edgewise) ≈ val2D && + main(; dim = 3, flux = :flux_marray, assembly = :edgewise) ≈ val3D && + all(map(strategy -> main(; dim = 2, flux = :flux_marray, strategy) ≈ val2D, strat1)) + + res2 = main(; dim = 1, assembly = :cellwise) ≈ val1D && + main(; dim = 2, assembly = :cellwise) ≈ val2D && + main(; dim = 3, assembly = :cellwise) ≈ val3D && + main(; dim = 1, flux = :flux_marray, assembly = :cellwise) ≈ val1D && + main(; dim = 2, flux = :flux_marray, assembly = :cellwise) ≈ val2D && + main(; dim = 3, flux = :flux_marray, assembly = :cellwise) ≈ val3D && + all(map(strategy -> main(; dim = 2, flux = :flux_marray, strategy) ≈ val2D, strat2)) + @show res1, res2 + + @test res1 && res2 +end +end diff --git a/v1.19.1/module_examples/Example510_Mixture/index.html b/v1.19.1/module_examples/Example510_Mixture/index.html new file mode 100644 index 000000000..e3152b6a8 --- /dev/null +++ b/v1.19.1/module_examples/Example510_Mixture/index.html @@ -0,0 +1,196 @@ + +510: Mixture · VoronoiFVM.jl

    510: Mixture

    (source code)

    Test mixture diffusion flux. The problem is here that in the flux function we need to solve a linear system of equations which calculates the fluxes from the gradients.# To do so without (heap) allocations can be achieved using StrideArrays, together with the possibility to have static (compile time) information about the size of the local arrays to be allocated.

    $u_i$ are the species partial pressures, $\vec N_i$ are the species fluxes. $D_i^K$ are the Knudsen diffusion coefficients, and $D^B_{ij}$ are the binary diffusion coefficients.

    \[ -\nabla \cdot \vec N_i =0 \quad (i=1\dots n)\\ + \frac{\vec N_i}{D^K_i} + \sum_{j\neq i}\frac{u_j \vec N_i - u_i \vec N_j}{D^B_{ij}} = -\vec \nabla u_i \quad (i=1\dots n)\]

    From this representation, we can derive the matrix $M=(m_{ij})$ with

    \[m_{ii}= \frac{1}{D^K_i} + \sum_{j\neq i} \frac{u_j}{D_ij}\\ +m_{ij}= -\sum_{j\neq i} \frac{u_i}{D_ij}\]

    such that

    \[ M\begin{pmatrix} +\vec N_1\\ +\vdots\\ +\vec N_n +\end{pmatrix} += +\begin{pmatrix} +\vec \nabla u_1\\ +\vdots\\ +\vec \nabla u_n +\end{pmatrix}\]

    In the two point flux finite volume discretization, this results into a corresponding linear system which calculates the discrete edge fluxes from the discrete gradients. Here we demonstrate how to implement this in a fast, (heap) allocation free way.

    For this purpose, intermediate arrays need to be allocated on the stack with via StrideArrays.jl or MArray from StaticArrays.jl They need to have the same element type as the unknowns passed to the flux function (which could be Float64 or some dual number). Size information must be static, e.g. a global constant, or, as demonstrated here, a type parameter. Broadcasting probably should be avoided.

    As documented in StrideArrays.jl, use @gc_preserve when passing a StrideArray as a function parameter.

    Alternatively, we can try to avoid StrideArrays.jl and use MArrays together with inlined code. In this case, one should be aware of this issue, which requires @inbounds e.g. with reverse order loops.

    See also this Discourse thread.

    module Example510_Mixture
    +
    +using Printf
    +using VoronoiFVM
    +using VoronoiFVM.SolverStrategies
    +
    +using ExtendableGrids
    +using GridVisualize
    +using LinearAlgebra
    +using AMGCLWrap
    +using Random
    +using StrideArraysCore: @gc_preserve, StrideArray, StaticInt, PtrArray
    +using LinearSolve, ExtendableSparse
    +using StaticArrays
    +using ExtendableSparse
    +
    +struct MyData{NSPec}
    +    DBinary::Symmetric{Float64, Matrix{Float64}}
    +    DKnudsen::Vector{Float64}
    +    diribc::Vector{Int}
    +end
    +
    +nspec(::MyData{NSpec}) where {NSpec} = NSpec
    +
    +function flux_strided(f, u, edge, data)
    +    T = eltype(u)
    +    M = StrideArray{T}(undef, StaticInt(nspec(data)), StaticInt(nspec(data)))
    +    au = StrideArray{T}(undef, StaticInt(nspec(data)))
    +    du = StrideArray{T}(undef, StaticInt(nspec(data)))
    +    ipiv = StrideArray{Int}(undef, StaticInt(nspec(data)))
    +
    +    for ispec = 1:nspec(data)
    +        M[ispec, ispec] = 1.0 / data.DKnudsen[ispec]
    +        du[ispec] = u[ispec, 1] - u[ispec, 2]
    +        au[ispec] = 0.5 * (u[ispec, 1] + u[ispec, 2])
    +    end
    +
    +    for ispec = 1:nspec(data)
    +        for jspec = 1:nspec(data)
    +            if ispec != jspec
    +                M[ispec, ispec] += au[jspec] / data.DBinary[ispec, jspec]
    +                M[ispec, jspec] = -au[ispec] / data.DBinary[ispec, jspec]
    +            end
    +        end
    +    end
    +
    +    if VERSION >= v"1.9-rc0"
    +        # Pivoting linear system solution via RecursiveFactorizations.jl
    +        @gc_preserve inplace_linsolve!(M, du, ipiv)
    +    else
    +        # Non-pivoting implementation currently implemented in vfvm_functions.jl
    +        @gc_preserve inplace_linsolve!(M, du)
    +    end
    +
    +    for ispec = 1:nspec(data)
    +        f[ispec] = du[ispec]
    +    end
    +end
    +
    +function flux_marray(f, u, edge, data)
    +    T = eltype(u)
    +    n = nspec(data)
    +
    +    M = MMatrix{nspec(data), nspec(data), T}(undef)
    +    au = MVector{nspec(data), T}(undef)
    +    du = MVector{nspec(data), T}(undef)
    +    ipiv = MVector{nspec(data), Int}(undef)
    +
    +    for ispec = 1:nspec(data)
    +        M[ispec, ispec] = 1.0 / data.DKnudsen[ispec]
    +        du[ispec] = u[ispec, 1] - u[ispec, 2]
    +        au[ispec] = 0.5 * (u[ispec, 1] + u[ispec, 2])
    +    end
    +
    +    for ispec = 1:nspec(data)
    +        for jspec = 1:nspec(data)
    +            if ispec != jspec
    +                M[ispec, ispec] += au[jspec] / data.DBinary[ispec, jspec]
    +                M[ispec, jspec] = -au[ispec] / data.DBinary[ispec, jspec]
    +            end
    +        end
    +    end
    +
    +    # Here, we also could use @gc_preserve.
    +    # As this function is inlined one can avoid StrideArrays.jl
    +    # Starting with Julia 1.8 one also can use callsite @inline.
    +    inplace_linsolve!(M, du)
    +
    +    for ispec = 1:nspec(data)
    +        f[ispec] = du[ispec]
    +    end
    +end
    +
    +function bcondition(f, u, node, data)
    +    for species = 1:nspec(data)
    +        boundary_dirichlet!(f, u, node; species, region = data.diribc[1],
    +                            value = species % 2)
    +        boundary_dirichlet!(f, u, node; species, region = data.diribc[2],
    +                            value = 1 - species % 2)
    +    end
    +end
    +
    +function main(; n = 11, nspec = 5,
    +              dim = 2,
    +              Plotter = nothing,
    +              verbose = false,
    +              unknown_storage = :dense,
    +              flux = :flux_strided,
    +              strategy = nothing,
    +              assembly = :cellwise)
    +    h = 1.0 / convert(Float64, n - 1)
    +    X = collect(0.0:h:1.0)
    +    DBinary = Symmetric(fill(0.1, nspec, nspec))
    +    for ispec = 1:nspec
    +        DBinary[ispec, ispec] = 0
    +    end
    +
    +    DKnudsen = fill(1.0, nspec)
    +
    +    if dim == 1
    +        grid = VoronoiFVM.Grid(X)
    +        diribc = [1, 2]
    +    elseif dim == 2
    +        grid = VoronoiFVM.Grid(X, X)
    +        diribc = [4, 2]
    +    else
    +        grid = VoronoiFVM.Grid(X, X, X)
    +        diribc = [4, 2]
    +    end
    +
    +    function storage(f, u, node, data)
    +        f .= u
    +    end
    +
    +    _flux = flux == :flux_strided ? flux_strided : flux_marray
    +
    +    data = MyData{nspec}(DBinary, DKnudsen, diribc)
    +    sys = VoronoiFVM.System(grid; flux = _flux, storage, bcondition, species = 1:nspec, data, assembly,unknown_storage)
    +
    +    @info "Strategy: $(strategy)"
    +    control = SolverControl(strategy, sys)
    +    control.maxiters = 500
    +    @info control.method_linear
    +    u = solve(sys; verbose, control, log = true)
    +    @show norm(u)
    +    norm(u)
    +end
    +
    +using Test
    +function runtests()

    Legacy strategy list (only in 1.5)

        strat1 = [direct_umfpack(), gmres_umfpack(), gmres_eqnblock_umfpack(),
    +        gmres_iluzero(),
           gmres_eqnblock_iluzero(),
    +       gmres_pointblock_iluzero()
        ]

    Equivalent up-to-date list

        strat2 = [DirectSolver(UMFPACKFactorization()),
    +        GMRESIteration(UMFPACKFactorization()),
    +        GMRESIteration(UMFPACKFactorization(), EquationBlock()),
    +        GMRESIteration(AMGCL_AMGPreconditioner(),EquationBlock()),
    +        BICGstabIteration(AMGCL_AMGPreconditioner(),EquationBlock()),
    +        GMRESIteration(ILUZeroPreconditioner()),
           GMRESIteration(ILUZeroPreconditioner(), EquationBlock()),
    +       GMRESIteration(ILUZeroPreconditioner(), PointBlock())
        ]
    +
    +    val1D = 4.788926530387466
    +    val2D = 15.883072449873742
    +    val3D = 52.67819183426213
    +
    +    res1 = main(; dim = 1, assembly = :edgewise) ≈ val1D &&
    +           main(; dim = 2, assembly = :edgewise) ≈ val2D &&
    +           main(; dim = 3, assembly = :edgewise) ≈ val3D &&
    +           main(; dim = 1, flux = :flux_marray, assembly = :edgewise) ≈ val1D &&
    +           main(; dim = 2, flux = :flux_marray, assembly = :edgewise) ≈ val2D &&
    +           main(; dim = 3, flux = :flux_marray, assembly = :edgewise) ≈ val3D &&
    +           all(map(strategy -> main(; dim = 2, flux = :flux_marray, strategy) ≈ val2D, strat1))
    +
    +    res2 = main(; dim = 1, assembly = :cellwise) ≈ val1D &&
    +           main(; dim = 2, assembly = :cellwise) ≈ val2D &&
    +           main(; dim = 3, assembly = :cellwise) ≈ val3D &&
    +           main(; dim = 1, flux = :flux_marray, assembly = :cellwise) ≈ val1D &&
    +           main(; dim = 2, flux = :flux_marray, assembly = :cellwise) ≈ val2D &&
    +           main(; dim = 3, flux = :flux_marray, assembly = :cellwise) ≈ val3D &&
    +           all(map(strategy -> main(; dim = 2, flux = :flux_marray, strategy) ≈ val2D, strat2))
    +    @show res1, res2
    +
    +    @test res1 && res2
    +end
    +end

    This page was generated using Literate.jl.

    diff --git a/v1.19.1/notebooks/index.html b/v1.19.1/notebooks/index.html new file mode 100644 index 000000000..81b981550 --- /dev/null +++ b/v1.19.1/notebooks/index.html @@ -0,0 +1,2 @@ + +About the notebooks · VoronoiFVM.jl

    About the notebooks

    Pluto.jl notebooks provide a great opportunity to put together code, text and graphics in a reproducible and accessible way. Therefore, the examples for this package are being amended by a series of Pluto notebooks. Like the example code, the notebook code is tested during CI.

    diff --git a/v1.19.1/objects.inv b/v1.19.1/objects.inv new file mode 100644 index 0000000000000000000000000000000000000000..a056b5de48094ece9f54772a1171e8d3c25e2a62 GIT binary patch literal 9188 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 literal 0 HcmV?d00001 diff --git a/v1.19.1/physics/index.html b/v1.19.1/physics/index.html new file mode 100644 index 000000000..3f798de5b --- /dev/null +++ b/v1.19.1/physics/index.html @@ -0,0 +1,20 @@ + +Physics & special functions · VoronoiFVM.jl

    Physics & special functions

    Physics

    VoronoiFVM.PhysicsType
    struct Physics

    Physics data record with the following fields:

    • flux::Function: Flux between neighboring control volumes: flux(f,u,edge) or flux(f,u,edge,data) should return in f[i] the flux of species i along the edge joining circumcenters of neighboring control volumes. u is a 2D array such that for species i, u[i,1] and u[i,2] contain the unknown values at the corresponding ends of the edge.
    • storage::Function: Storage term (term under time derivative): storage(f,u,node) or storage(f,u,node,data)

      It should return in f[i] the storage term for the i-th equation. u[i] contains the value of the i-th unknown.

    • reaction::Function: Reaction term: reaction(f,u,node) or reaction(f,u,node,data)

      It should return in f[i] the reaction term for the i-th equation. u[i] contains the value of the i-th unknown.

    • edgereaction::Function: Edge reaction term: edgereaction(f,u,edge) or edgereaction(f,u,edge,data)

      It should return in f[i] the reaction term for the i-th equation. u[i] contains the value of the i-th unknown. u is a 2D array such that for species i, u[i,1] and u[i,2] contain the unknown values at the corresponding ends of the edge.

    • source::Function: Source term: source(f,node) or source(f,node,data).

      It should return the in f[i] the value of the source term for the i-th equation.

    • bflux::Function: Flux between neighboring control volumes on the boundary. Called on edges fully adjacent to the boundary: bflux(f,u,bedge) or `bflux(f,u,bedge,data)
    • breaction::Function: Boundary reaction term: breaction(f,u,node) or breaction(f,u,node,data) Similar to reaction, but restricted to the inner or outer boundaries.
    • bsource::Function: Boundary source term: bsource(f,node) or bsource(f,node,data).

      It should return in f[i] the value of the source term for the i-th equation.

    • bstorage::Function: Boundary storage term: bstorage(f,u,node) or bstorage(f,u,node,data) Similar to storage, but restricted to the inner or outer boundaries.
    • boutflow::Function: Outflow boundary term boutflow(f,u,edge) or boutflow(f,u,edge,data) This function is called for edges (including interior ones) which have at least one ode on one of the outflow boundaries. Within this function, outflownode and isoutflownode can be used to identify that node. There is some ambiguity in the case that both nodes are outflow nodes, in that case it is assumed that the contribution is zero.
    • outflowboundaries::Vector{Int64}: List (Vector) of boundary regions which carry outflow bondary conditions. Influences when boutflow is called.
    • generic_operator::Function: Generic operator generic_operator(f,u,sys). This operator acts on the full solution u of a system. Sparsity is detected automatically unless generic_operator_sparsity is given.
    • generic_operator_sparsity::Function: Function defining the sparsity structure of the generic operator. This should return the sparsity pattern of the generic_operator.
    • data::Any: User data (parameters). This allows to pass various parameters to the callback functions.
    • num_species::Int8: Number of species including boundary species.
    source

    Edge and node data

    VoronoiFVM.NodeType
    mutable struct Node{Tc, Tp, Ti} <: VoronoiFVM.AbstractNode{Tc, Tp, Ti}

    Structure holding local node information.

    • index::Any: Index in grid
    • region::Any: Inner region number
    • nspec::Any: Number of species defined in node
    • icell::Any: Number of discretization cell the node is invoked from
    • coord::Matrix: Grid coordinates
    • cellnodes::Matrix: Grid cell nodes
    • cellregions::Vector: Grid cell regions
    • time::Float64: System time
    • embedparam::Float64: Current value of embedding parameter
    • params::Vector: parameters
    • fac::Float64: Form factor
    • _idx::Any: Local loop index
    source
    VoronoiFVM.BNodeType
    mutable struct BNode{Tv, Tc, Tp, Ti} <: VoronoiFVM.AbstractNode{Tc, Tp, Ti}

    Structure holding local boundary node information.

    • index::Any: Index in grid
    • ibface::Any: BFace number it is called from
    • ibnode::Any: local node number
    • region::Any: Boundary region number
    • cellregions::Vector

    • nspec::Any: Number of species defined in node

    • coord::Matrix: Grid coordinates
    • bfacenodes::Matrix

    • bfaceregions::Vector

    • allcellregions::Vector

    • bfacecells::Adjacency

    • Dirichlet::Any

    • time::Float64: System time

    • embedparam::Float64: Current value of embedding parameter
    • params::Vector

    • dirichlet_value::Vector

    • fac::Float64

    source
    VoronoiFVM.EdgeType
    mutable struct Edge{Tc, Tp, Ti} <: VoronoiFVM.AbstractEdge{Tc, Tp, Ti}

    Structure holding local edge information.

    • index::Any: Index in grid
    • node::Vector: Index
    • region::Any: Inner region number corresponding to edge
    • nspec::Any: Number of species defined in edge
    • icell::Any: Number of discretization cell the edge is invoked from
    • coord::Matrix: Grid coordinates
    • cellx::Matrix

    • edgenodes::Matrix

    • cellregions::Vector

    • has_celledges::Bool

    • time::Float64: System time

    • embedparam::Float64: Current value of embedding parameter
    • params::Vector

    • fac::Float64: Form factor

    • _idx::Any: Local loop index
    • outflownoderegions::Union{Nothing, SparseArrays.SparseMatrixCSC{Bool, Int64}}

    • outflownode::Int64: Outflow node

    source
    VoronoiFVM.BEdgeType
    mutable struct BEdge{Tc, Tp, Ti} <: VoronoiFVM.AbstractEdge{Tc, Tp, Ti}

    Structure holding local edge information.

    • index::Any: Index in grid
    • node::Vector: Index
    • region::Any: Inner region number corresponding to edge
    • nspec::Any: Number of species defined in edge
    • icell::Any: Number of discretization cell the edge is invoked from
    • coord::Matrix: Grid coordinates
    • bedgenodes::Matrix

    • bfaceedges::Matrix

    • bfaceregions::Vector

    • time::Float64: System time

    • embedparam::Float64: Current value of embedding parameter
    • params::Vector

    • fac::Float64

    source

    Special functions

    VoronoiFVM.fbernoulliFunction
    fbernoulli(x)
    +

    Bernoulli function $B(x)=\frac{x}{e^x-1}$ for exponentially fitted upwinding.

    The name fbernoulli has been chosen to avoid confusion with Bernoulli from JuliaStats/Distributions.jl

    Returns a real number containing the result.

    source
    VoronoiFVM.fbernoulli_pmFunction
    fbernoulli_pm(x)
    +

    Bernoulli function $B(x)=\frac{x}{e^x-1}$ for exponentially fitted upwind, joint evaluation for positive and negative argument

    Usually, we need $B(x), B(-x)$ together, and it is cheaper to calculate them together.

    Returns two real numbers containing the result for argument x and argument -x.

    The error in comparison with the evaluation of the original expression with BigFloat is less than 1.0e-15

    source
    VoronoiFVM.inplace_linsolve!Method
    inplace_linsolve!(A, b)
    +

    Non-allocating, non-pivoting inplace solution of square linear system of equations A*x=b using Doolittle's method.

    After solution, A will contain the LU factorization, and b the result.

    A pivoting version is available with Julia v1.9.

    source
    VoronoiFVM.inplace_linsolve!Method
    inplace_linsolve!(A, b, ipiv)
    +

    Non-allocating, pivoting, inplace solution of square linear system of equations A*x=b using LU factorization from RecursiveFactorizations.jl.

    After solution, A will contain the LU factorization, and b the result. ipiv must be an Int64 vector of the same length as b.

    Info

    Needs Julia v1.9 or later

    source
    diff --git a/v1.19.1/plutostatichtml_examples/api-update/index.html b/v1.19.1/plutostatichtml_examples/api-update/index.html new file mode 100644 index 000000000..ecfd40d3d --- /dev/null +++ b/v1.19.1/plutostatichtml_examples/api-update/index.html @@ -0,0 +1,454 @@ + +API Updates · VoronoiFVM.jl
    + + + + +

    API updates

    Source

    + + +

    Here we describe some updates for the API of VoronoiFVM.jl. These have been implemented mostly on top of the existing API, whose functionality is not affected.

    + +
    TableOfContents(; aside = false, depth = 5)
    + + +
    begin
    +    import Pkg as _Pkg
    +    haskey(ENV, "PLUTO_PROJECT") && _Pkg.activate(ENV["PLUTO_PROJECT"])
    +    using Revise
    +    using VoronoiFVM
    +    using ExtendableGrids
    +    using ExtendableSparse
    +    using Test
    +    using PlutoUI
    +    using GridVisualize
    +    using LinearSolve
    +    using ILUZero
    +    using LinearAlgebra
    +    using CairoMakie
    +    CairoMakie.activate!(; type = "svg", visible = false)
    +    GridVisualize.default_plotter!(CairoMakie)
    +end;
    + + +

    v0.19

    +
    + + +

    This is a breaking release. Implementations using default solver settings should continue to work (albeit possibly with deprecation and allocation warnings). Really breaking is control of iterative linear solvers and allocation checks.

    + + +

    Solve now a method of CommonSolve.solve

    + + +

    As a consequence, all VoronoiFVM.solve methods with signatures others than solve(system; kwargs...) are now deprecated

    + +
    n = 100
    +
    100
    + +
    begin
    +    h = 1.0 / convert(Float64, n)
    +    const eps = 1.0e-2
    +    function reaction(f, u, node)
    +        f[1] = u[1]^2
    +    end
    +
    +    function flux(f, u, edge)
    +        f[1] = eps * (u[1, 1]^2 - u[1, 2]^2)
    +    end
    +
    +    function source(f, node)
    +        x1 = node[1] - 0.5
    +        x2 = node[2] - 0.5
    +        f[1] = exp(-20.0 * (x1^2 + x2^2))
    +    end
    +
    +    function storage(f, u, node)
    +        f[1] = u[1]
    +    end
    +
    +    function bcondition(f, u, node)
    +        boundary_dirichlet!(f,
    +                            u,
    +                            node;
    +                            species = 1,
    +                            region = 2,
    +                            value = ramp(node.time; dt = (0, 0.1), du = (0, 1)))
    +        boundary_dirichlet!(f,
    +                            u,
    +                            node;
    +                            species = 1,
    +                            region = 4,
    +                            value = ramp(node.time; dt = (0, 0.1), du = (0, 1)))
    +    end
    +
    +    sys0 = VoronoiFVM.System(0.0:h:1.0,
    +                             0.0:h:1.0;
    +                             reaction,
    +                             flux,
    +                             source,
    +                             storage,
    +                             bcondition,
    +                             species = [1],)
    +end
    +
    VoronoiFVM.System{Float64, Float64, Int32, Int64, Matrix{Int32}, Matrix{Float64}}(num_species=1)
    +
    + + +

    Deprecated call:

    + +
    begin
    +    inival = unknowns(sys0; inival = 0.1)
    +    sol00 = unknowns(sys0)
    +    solve!(sol00, inival, sys0)
    +end
    +
    1×10201 Matrix{Float64}:
    + 1.0  0.951791  0.906016  0.862563  0.821331  …  0.862563  0.906016  0.951791  1.0
    + + +

    Replace this by:

    + +
    sol0 = solve(sys0; inival = 0.1)
    +
    1×10201 Matrix{Float64}:
    + 1.02241e-33  0.0319717  0.045243  0.0554731  …  0.045243  0.0319717  1.02241e-33
    + + +

    Docstring of solve

    + + +
    solve(system; kwargs...)

    Built-in solution method for VoronoiFVM.System.

    Keyword arguments:

    • General for all solvers

      • inival (default: 0) : Array created via unknowns or number giving the initial value.

      • control (default: nothing): Pass instance of SolverControl

      • All elements of SolverControl can be used as kwargs. Eventually overwrites values given via control

      • params: Parameters (Parameter handling is experimental and may change)

    • Stationary solver: Invoked if neither times nor embed, nor tstep are given as keyword argument.

      • time (default: 0.0): Set time value.

      Returns a DenseSolutionArray or SparseSolutionArray

    • Embedding (homotopy) solver: Invoked if embed kwarg is given. Use homotopy embedding + damped Newton's method to solve stationary problem or to solve series of parameter dependent problems. Parameter step control is performed according to solver control data. kwargs and default values are:

      • embed (default: nothing ): vector of parameter values to be reached exactly

      In addition, all kwargs of the implicit Euler solver (besides times) are handled. Returns a transient solution object sol containing the stored solution(s), see TransientSolution.

    • Implicit Euler transient solver: Invoked if times kwarg is given. Use implicit Euler method + damped Newton's method to solve time dependent problem. Time step control is performed according to solver control data. kwargs and default values are:

      • times (default: nothing ): vector of time values to be reached exactly

      • pre (default: (sol,t)->nothing ): callback invoked before each time step

      • post (default: (sol,oldsol, t, Δt)->nothing ): callback invoked after each time step

      • sample (default: (sol,t)->nothing ): callback invoked after timestep for all times in times[2:end].

      • delta (default: (system, u,v,t, Δt)->norm(sys,u-v,Inf) ): Value used to control the time step size Δu

      If control.handle_error is true, if time step solution throws an error, stepsize is lowered, and step solution is called again with a smaller time value. If control.Δt<control.Δt_min, solution is aborted with error. Returns a transient solution object sol containing the stored solution, see TransientSolution.

    • Implicit Euler timestep solver. Invoked if tstep kwarg is given. Solve one time step of the implicit Euler method.

      • time (default: 0): Set time value.

      • tstep: time step

      Returns a DenseSolutionArray or SparseSolutionArray

    + + +

    Docstring of SolverControl

    + + +
    SolverControl
    +SolverControl(;kwargs...)
    +SolverControl(linear_solver_strategy, sys; kwargs...)

    Solver control parameter for time stepping, embedding, Newton method and linear solver control. All field names can be used as keyword arguments for solve(system::VoronoiFVM.AbstractSystem; kwargs...)

    Newton's method solves $F(u)=0$ by the iterative procedure $u_{i+1}=u_{i} - d_i F'(u_i)^{-1}F(u_i)$ starting with some initial value $u_0$, where $d_i$ is a damping parameter.

    For linear solver strategies, see VoronoiFVM.LinearSolverStrategy.

    • verbose::Union{Bool, String}: Verbosity control. A collection of output categories is given in a string composed of the following letters:

      • a: allocation warnings

      • d: deprecation warnings

      • e: time/parameter evolution log

      • n: newton solver log

      • l: linear solver log

      Alternatively, a Bool value can be given, resulting in

      • true: "neda"

      • false: "da"

      Switch off all output including deprecation warnings via verbose="". In the output, corresponding messages are marked e.g. via '[n]', [a] etc. (besides of '[l]')

    • abstol::Float64: Tolerance (in terms of norm of Newton update): terminate if $\Delta u_i=||u_{i+1}-u_i||_\infty <$abstol.

    • reltol::Float64: Tolerance (relative to the size of the first update): terminate if $\Delta u_i/\Delta u_1<$reltol.

    • maxiters::Int64: Maximum number of newton iterations.

    • tol_round::Float64: Tolerance for roundoff error detection: terminate if $|\;||u_{i+1}||_1 - ||u_{i}||_1\;|/ ||u_{i}||_1<$tol_round occurred max_round times in a row.

    • tol_mono::Float64: Tolerance for monotonicity test: terminate with error if $\Delta u_i/\Delta u_{i-1}>$1/tol_mono.

    • damp_initial::Float64: Initial damping parameter $d_0$. To handle convergence problems, set this to a value less than 1.

    • damp_growth::Float64: Damping parameter growth factor: $d_{i+1}=\min(d_i\cdot$max_growth$,1)$. It should be larger than 1.

    • max_round::Int64: Maximum number of consecutive iterations within roundoff error tolerance The default effectively disables this criterion.

    • unorm::Function: Calculation of Newton update norm

    • rnorm::Function: Functional for roundoff error calculation

    • method_linear::Union{Nothing, LinearSolve.SciMLLinearSolveAlgorithm}: Solver method for linear systems (see LinearSolve.jl). If given nothing, as default are chosen (for Float64 calculations):

      • 1D: KLUFactorization()

      • 2D: SparspakFactorization()

      • 3D: UMFPACKFactorization()

      SparspakFactorization() is the default choice for general number types. Users should experiment with what works best for their problem.

      For easy access to this functionality, see see also VoronoiFVM.LinearSolverStrategy.

    • reltol_linear::Float64: Relative tolerance of iterative linear solver.

    • abstol_linear::Float64: Absolute tolerance of iterative linear solver.

    • maxiters_linear::Int64: Maximum number of iterations of linear solver

    • precon_linear::Union{Nothing, Function, ExtendableSparse.AbstractFactorization, LinearSolve.SciMLLinearSolveAlgorithm, Type}: Constructor for preconditioner for linear systems. This should work as a function precon_linear(A) which takes an AbstractSparseMatrixCSC (e.g. an ExtendableSparseMatrix) and returns a preconditioner object in the sense of LinearSolve.jl, i.e. which has an ldiv!(u,A,v) method. Useful examples:

      • ExtendableSparse.ILUZero

      • ExtendableSparse.Jacobi

      For easy access to this functionality, see see also VoronoiFVM.LinearSolverStrategy.

    • keepcurrent_linear::Bool: Update preconditioner in each Newton step ?

    • Δp::Float64: Initial parameter step for embedding.

    • Δp_max::Float64: Maximal parameter step size.

    • Δp_min::Float64: Minimal parameter step size.

    • Δp_grow::Float64: Maximal parameter step size growth.

    • Δp_decrease::Float64: Parameter step decrease factor upon rejection

    • Δt::Float64: Initial time step size.

    • Δt_max::Float64: Maximal time step size.

    • Δt_min::Float64: Minimal time step size.

    • Δt_grow::Float64: Maximal time step size growth.

    • Δt_decrease::Float64: Time step decrease factor upon rejection

    • Δu_opt::Float64: Optimal size of update for time stepping and embedding. The algorithm tries to keep the difference in norm between "old" and "new" solutions approximately at this value.

    • Δu_max_factor::Float64: Control maximum sice of update Δu for time stepping and embeding relative to Δu_opt. Time steps with Δu > Δu_max_factor*Δu_opt will be rejected.

    • force_first_step::Bool: Force first timestep.

    • num_final_steps::Int64: Number of final steps to adjust at end of time interval in order to prevent breakdown of step size.

    • handle_exceptions::Bool: Handle exceptions during transient solver and parameter embedding. If true, exceptions in Newton solves are caught, the embedding resp. time step is lowered, and solution is retried. Moreover, if embedding or time stepping fails (e.g. due to reaching minimal step size), a warning is issued, and a solution is returned with all steps calculated so far.

      Otherwise (by default) errors are thrown.

    • store_all::Bool: Store all steps of transient/embedding problem:

    • in_memory::Bool: Store transient/embedding solution in memory

    • log::Any: Record history

    • edge_cutoff::Float64: Edge parameter cutoff for rectangular triangles.

    • pre::Function: Function pre(sol,t) called before time/embedding step

    • post::Function: Function post(sol,oldsol,t,Δt) called after successful time/embedding step

    • sample::Function: Function sample(sol,t) to be called for each t in times[2:end]

    • delta::Function: Time step error estimator. A function Δu=delta(system,u,uold,t,Δt) to calculate Δu.

    • tol_absolute::Union{Nothing, Float64}

    • tol_relative::Union{Nothing, Float64}

    • damp::Union{Nothing, Float64}

    • damp_grow::Union{Nothing, Float64}

    • max_iterations::Union{Nothing, Int64}

    • tol_linear::Union{Nothing, Float64}

    • max_lureuse::Union{Nothing, Int64}

    • mynorm::Union{Nothing, Function}

    • myrnorm::Union{Nothing, Function}

    + + +

    Rely on LinearSolve.jl for linear system solution

    + + +

    This provides easy access to a large variety of linear solvers:

    + + +

    LU factorization from UMFPACK

    + +
    umf_sol = solve(sys0; inival = 0.1, method_linear = UMFPACKFactorization(), verbose = true)
    +
    1×10201 Matrix{Float64}:
    + 1.02241e-33  0.0319717  0.045243  0.0554731  …  0.045243  0.0319717  1.02241e-33
    + +
    @test isapprox(umf_sol, sol0, atol = 1.0e-7)
    +
    Test Passed
    + + +

    LU factorization from Sparspak.jl

    + +
    sppk_sol = solve(sys0; inival = 0.1, method_linear = SparspakFactorization(), verbose = true)
    +
    1×10201 Matrix{Float64}:
    + 1.02241e-33  0.0319717  0.045243  0.0554731  …  0.045243  0.0319717  1.02241e-33
    + +
    @test isapprox(sppk_sol, sol0, atol = 1.0e-7)
    +
    Test Passed
    + + +

    Iterative solvers

    + + +
    BICGstab from Krylov.jl with diagonal (Jacobi) preconditioner

    The Jacobi preconditioner is defined in ExtendableSparse.jl.

    + +
    krydiag_sol = solve(sys0;
    +                    inival = 0.1,
    +                    method_linear = KrylovJL_BICGSTAB(),
    +                    precon_linear = JacobiPreconditioner,
    +                    verbose = true,)
    +
    1×10201 Matrix{Float64}:
    + 1.02241e-33  0.0319717  0.045243  0.0554731  …  0.045243  0.0319717  1.02241e-33
    + +
    @test isapprox(krydiag_sol, sol0, atol = 1.0e-5)
    +
    Test Passed
    + + +
    BICGstab from Krylov.jl with delayed factorization preconditioner
    + +
    krydel_sol = solve(sys0;
    +                   inival = 0.1,
    +                   method_linear = KrylovJL_BICGSTAB(),
    +                   precon_linear = SparspakFactorization(),
    +                   verbose = "nlad",)
    +
    1×10201 Matrix{Float64}:
    + 1.02241e-33  0.0319717  0.045243  0.0554731  …  0.045243  0.0319717  1.02241e-33
    + +
    @test isapprox(krydel_sol, sol0, atol = 1.0e-5)
    +
    Test Passed
    + + +
    BICGstab from Krylov.jl with ilu0 preconditioner

    ILUZeroPreconditioner is exported from ExtendableSparse and wraps the predonditioner defined in ILUZero.jl .

    + +
    kryilu0_sol = solve(sys0;
    +                    inival = 0.5,
    +                    method_linear = KrylovJL_BICGSTAB(),
    +                    precon_linear = ILUZeroPreconditioner,
    +                    verbose = true,)
    +
    1×10201 Matrix{Float64}:
    + 1.05391e-33  0.0319717  0.045243  0.0554731  …  0.045243  0.0319717  1.05573e-33
    + +
    @test isapprox(kryilu0_sol, sol0, atol = 1.0e-5)
    +
    Test Passed
    + + +

    New verbosity handling

    + + +
    • verbose can now be a Bool or a String of flag characters, allowing for control of different output categories. I would love to do this via logging, but there is still a long way to go IMHO

    • Allocation check is active by default with warnings which can be muted by passing a verbose string without 'a'. This is now the only control in this respect. All check_allocs methods/kwargs, control via environment variables have been removed.

    • Deprecation warnings can be switched off by passing a verbose string without 'd'.

    • Improve iteration logging etc., allow for logging of linear iterations ('l' flag character)

    + + +

    The following example gives some information in this respect:

    + +
    D = 0.1
    +
    0.1
    + +
    function xflux(f, u, edge)
    +    f[1] = D * (u[1, 1]^2 - u[1, 2]^2)
    +end
    +
    xflux (generic function with 1 method)
    + +
    xsys = VoronoiFVM.System(0:0.001:1; flux = xflux, species = [1])
    +
    VoronoiFVM.System{Float64, Float64, Int32, Int64, Matrix{Int32}, Matrix{Float64}}(num_species=1)
    +
    + +
    solve(xsys; inival = 0.1, times = [0, 1]);
    + + + +

    If we find these warnings annoying, we can switch them off:

    + +
    solve(xsys; inival = 0.1, times = [0, 1], verbose = "");
    + + + +

    Or we get some more logging:

    + +
    solve(xsys; inival = 0.1, times = [0, 1], verbose = "en");
    + + + +

    But we can also look for the reasons of the allocations. Here, global values should be declared as constants.

    + +
    const D1 = 0.1
    +
    0.1
    + +
    function xflux1(f, u, edge)
    +    f[1] = D1 * (u[1, 1]^2 - u[1, 2]^2)
    +end
    +
    xflux1 (generic function with 1 method)
    + +
    xsys1 = VoronoiFVM.System(0:0.001:1; flux = xflux1, species = [1])
    +
    VoronoiFVM.System{Float64, Float64, Int32, Int64, Matrix{Int32}, Matrix{Float64}}(num_species=1)
    +
    + +
    solve(xsys1; inival = 0.1, times = [0, 1]);
    + + +

    v0.14

    +
    + + +

    VoronoiFVM.System constructor

    + + +

    Implicit creation of physics

    The VoronoiFVM.Physics struct almost never was used outside of the constructor of VoronoiFVM.System. Now it is possible to specify the flux functions directly in the system constructor. By default, it is also possible to set a list of species which are attached to all interior and boundary regions of the grid.

    + +
    grid1 = simplexgrid(0:0.1:1);
    + + +
    function multispecies_flux(y, u, edge)
    +    for i = 1:(edge.nspec)
    +        y[i] = u[i, 1] - u[i, 2]
    +    end
    +end
    +
    multispecies_flux (generic function with 1 method)
    + +
    function test_reaction(y, u, node)
    +    y[1] = u[1]
    +    y[2] = -u[1]
    +end
    +
    test_reaction (generic function with 1 method)
    + +
    begin
    +    system1 = VoronoiFVM.System(grid1;
    +                                flux = multispecies_flux,
    +                                reaction = test_reaction,
    +                                species = [1, 2])
    +    boundary_dirichlet!(system1; species = 1, region = 1, value = 1)
    +    boundary_dirichlet!(system1; species = 2, region = 2, value = 0)
    +end;
    + + +
    sol1 = solve(system1);
    + + + + + +
    @test isapprox(sum(sol1), 11.323894375033476, rtol = 1.0e-14)
    +
    Test Passed
    + + +

    Boundary conditions as part of physics

    This makes the API more consistent and opens an easy possibility to have space and time dependent boundary conditions. One can specify them either in breaction or the synonymous bcondition.

    + +
    function bcond2(y, u, bnode)
    +    boundary_neumann!(y, u, bnode; species = 1, region = 1, value = sin(bnode.time))
    +    boundary_dirichlet!(y, u, bnode; species = 2, region = 2, value = 0)
    +end;
    + + +
    system2 = VoronoiFVM.System(grid1;
    +                            flux = multispecies_flux,
    +                            reaction = test_reaction,
    +                            species = [1, 2],
    +                            bcondition = bcond2,
    +                            check_allocs = false);
    + + +
    sol2 = solve(system2; times = (0, 10), Δt_max = 0.01);
    + + + +GridVisualizer(Plotter=CairoMakie) + + +

    time: 4.99

    + + + + +
    @test isapprox(sum(sol2) / length(sol2), 2.4921650158811794, rtol = 1.0e-14)
    +
    Test Passed
    + + +

    Implicit creation of grid

    + + +

    By passing data for grid creation (one to three abstract vectors) instead a grid, a tensor product grid is implicitly created. This example also demonstrates position dependent boundary values.

    + +
    function bcond3(y, u, bnode)
    +    boundary_dirichlet!(y, u, bnode; region = 4, value = bnode[2])
    +    boundary_dirichlet!(y, u, bnode; region = 2, value = -bnode[2])
    +end;
    + + +
    system3 = VoronoiFVM.System(-1:0.1:1,
    +                            -1:0.1:1;
    +                            flux = multispecies_flux,
    +                            bcondition = bcond3,
    +                            species = 1);
    + + +
    sol3 = solve(system3);
    + + +
    @test isapprox(sum(sol3), 0.0, atol = 1.0e-14)
    +
    Test Passed
    + + +

    GridVisualize API extended to System

    Instead of a grid, a system can be passed to gridplot and scalarplot.

    + +
    scalarplot(system3, sol3; resolution = (300, 300), levels = 10, colormap = :hot)
    + + + +

    Parameters of solve

    + + +

    The solve API has been simplified and made more Julian. All entries of VoronoiFVM.NewtonControl can be now passed as keyword arguments to solve.

    Another new keyword argument is inival which allows to pass an initial value which by default is initialized to zero. Therefore we now can write solve(system) as we already have seen above.

    + +
    reaction4(y, u, bnode) = y[1] = -bnode[1]^2 + u[1]^4;
    + + +
    bc4(args...) = boundary_dirichlet!(args...; value = 0);
    + + +
    system4 = VoronoiFVM.System(-10:0.1:10;
    +                            species = [1],
    +                            reaction = reaction4,
    +                            flux = multispecies_flux,
    +                            bcondition = bc4);
    + + +
    sol4 = solve(system4; log = true, damp_initial = 0.001, damp_growth = 3);
    + + + + + +
    @test isapprox(sum(sol4), 418.58515700568535, rtol = 1.0e-14)
    +
    Test Passed
    + + +
    + + +
    +
    +

    Built with Julia 1.10.2 and

    +CairoMakie 0.11.5
    +ExtendableGrids 1.2.3
    +ExtendableSparse 1.3.1
    +GridVisualize 1.5.0
    +ILUZero 0.2.0
    +LinearSolve 2.22.1
    +Pkg 1.10.0
    +PlutoUI 0.7.55
    +Revise 3.5.13
    +VoronoiFVM 1.16.0 +
    + +
    diff --git a/v1.19.1/plutostatichtml_examples/flux-reconstruction/index.html b/v1.19.1/plutostatichtml_examples/flux-reconstruction/index.html new file mode 100644 index 000000000..6a6a98080 --- /dev/null +++ b/v1.19.1/plutostatichtml_examples/flux-reconstruction/index.html @@ -0,0 +1,355 @@ + +Obtaining vector fields · VoronoiFVM.jl
    + + + + +

    Flux reconstruction and visualization for the Laplace operator

    https://github.com/j-fu/VoronoiFVM.jl/blob/master/pluto-examples/outflow

    + + +

    We demonstrate the reconstruction of the gradient vector field from the solution of the Laplace operator and its visualization.

    + + + + +
    begin
    +    import Pkg as _Pkg
    +    haskey(ENV, "PLUTO_PROJECT") && _Pkg.activate(ENV["PLUTO_PROJECT"])
    +    using Revise
    +    using Test
    +    using SimplexGridFactory, Triangulate, ExtendableGrids, VoronoiFVM
    +    using PlutoUI, GridVisualize
    +    using CairoMakie
    +    CairoMakie.activate!(; type = "svg", visible = false)
    +    GridVisualize.default_plotter!(CairoMakie)
    +end;
    + + +

    Grid

    +
    + + +

    Define a "Swiss cheese domain" with punched-out holes, where each hole boundary corresponds to a different boundary condition.

    + +
    function swiss_cheese_2d()
    +    function circlehole!(builder, center, radius; n = 20)
    +        points = [point!(builder, center[1] + radius * sin(t), center[2] + radius * cos(t))
    +                  for t in range(0, 2π; length = n)]
    +        for i = 1:(n - 1)
    +            facet!(builder, points[i], points[i + 1])
    +        end
    +        facet!(builder, points[end], points[1])
    +        holepoint!(builder, center)
    +    end
    +
    +    builder = SimplexGridBuilder(; Generator = Triangulate)
    +    cellregion!(builder, 1)
    +    maxvolume!(builder, 0.1)
    +    regionpoint!(builder, 0.1, 0.1)
    +
    +    p1 = point!(builder, 0, 0)
    +    p2 = point!(builder, 10, 0)
    +    p3 = point!(builder, 10, 10)
    +    p4 = point!(builder, 0, 10)
    +
    +    facetregion!(builder, 1)
    +    facet!(builder, p1, p2)
    +    facet!(builder, p2, p3)
    +    facet!(builder, p3, p4)
    +    facet!(builder, p4, p1)
    +
    +    holes = [1.0 2.0
    +             8.0 9.0
    +             2.0 8.0
    +             8.0 4.0
    +             9.0 1.0
    +             3.0 4.0
    +             4.0 6.0
    +             7.0 9.0
    +             4.0 7.0
    +             7.0 5.0
    +             2.0 1.0
    +             4.0 1.0
    +             4.0 8.0
    +             3.0 6.0
    +             4.0 9.0
    +             6.0 9.0
    +             3.0 5.0
    +             1.0 4.0]'
    +
    +    radii = [
    +        0.15,
    +        0.15,
    +        0.1,
    +        0.35,
    +        0.2,
    +        0.3,
    +        0.1,
    +        0.4,
    +        0.1,
    +        0.4,
    +        0.2,
    +        0.2,
    +        0.2,
    +        0.35,
    +        0.15,
    +        0.25,
    +        0.15,
    +        0.25,
    +    ]
    +
    +    for i = 1:length(radii)
    +        facetregion!(builder, i + 1)
    +        circlehole!(builder, holes[:, i], radii[i])
    +    end
    +
    +    simplexgrid(builder)
    +end
    +
    swiss_cheese_2d (generic function with 1 method)
    + +
    grid = swiss_cheese_2d()
    +
    ExtendableGrids.ExtendableGrid{Float64, Int32};
    +dim: 2 nodes: 1434 cells: 2443 bfaces: 459
    +
    +
    + + + + +

    System + solution

    +
    + +
    mutable struct Params
    +    val11::Float64
    +end
    + + +
    params = Params(5)
    +
    Params(5.0)
    + + +

    Simple flux function for Laplace operator

    + +
    flux(y, u, edge, data) = y[1] = u[1, 1] - u[1, 2];
    + + + +

    At hole #11, the value will be bound to a slider defined below

    + +
    function bc(y, u, bnode, data)
    +    boundary_dirichlet!(y, u, bnode; region = 2, value = 10.0)
    +    boundary_dirichlet!(y, u, bnode; region = 3, value = 0.0)
    +    boundary_dirichlet!(y, u, bnode; region = 11, value = data.val11)
    +end
    +
    bc (generic function with 1 method)
    + + +

    Define a finite volume system with Dirichlet boundary conditions at some of the holes

    + +
    system = VoronoiFVM.System(grid; flux = flux, species = 1, bcondition = bc, data = params)
    +
    VoronoiFVM.System{Float64, Float64, Int32, Int64, Matrix{Int32}, Matrix{Float64}}(num_species=1)
    +
    + + +

    Solve, and trigger solution upon boundary value change

    + +
    begin
    +    params.val11 = val11
    +    sol = solve(system)
    +end;
    + + +
    @test params.val11 != 5.0 || isapprox(sum(sol), 7842.2173682050525; rtol = 1.0e-12)
    +
    Test Passed
    + +

    Flux reconstruction

    +
    + + +

    Reconstruct the node flux. It is a $d\times n_{spec}\times n_{nodes}$ tensor. nf[:,ispec,:] then is a vector function representing the flux density of species ispec in each node of the domain. This readily can be fed into GridVisualize.vectorplot.

    The reconstruction is based on the "magic formula" R. Eymard, T. Gallouet, R. Herbin, IMA Journal of Numerical Analysis (2006) 26, 326−353 (Arxive version), Lemma 2.4 .

    + +
    nf = nodeflux(system, sol)
    +
    2×1×1434 Array{Float64, 3}:
    +[:, :, 1] =
    +  0.05468367090633706
    + -0.05468367090633706
    +
    +[:, :, 2] =
    + 0.01852257911924369
    + 0.014818063295393813
    +
    +[:, :, 3] =
    + 0.0
    + 0.0
    +
    +;;; … 
    +
    +[:, :, 1432] =
    + 0.020523328431052094
    + 0.036034112502081016
    +
    +[:, :, 1433] =
    + 0.028906869669800477
    + 0.012529162794698063
    +
    +[:, :, 1434] =
    + 0.03545781788403497
    + 0.0342143068249023
    + +
    @test params.val11 != 5.0 || isapprox(sum(nf), 978.000534849034; rtol = 1.0e-14)
    +
    Test Passed
    + +
    vis = GridVisualizer(; dim = 2, resolution = (400, 400))
    +GridVisualizer(Plotter=CairoMakie) + + +

    $v_{11}:$5.0

    + + +

    Joint plot of solution and flux reconstruction

    + +
    begin
    +    scalarplot!(vis, grid, sol[1, :]; levels = 9, colormap = :summer, clear = true)
    +    vectorplot!(vis, grid, nf[:, 1, :]; clear = false, spacing = 0.5, vscale = 1.5)
    +    reveal(vis)
    +end
    + + + +

    The 1D case

    + +
    src(x) = exp(-x^2 / 0.01)
    +
    src (generic function with 1 method)
    + +
    source1d(y, node) = y[1] = src(node[1])
    +
    source1d (generic function with 1 method)
    + +
    flux1d(y, u, edge) = y[1] = u[1, 1]^2 - u[1, 2]^2
    +
    flux1d (generic function with 1 method)
    + +
    function bc1d(y, u, bnode)
    +    boundary_dirichlet!(y, u, bnode; region = 1, value = 0.01)
    +    boundary_dirichlet!(y, u, bnode; region = 2, value = 0.01)
    +end
    +
    bc1d (generic function with 1 method)
    + +
    grid1d = simplexgrid(-1:0.01:1)
    +
    ExtendableGrids.ExtendableGrid{Float64, Int32};
    +dim: 1 nodes: 201 cells: 200 bfaces: 2
    +
    +
    + +
    sys1d = VoronoiFVM.System(grid1d;
    +                          flux = flux1d,
    +                          bcondition = bc1d,
    +                          source = source1d,
    +                          species = [1],)
    +
    VoronoiFVM.System{Float64, Float64, Int32, Int64, Matrix{Int32}, Matrix{Float64}}(num_species=1)
    +
    + +
    sol1d = solve(sys1d; inival = 0.1)
    +
    1×201 Matrix{Float64}:
    + 0.01  0.0314043  0.0432719  0.0525231  …  0.0525231  0.0432719  0.0314043  0.01
    + +
    nf1d = nodeflux(sys1d, sol1d)
    +
    1×1×201 Array{Float64, 3}:
    +[:, :, 1] =
    + -0.08862269254527583
    +
    +[:, :, 2] =
    + -0.08862269254527581
    +
    +[:, :, 3] =
    + -0.08862269254527581
    +
    +;;; … 
    +
    +[:, :, 199] =
    + 0.08862269254527581
    +
    +[:, :, 200] =
    + 0.08862269254527581
    +
    +[:, :, 201] =
    + 0.08862269254527583
    + +
    let
    +    vis1d = GridVisualizer(; dim = 1, resolution = (500, 250), legend = :lt)
    +    scalarplot!(vis1d, grid1d, map(src, grid1d); label = "rhs", color = :blue)
    +    scalarplot!(vis1d, grid1d, sol1d[1, :]; label = "solution", color = :red, clear = false)
    +    vectorplot!(vis1d, grid1d, nf1d[:, 1, :]; label = "flux", clear = false, color = :green)
    +    reveal(vis1d)
    +end
    + + +
    @test nf1d[1, 1, 101] ≈ 0.0
    +
    Test Passed
    + +
    @test nf1d[1, 1, 1] ≈ -nf1d[1, 1, end]
    +
    Test Passed
    + + +
    + + +
    +
    +

    Built with Julia 1.10.2 and

    +CairoMakie 0.11.5
    +ExtendableGrids 1.2.3
    +GridVisualize 1.5.0
    +Pkg 1.10.0
    +PlutoUI 0.7.55
    +Revise 3.5.13
    +SimplexGridFactory 1.0.0
    +Triangulate 2.3.2
    +VoronoiFVM 1.16.0 +
    + +
    diff --git a/v1.19.1/plutostatichtml_examples/interfaces1d/index.html b/v1.19.1/plutostatichtml_examples/interfaces1d/index.html new file mode 100644 index 000000000..22fbfee41 --- /dev/null +++ b/v1.19.1/plutostatichtml_examples/interfaces1d/index.html @@ -0,0 +1,487 @@ + +Internal interfaces (1D) · VoronoiFVM.jl
    + + + +
    begin
    +    import Pkg as _Pkg
    +    haskey(ENV, "PLUTO_PROJECT") && _Pkg.activate(ENV["PLUTO_PROJECT"])
    +    using Revise
    +    using VoronoiFVM
    +    using ExtendableGrids
    +    using GridVisualize
    +    using PlutoUI
    +    using HypertextLiteral
    +    using LinearAlgebra
    +    using LinearSolve
    +    using Test
    +    using CairoMakie
    +    CairoMakie.activate!(; type = "svg", visible = false)
    +    GridVisualize.default_plotter!(CairoMakie)
    +end
    +
    CairoMakie
    + + + + + +

    Interface conditions in 1D

    Source

    This notebooks discusses handling of internal interfaces with VoronoiFVM.jl.

    Two subdomains

    For a simple stationary diffusion equation with an interior interface, we discuss possible interface conditions between two subdomains.

    Let $\Omega=\Omega_1\cup\Omega_2$ where $\Omega_1=(-1,0)$ and $\Omega_2=(0,1)$. Let $\Gamma_1={-1}$,$\Gamma_2={1}$ and $\Gamma_3={0}$.

    Regard the following problem:

    $\begin{aligned} -\Delta u_1 &= 0 & \text{in}\quad \Omega_1\\ -\Delta u_2 &= 0 & \text{in}\quad \Omega_2\\ \end{aligned}$

    with exterior boundary conditions

    $u_1|_{\Gamma_1} = g_1$ and $u_2|_{\Gamma_2} = g_2$

    For the interior boundary (interface) conditions we set

    $\nabla u_1|_{\Gamma_3}+f_1(u_1,u_2)=0$

    $-\nabla u_2|_{\Gamma_3}+f_2(u_1,u_2)=0$

    where $f_1$, $f_2$ are discussed later.

    + + +

    Set up

    + + +

    Create a grid with two subdomins and an interface in the center.

    + +
    nref = 2
    +
    2
    + +
    begin
    +    hmax = 0.2 / 2.0^nref
    +    hmin = 0.05 / 2.0^nref
    +    X1 = geomspace(-1.0, 0.0, hmax, hmin)
    +    X2 = geomspace(0.0, 1.0, hmin, hmax)
    +    X = glue(X1, X2)
    +    grid = VoronoiFVM.Grid(X)
    +
    +    bfacemask!(grid, [0.0], [0.0], 3)
    +    ## Material 1 left of 0
    +    cellmask!(grid, [-1.0], [0.0], 1)
    +    ## Material 2 right of 0
    +    cellmask!(grid, [0.0], [1.0], 2)
    +end;
    + + +
    gridplot(grid; legend = :rt, resolution = (600, 200))
    + + + +

    For later use (plotting) extract the two subgrids from the grid

    + +
    subgrid1 = subgrid(grid, [1]);
    + + +
    subgrid2 = subgrid(grid, [2]);
    + + + +

    Define the diffusion flux for the two species in their respective subdomains

    + +
    function flux!(f, u, edge)
    +    if edge.region == 1
    +        f[1] = u[1, 1] - u[1, 2]
    +    end
    +    if edge.region == 2
    +        f[2] = u[2, 1] - u[2, 2]
    +    end
    +end
    +
    flux! (generic function with 1 method)
    + + +

    Specify the outer boundary values.

    + +
    const g_1 = 1.0
    +
    1.0
    + +
    const g_2 = 0.1
    +
    0.1
    + + +

    Create the system. We pass the interface condition function as a parameter.

    + +
    function make_system(breaction)
    +    physics = VoronoiFVM.Physics(; flux = flux!, breaction = breaction)
    +
    +    ## Create system
    +    sys = VoronoiFVM.System(grid, physics; unknown_storage = :sparse)
    +
    +    ##  Enable species in their respective subregions
    +    enable_species!(sys, 1, [1])
    +    enable_species!(sys, 2, [2])
    +
    +    ## Set boundary conditions
    +    for ispec = 1:2
    +        boundary_dirichlet!(sys, ispec, 1, g_1)
    +        boundary_dirichlet!(sys, ispec, 2, g_2)
    +    end
    +    sys
    +end
    +
    make_system (generic function with 1 method)
    + + +

    Stationary solution with zero initial value

    + +
    function mysolve(sys)
    +    U = solve(sys)
    +    U1 = view(U[1, :], subgrid1)
    +    U2 = view(U[2, :], subgrid2)
    +    U1, U2
    +end
    +
    mysolve (generic function with 1 method)
    + + +

    Plot the results

    + +
    function plot(U1, U2; title = "")
    +    vis = GridVisualizer(; resolution = (600, 300))
    +    scalarplot!(vis,
    +                subgrid1,
    +                U1;
    +                clear = false,
    +                show = false,
    +                color = :green,
    +                label = "u1")
    +    scalarplot!(vis,
    +                subgrid2,
    +                U2;
    +                clear = false,
    +                show = true,
    +                color = :blue,
    +                label = "u2",
    +                legend = :rt,
    +                title = title,
    +                flimits = (-0.5, 1.5))
    +end
    +
    plot (generic function with 1 method)
    + + +

    No interface reaction

    This means we set $f_1(u_1,u_2)=0$ and $f_2(u_1,u_2)=0$.

    + +
    function noreaction(f, u, node) end
    +
    noreaction (generic function with 1 method)
    + +
    system1 = make_system(noreaction);
    + + +
    plot(mysolve(system1)...)
    + + + +

    The solution consists of two constants defined by the respective Dirichlet boundary conditions at the outer boundary.

    + + +

    Mass action law reaction $u_1 \leftrightharpoons u_2$

    This is a rather general ansatz where we assume a backward-forward reaction between the two species meeting at the interface with reaction constants $k_1$ and $k_2$, respectively.

    According to the mass action law, this translates to a reaction rate

    $r(u_1,u_2)=k_1u_1 - k_2u_2$

    and correspondingly

    $f_1(u_1,u_2)=r$

    $f_2(u_1,u_2)=-r$

    Note, that $f_i$ is monotonically increasing in $u_i$ and monotonically decreasing in the respective other argument, leading to an M-Property of the overall discretization matrix.

    Note that the "no reaction" case is just a special case where $k_1,k_2=0$.

    + +
    function mal_reaction(f, u, node)
    +    if node.region == 3
    +        react = k1 * u[1] - k2 * u[2]
    +        f[1] = react
    +        f[2] = -react
    +    end
    +end
    +
    mal_reaction (generic function with 1 method)
    + +
    system2 = make_system(mal_reaction)
    +
    VoronoiFVM.System{Float64, Float64, Int32, Int64, SparseArrays.SparseMatrixCSC{Int32, Int32}, VoronoiFVM.SparseSolutionArray{Float64, Int32}}(num_species=2)
    +
    + +
    begin
    +    const k1 = 0.1
    +    const k2 = 10
    +end
    +
    10
    + + + + + +

    The back reaction is 100 times stronger than the forward reaction. This means that species 2 is consumed, creating species 1.

    + + +

    Penalty enforcing continuity

    Setting $k_1,k_2$ to a large number leads to another special case of the above reaction - similar to the penalty method to implement the Dirichlet boundary conditions, this lets the reaction equation dominate, which in this case forces $u_1-u_2=0$ at the interface, and thus continuity.

    + +
    function penalty_reaction(f, u, node)
    +    if node.region == 3
    +        react = 1.0e10 * (u[1] - u[2])
    +        f[1] = react
    +        f[2] = -react
    +    end
    +end
    +
    penalty_reaction (generic function with 1 method)
    + +
    system3 = make_system(penalty_reaction);
    + + +
    plot(mysolve(system3)...)
    + + + +

    Penalty enforcing fixed jump

    Instead of enforcing continuity, one can enforce a fixed jump.

    + +
    const jump = 0.2
    +
    0.2
    + +
    function penalty_jump_reaction(f, u, node)
    +    if node.region == 3
    +        react = 1.0e10 * (u[1] - u[2] - jump)
    +        f[1] = react
    +        f[2] = -react
    +    end
    +end
    +
    penalty_jump_reaction (generic function with 1 method)
    + +
    system3jump = make_system(penalty_jump_reaction);
    + + +
    plot(mysolve(system3jump)...)
    + + + +

    Interface recombination

    Here, we implement an annihilation reaction $u_1 + u_2 \to \emptyset$ According to the mass action law, this is implemented via

    $r(u_1,u_2)=k_r u_1 u_2$

    $f_1(u_1,u_2)=r$

    $f_2(u_1,u_2)=r$

    + +
    function recombination(f, u, node)
    +    if node.region == 3
    +        react = k_r * (u[1] * u[2])
    +        f[1] = react
    +        f[2] = react
    +    end
    +end;
    + + +
    system4 = make_system(recombination);
    + + +
    const k_r = 1000
    +
    1000
    + +
    plot(mysolve(system4)...)
    + + + +

    Bot species are consumed at the interface.

    + + +

    Thin conductive interface layer

    Let us assume that the interface is of thickness $d$ which is however small with respect to $\Omega$ that we want to derive an interface condition from the assumption of an exact continuous solution within the interface.

    So let $\Omega_I=(x_l,x_r)$ be the interface region where we have $-\Delta u_I=0$ with values $u_l$, $u_r$ at the boundaries.

    Then we have for the flux in the interface region, $q_I=\nabla u = \frac1{d}(u_r - u_l)$

    Continuity of fluxes then gives $f_1=q_I$ and $f_2=-q_I$.

    Continuity of $u$ gives $u_{1,I}=u_l, u_{2,I}=u_r$ This gives

    $r=q_I=\frac{1}{d}(u_1-u_{2})$

    $f_1(u_1,v_1)=r$

    $f_2(u_1,v_1)=-r$

    and therefore another special case of the mass action law condition.

    + +
    const d = 1
    +
    1
    + +
    function thinlayer(f, u, node)
    +    if node.region == 3
    +        react = (u[1] - u[2]) / d
    +        f[1] = react
    +        f[2] = -react
    +    end
    +end
    +
    thinlayer (generic function with 1 method)
    + +
    system5 = make_system(thinlayer);
    + + +
    plot(mysolve(system5)...)
    + + + +

    The solution looks very similar to the case of the jump condition, however here, the size of the jump is defined by the physics of the interface.

    + +

    Multiple domains

    +

    From the above discussion it seems that discontinuous interface conditions can be formulated in a rather general way via linear or nonlinear robin boundary conditions for each of the adjacent discontinuous species. Technically, it is necessary to be able to access the adjacent bulk data.

    + + +

    In order to streamline the handling of multiple interfaces, we propose an API layer on top of the species handling of VoronoiFVM. We call these "meta species" "quantities".

    + + +

    We define a grid with N=6 subregions

    + +
    N = 6
    +
    6
    + +
    begin
    +    XX = collect(0:0.1:1)
    +    local xcoord = XX
    +    for i = 1:(N - 1)
    +        xcoord = glue(xcoord, XX .+ i)
    +    end
    +    grid2 = simplexgrid(xcoord)
    +    for i = 1:N
    +        cellmask!(grid2, [i - 1], [i], i)
    +    end
    +    for i = 1:(N - 1)
    +        bfacemask!(grid2, [i], [i], i + 2)
    +    end
    +end
    + + +
    gridplot(grid2; legend = :lt, resolution = (600, 200))
    + + + +

    To work with quantities, we first introduce a new constructor call without the "physics" parameter:

    + +
    system6 = VoronoiFVM.System(grid2)
    +
    VoronoiFVM.System{Float64, Float64, Int32, Int64, Matrix{Int32}, Matrix{Float64}}(num_species=0)
    +
    + + +

    First, we introduce a continuous quantity which we name "cspec". Note that the "species number" can be assigned automatically if not given explicitly.

    + +
    const cspec = ContinuousQuantity(system6, 1:N; ispec = 1)
    +
    ContinuousQuantity{Int32}(1, 1)
    + + +

    A discontinuous quantity can be introduced as well. by default, each reagion gets a new species number. This can be overwritten by the user. It is important that the speces numbers of neighboring regions differ.

    + +
    const dspec = DiscontinuousQuantity(system6, 1:N; regionspec = [2 + i % 2 for i = 1:N])
    +
    DiscontinuousQuantity{Int32}(Int32[3, 2, 3, 2, 3, 2], 2)
    + + +

    For both quantities, we define simple diffusion fluxes:

    + +
    function flux2(f, u, edge)
    +    f[dspec] = u[dspec, 1] - u[dspec, 2]
    +    f[cspec] = u[cspec, 1] - u[cspec, 2]
    +end
    +
    flux2 (generic function with 1 method)
    + + +

    Define a thin layer interface condition for dspec and an interface source for cspec.

    + +
    function breaction2(f, u, node)
    +    if node.region > 2
    +        react = (u[dspec, 1] - u[dspec, 2]) / d1
    +        f[dspec, 1] = react
    +        f[dspec, 2] = -react
    +
    +        f[cspec] = -q1
    +    end
    +end
    +
    breaction2 (generic function with 1 method)
    + + +

    Add physics to the system, set dirichlet bc at both ends, and extract subgrids for plotting (until there will be a plotting API for this...)

    + +
    begin
    +    physics!(system6, VoronoiFVM.Physics(; flux = flux2, breaction = breaction2))
    +
    +    ## Set boundary conditions
    +    boundary_dirichlet!(system6, dspec, 1, g_1)
    +    boundary_dirichlet!(system6, dspec, 2, g_2)
    +    boundary_dirichlet!(system6, cspec, 1, 0)
    +    boundary_dirichlet!(system6, cspec, 2, 0)
    +
    +    # ensure that `solve` is called only after this cell
    +    # as mutating circumvents the reactivity of the notebook
    +    physics_ok = true
    +end;
    + + +
    allsubgrids = subgrids(dspec, system6)
    +
    6-element Vector{ExtendableGrid{Float64, Int32}}:
    + ExtendableGrids.ExtendableGrid{Float64, Int32};
    +dim: 1 nodes: 11 cells: 10 bfaces: 2
    +
    +
    + ExtendableGrids.ExtendableGrid{Float64, Int32};
    +dim: 1 nodes: 11 cells: 10 bfaces: 2
    +
    +
    + ExtendableGrids.ExtendableGrid{Float64, Int32};
    +dim: 1 nodes: 11 cells: 10 bfaces: 2
    +
    +
    + ExtendableGrids.ExtendableGrid{Float64, Int32};
    +dim: 1 nodes: 11 cells: 10 bfaces: 2
    +
    +
    + ExtendableGrids.ExtendableGrid{Float64, Int32};
    +dim: 1 nodes: 11 cells: 10 bfaces: 2
    +
    +
    + ExtendableGrids.ExtendableGrid{Float64, Int32};
    +dim: 1 nodes: 11 cells: 10 bfaces: 2
    +
    +
    + +
    if physics_ok
    +    sol6 = solve(system6; inival = 0.5)
    +end;
    + + +
    const d1 = 0.1
    +
    0.1
    + +
    const q1 = 0.2
    +
    0.2
    + +
    function plot2(U, subgrids, system6)
    +    dvws = VoronoiFVM.views(U, dspec, allsubgrids, system6)
    +    cvws = VoronoiFVM.views(U, cspec, allsubgrids, system6)
    +    vis = GridVisualizer(; resolution = (600, 300), legend = :rt)
    +    scalarplot!(vis,
    +                allsubgrids,
    +                grid2,
    +                dvws;
    +                flimits = (-0.5, 1.5),
    +                clear = false,
    +                color = :red,
    +                label = "discontinuous species")
    +    scalarplot!(vis,
    +                allsubgrids,
    +                grid2,
    +                cvws;
    +                flimits = (-0.5, 1.5),
    +                clear = false,
    +                color = :green,
    +                label = "continuous species")
    +    reveal(vis)
    +end
    +
    plot2 (generic function with 1 method)
    + +
    plot2(sol6, subgrids, system6)
    + + +

    Testing

    +
    + +
    if d1 == 0.1 && N == 6
    +    @test norm(system6, sol6, 2) ≈ 7.0215437706445245
    +end
    +
    Test Passed
    + + +
    + + + + + +
    +
    +

    Built with Julia 1.10.2 and

    +CairoMakie 0.11.5
    +ExtendableGrids 1.2.3
    +GridVisualize 1.5.0
    +HypertextLiteral 0.9.5
    +LinearSolve 2.22.1
    +Pkg 1.10.0
    +PlutoUI 0.7.55
    +Revise 3.5.13
    +VoronoiFVM 1.16.0 +
    + +
    diff --git a/v1.19.1/plutostatichtml_examples/nonlinear-solvers/index.html b/v1.19.1/plutostatichtml_examples/nonlinear-solvers/index.html new file mode 100644 index 000000000..914054fd0 --- /dev/null +++ b/v1.19.1/plutostatichtml_examples/nonlinear-solvers/index.html @@ -0,0 +1,300 @@ + +Nonlinear solver control · VoronoiFVM.jl
    + + + + +

    Nonlinear solver control

    Source

    + + +

    Generally, nonlinear systems in this package are solved using Newton's method. In many cases, the default settings provided by this package work well. However, the convergence of Newton's method is only guaranteed with initial values s7ufficiently close to the exact solution. This notebook describes how change the default settings for the solution of nonlinear problems with VoronoiFVM.jl.

    + + + + +
    begin
    +    import Pkg as _Pkg
    +    haskey(ENV, "PLUTO_PROJECT") && _Pkg.activate(ENV["PLUTO_PROJECT"])
    +    using Revise
    +    using VoronoiFVM
    +    using ExtendableGrids
    +    using Test
    +    using PlutoUI
    +    using LinearAlgebra
    +    using GridVisualize
    +    using CairoMakie
    +    CairoMakie.activate!(; type = "svg", visible = false)
    +    GridVisualize.default_plotter!(CairoMakie)
    +end;
    + + + +

    Define a nonlinear Poisson equation to have an example. Let $Ω=(0,10)$ and define

    $$\begin{aligned} +-Δ u + e^u-e^{-u} & = 0 & \text{in}\; Ω \\ + u(0)&=100\\ + u(10)&=0 +\end{aligned}$$

    + +
    X = 0:0.001:1
    +
    0.0:0.001:1.0
    + +
    flux(y, u, edge) = y[1] = u[1, 1] - u[1, 2];
    + + +
    function reaction(y, u, node)
    +    eplus = exp(u[1])
    +    eminus = 1 / eplus
    +    y[1] = eplus - eminus
    +end
    +
    reaction (generic function with 1 method)
    + +
    function bc(y, u, node)
    +    boundary_dirichlet!(y, u, node; region = 1, value = 100)
    +    boundary_dirichlet!(y, u, node; region = 2, value = 0.0)
    +end;
    + + +
    system = VoronoiFVM.System(X; flux = flux, reaction = reaction, bcondition = bc, species = 1);
    + + +

    Solution using default settings

    +
    + +
    begin
    +    sol = solve(system; log = true)
    +    hist = history(system)
    +end;
    + + +
    scalarplot(system,
    +           sol;
    +           resolution = (500, 200),
    +           xlabel = "x",
    +           ylable = "y",
    +           title = "solution")
    + + + +

    With log=true, the solve method in addition to the solution records the solution history which after finished solution can be obtatined as history(system).

    + + +

    The history can be plotted:

    + +
    function plothistory(h)
    +    scalarplot(1:length(h),
    +               h;
    +               resolution = (500, 200),
    +               yscale = :log,
    +               xlabel = "step",
    +               ylabel = "||δu||_∞",
    +               title = "Maximum norm of Newton update")
    +end;
    + + +
    plothistory(hist)
    + + + +

    History can be summarized:

    + +
    summary(hist)
    +
    (seconds = 6.22, tasm = 4.95, tlinsolve = 0.501, iters = 93, absnorm = 1.6e-12, relnorm = 1.62e-14, roundoff = 1.69e-13, factorizations = 1, liniters = 0)
    + + +

    History can be explored in detail:

    + + +
    93-element Vector{Any}:
    + (update = 98.8, contraction = 1.0, round = 426.0)
    + (update = 1.0, contraction = 0.0101, round = 0.0222)
    + (update = 1.0, contraction = 1.0, round = 0.0223)
    + (update = 1.0, contraction = 1.0, round = 0.0225)
    + (update = 1.0, contraction = 1.0, round = 0.0227)
    + (update = 1.0, contraction = 1.0, round = 0.0229)
    + (update = 1.0, contraction = 1.0, round = 0.0231)
    + ⋮
    + (update = 0.87, contraction = 0.88, round = 0.0247)
    + (update = 0.477, contraction = 0.548, round = 0.0167)
    + (update = 0.0915, contraction = 0.192, round = 0.00446)
    + (update = 0.00266, contraction = 0.029, round = 0.000177)
    + (update = 2.22e-6, contraction = 0.000837, round = 1.9e-7)
    + (update = 1.6e-12, contraction = 7.21e-7, round = 1.69e-13)
    + + +

    With default solver settings, for this particular problem, Newton's method needs 93 iteration steps.

    + +
    check(sol) = isapprox(sum(sol), 2554.7106586964906; rtol = 1.0e-12)
    +
    check (generic function with 1 method)
    + +
    @test check(sol)
    +
    Test Passed
    + +

    Damping

    +
    + + +

    Try to use a damped version of Newton method. The damping scheme is rather simple: an initial damping value damp_initial is increased by a growth factor damp_growth in each iteration until it reaches 1.

    + +
    begin
    +    sol1 = solve(system; log = true, inival = 1, damp = 0.15, damp_grow = 1.5)
    +    hist1 = history(system)
    +end
    +
    28-element NewtonSolverHistory:
    + 97.81584868964866
    +  9.16276036973695
    +  0.9999999998511513
    +  0.9999999997401952
    +  0.999999999441547
    +  0.9999999983981808
    +  0.9999999941837444
    +  ⋮
    +  0.7769510871365354
    +  0.50248357049323
    +  0.17071846878363076
    +  0.015586640226738904
    +  0.00011698226343053463
    +  6.5218450955795574e-9
    + + + + +
    VoronoiFVM.details(hist1)
    +
    28-element Vector{Any}:
    + (update = 97.8, contraction = 1.0, round = 5.29)
    + (update = 9.16, contraction = 0.0937, round = 0.0298)
    + (update = 1.0, contraction = 0.109, round = 0.0446)
    + (update = 1.0, contraction = 1.0, round = 0.0655)
    + (update = 1.0, contraction = 1.0, round = 0.0959)
    + (update = 1.0, contraction = 1.0, round = 0.123)
    + (update = 1.0, contraction = 1.0, round = 0.119)
    + ⋮
    + (update = 0.777, contraction = 0.85, round = 0.00032)
    + (update = 0.502, contraction = 0.647, round = 0.000207)
    + (update = 0.171, contraction = 0.34, round = 7.04e-5)
    + (update = 0.0156, contraction = 0.0913, round = 6.43e-6)
    + (update = 0.000117, contraction = 0.00751, round = 4.83e-8)
    + (update = 6.52e-9, contraction = 5.58e-5, round = 2.69e-12)
    + +
    summary(hist1)
    +
    (seconds = 0.117, tasm = 0.071, tlinsolve = 0.00208, iters = 28, absnorm = 6.52e-9, relnorm = 6.67e-11, roundoff = 2.69e-12, factorizations = 1, liniters = 0)
    + + +

    We see that the number of iterations decreased significantly.

    + +
    @test check(sol1)
    +
    Test Passed
    + +

    Embedding

    +
    + + +

    Another possibility is the embedding (or homotopy) via a parameter: start with solving a simple problem and increase the level of complexity by increasing the parameter until the full problem is solved. This process is controlled by the parameters

    • Δp: initial parameter step size

    • Δp_min: minimal parameter step size

    • Δp_max: maximum parameter step size

    • Δp_grow: maximum growth factor

    • Δu_opt: optimal difference of solutions between two embedding steps

    After successful solution of a parameter, the new parameter step size is calculated as Δp_new=min(Δp_max, Δp_grow, Δp*Δu_opt/(|u-u_old|+1.0e-14)) and adjusted to the end of the parameter interval.

    If the solution is unsuccessful, the parameter stepsize is halved and solution is retried, until the minimum step size is reached.

    + +
    function pbc(y, u, node)
    +    boundary_dirichlet!(y, u, node; region = 1, value = 100 * embedparam(node))
    +    boundary_dirichlet!(y, u, node; region = 2, value = 0)
    +end;
    + + +
    system2 = VoronoiFVM.System(X;
    +                            flux = flux,
    +                            reaction = function (y, u, node)
    +                                reaction(y, u, node)
    +
    +                                y[1] = y[1] * embedparam(node)
    +                            end,
    +                            bcondition = pbc,
    +                            species = 1,);
    + + +
    begin
    +    sol2 = solve(system2;
    +                 inival = 0,
    +                 log = true,
    +                 embed = (0, 1),
    +                 Δp = 0.1,
    +                 max_lureuse = 0,
    +                 Δp_grow = 1.2,
    +                 Δu_opt = 15)
    +    history2 = history(system2)
    +end
    +
    9-element TransientSolverHistory:
    + [0.0]
    + [9.989343055885081, 0.9814815933242419, 0.8053267053699209, 0.34118506593318715, 0.03588566608739014, 0.00030501510006572047, 2.0656145571680413e-8, 3.757192460108166e-15]
    + [11.341463937623553, 0.9997315206431724, 0.9990808258671514, 0.9966384496135858, 0.9877041414138585, 0.9368309534589447, 0.7284028335581745, 0.3000679588018341, 0.03462415975336775, 0.0003922413719470201, 5.021311921938722e-8, 1.3746465935025473e-15]
    + [1.5085724152572164, 0.4205506561491683, 0.108269451712538, 0.005760884131907183, 1.5240635820497453e-5, 1.0633964457339243e-10]
    + [0.32730498752976783, 0.04842614744100337, 0.0010781622274304223, 4.822924090663502e-7, 8.955521318268822e-14]
    + [0.18959174175720017, 0.019475257499845283, 0.00017208751333109998, 1.2283470332866745e-8, 2.6864715379034067e-15]
    + [0.22151709368816527, 0.01350594149309332, 8.295254108208108e-5, 2.874746673723172e-9, 1.5200200846066591e-15]
    + [0.9999839855864412, 0.9999129411082036, 0.9996450908924625, 0.9987144967036693, 0.9956410853148298, 0.9858738195909444, 0.956086313069598, 0.8714057437368012, 0.6663892012877948, 0.32544591325884925, 0.05964842227578665, 0.001663844974785831, 1.2451922222765833e-6, 6.958364377298142e-13]
    + [0.9999999999255985, 0.9999999995955114, 0.9999999983507291, 0.9999999940224221, 0.9999999796890736, 0.9999999337470156, 0.9999997898900223, 0.9999993472708903, 0.9999980039123315, 0.9999939712056518  …  0.988838205675924, 0.9682851805189654, 0.9122371945634715, 0.772531338122871, 0.4950582642141573, 0.16481352791232465, 0.014468420882723347, 0.00010072395527063067, 4.834945474226937e-9, 1.0894463251845288e-15]
    + +
    summary(history2)
    +
    (seconds = 0.258, tasm = 0.186, tlinsolve = 0.00497, steps = 9, iters = 81, maxabsnorm = 1.06e-10, maxrelnorm = 7.05e-11, maxroundoff = 2.26e-13, iters_per_step = 10.1, facts_per_step = 0.0, liniters_per_step = 0.0)
    + +
    plothistory(vcat(history2[2:end]...))
    + + +
    sol2[end]
    +
    1×1001 Matrix{Float64}:
    + 79.6896  17.8835  14.5192  13.1759  12.3601  …  0.00695716  0.00347857  3.47857e-30
    + +
    @test check(sol2[end])
    +
    Test Passed
    + + +

    For this particular problem, embedding uses less overall Newton steps than the default settings, but the damped method is faster.

    + +

    Solver control

    +
    + + +

    Here we show the docsctring of SolverControl (formerly NewtonControl). This is a struct which can be passed to the solve method. Alternatively, as shown in this notebook, keyword arguments named like its entries can be passed directly to the solve method.

    + +
    @doc VoronoiFVM.SolverControl
    +
    SolverControl
    +SolverControl(;kwargs...)
    +SolverControl(linear_solver_strategy, sys; kwargs...)

    Solver control parameter for time stepping, embedding, Newton method and linear solver control. All field names can be used as keyword arguments for solve(system::VoronoiFVM.AbstractSystem; kwargs...)

    Newton's method solves $F(u)=0$ by the iterative procedure $u_{i+1}=u_{i} - d_i F'(u_i)^{-1}F(u_i)$ starting with some initial value $u_0$, where $d_i$ is a damping parameter.

    For linear solver strategies, see VoronoiFVM.LinearSolverStrategy.

    • verbose::Union{Bool, String}: Verbosity control. A collection of output categories is given in a string composed of the following letters:

      • a: allocation warnings

      • d: deprecation warnings

      • e: time/parameter evolution log

      • n: newton solver log

      • l: linear solver log

      Alternatively, a Bool value can be given, resulting in

      • true: "neda"

      • false: "da"

      Switch off all output including deprecation warnings via verbose="". In the output, corresponding messages are marked e.g. via '[n]', [a] etc. (besides of '[l]')

    • abstol::Float64: Tolerance (in terms of norm of Newton update): terminate if $\Delta u_i=||u_{i+1}-u_i||_\infty <$abstol.

    • reltol::Float64: Tolerance (relative to the size of the first update): terminate if $\Delta u_i/\Delta u_1<$reltol.

    • maxiters::Int64: Maximum number of newton iterations.

    • tol_round::Float64: Tolerance for roundoff error detection: terminate if $|\;||u_{i+1}||_1 - ||u_{i}||_1\;|/ ||u_{i}||_1<$tol_round occurred max_round times in a row.

    • tol_mono::Float64: Tolerance for monotonicity test: terminate with error if $\Delta u_i/\Delta u_{i-1}>$1/tol_mono.

    • damp_initial::Float64: Initial damping parameter $d_0$. To handle convergence problems, set this to a value less than 1.

    • damp_growth::Float64: Damping parameter growth factor: $d_{i+1}=\min(d_i\cdot$max_growth$,1)$. It should be larger than 1.

    • max_round::Int64: Maximum number of consecutive iterations within roundoff error tolerance The default effectively disables this criterion.

    • unorm::Function: Calculation of Newton update norm

    • rnorm::Function: Functional for roundoff error calculation

    • method_linear::Union{Nothing, LinearSolve.SciMLLinearSolveAlgorithm}: Solver method for linear systems (see LinearSolve.jl). If given nothing, as default are chosen (for Float64 calculations):

      • 1D: KLUFactorization()

      • 2D: SparspakFactorization()

      • 3D: UMFPACKFactorization()

      SparspakFactorization() is the default choice for general number types. Users should experiment with what works best for their problem.

      For easy access to this functionality, see see also VoronoiFVM.LinearSolverStrategy.

    • reltol_linear::Float64: Relative tolerance of iterative linear solver.

    • abstol_linear::Float64: Absolute tolerance of iterative linear solver.

    • maxiters_linear::Int64: Maximum number of iterations of linear solver

    • precon_linear::Union{Nothing, Function, ExtendableSparse.AbstractFactorization, LinearSolve.SciMLLinearSolveAlgorithm, Type}: Constructor for preconditioner for linear systems. This should work as a function precon_linear(A) which takes an AbstractSparseMatrixCSC (e.g. an ExtendableSparseMatrix) and returns a preconditioner object in the sense of LinearSolve.jl, i.e. which has an ldiv!(u,A,v) method. Useful examples:

      • ExtendableSparse.ILUZero

      • ExtendableSparse.Jacobi

      For easy access to this functionality, see see also VoronoiFVM.LinearSolverStrategy.

    • keepcurrent_linear::Bool: Update preconditioner in each Newton step ?

    • Δp::Float64: Initial parameter step for embedding.

    • Δp_max::Float64: Maximal parameter step size.

    • Δp_min::Float64: Minimal parameter step size.

    • Δp_grow::Float64: Maximal parameter step size growth.

    • Δp_decrease::Float64: Parameter step decrease factor upon rejection

    • Δt::Float64: Initial time step size.

    • Δt_max::Float64: Maximal time step size.

    • Δt_min::Float64: Minimal time step size.

    • Δt_grow::Float64: Maximal time step size growth.

    • Δt_decrease::Float64: Time step decrease factor upon rejection

    • Δu_opt::Float64: Optimal size of update for time stepping and embedding. The algorithm tries to keep the difference in norm between "old" and "new" solutions approximately at this value.

    • Δu_max_factor::Float64: Control maximum sice of update Δu for time stepping and embeding relative to Δu_opt. Time steps with Δu > Δu_max_factor*Δu_opt will be rejected.

    • force_first_step::Bool: Force first timestep.

    • num_final_steps::Int64: Number of final steps to adjust at end of time interval in order to prevent breakdown of step size.

    • handle_exceptions::Bool: Handle exceptions during transient solver and parameter embedding. If true, exceptions in Newton solves are caught, the embedding resp. time step is lowered, and solution is retried. Moreover, if embedding or time stepping fails (e.g. due to reaching minimal step size), a warning is issued, and a solution is returned with all steps calculated so far.

      Otherwise (by default) errors are thrown.

    • store_all::Bool: Store all steps of transient/embedding problem:

    • in_memory::Bool: Store transient/embedding solution in memory

    • log::Any: Record history

    • edge_cutoff::Float64: Edge parameter cutoff for rectangular triangles.

    • pre::Function: Function pre(sol,t) called before time/embedding step

    • post::Function: Function post(sol,oldsol,t,Δt) called after successful time/embedding step

    • sample::Function: Function sample(sol,t) to be called for each t in times[2:end]

    • delta::Function: Time step error estimator. A function Δu=delta(system,u,uold,t,Δt) to calculate Δu.

    • tol_absolute::Union{Nothing, Float64}

    • tol_relative::Union{Nothing, Float64}

    • damp::Union{Nothing, Float64}

    • damp_grow::Union{Nothing, Float64}

    • max_iterations::Union{Nothing, Int64}

    • tol_linear::Union{Nothing, Float64}

    • max_lureuse::Union{Nothing, Int64}

    • mynorm::Union{Nothing, Function}

    • myrnorm::Union{Nothing, Function}

    + + +
    + + +
    +
    +

    Built with Julia 1.10.2 and

    +CairoMakie 0.11.5
    +ExtendableGrids 1.2.3
    +GridVisualize 1.5.0
    +Pkg 1.10.0
    +PlutoUI 0.7.55
    +Revise 3.5.13
    +VoronoiFVM 1.16.0 +
    + +
    diff --git a/v1.19.1/plutostatichtml_examples/ode-brusselator/index.html b/v1.19.1/plutostatichtml_examples/ode-brusselator/index.html new file mode 100644 index 000000000..2b87e9293 --- /dev/null +++ b/v1.19.1/plutostatichtml_examples/ode-brusselator/index.html @@ -0,0 +1,270 @@ + +OrdinaryDiffEq.jl brusselator · VoronoiFVM.jl
    + + + +
    begin
    +    import Pkg as _Pkg
    +    haskey(ENV, "PLUTO_PROJECT") && _Pkg.activate(ENV["PLUTO_PROJECT"])
    +    using Test
    +    using Revise
    +    using Printf
    +    using VoronoiFVM
    +    using OrdinaryDiffEq
    +    using LinearAlgebra
    +    using PlutoUI
    +    using ExtendableGrids
    +    using DataStructures
    +    using GridVisualize,CairoMakie
    +    default_plotter!(CairoMakie)
    +    CairoMakie.activate!(type="svg")
    +end
    + + +

    A Brusselator problem

    +
    + + +

    Two species diffusing and interacting via a reaction

    $$\begin{aligned} + \partial_t u_1 - \nabla \cdot (D_1 \nabla u_1) &+ (B+1)u_1-A-u_1^2u_2 =0\\ + \partial_t u_2 - \nabla \cdot (D_2 \nabla u_2) &+ u_1^2u_2 -B u_1 =0\\ +\end{aligned}$$

    + +
    begin 
    +    const bruss_A=2.25
    +    const bruss_B=7.0
    +    const bruss_D_1=0.025
    +    const bruss_D_2=0.25
    +    const pert=0.1
    +    const bruss_tend=150
    +end;
    +
    + + +
    function bruss_storage(f,u,node)
    +    f[1]=u[1]
    +    f[2]=u[2]
    +end;
    + + +
    function bruss_diffusion(f,u,edge)
    +    f[1]=bruss_D_1*(u[1,1]-u[1,2])
    +    f[2]=bruss_D_2*(u[2,1]-u[2,2])	
    +end;
    + + +
    function bruss_reaction(f,u,node)
    +    f[1]= (bruss_B+1.0)*u[1]-bruss_A-u[1]^2*u[2]
    +    f[2]= u[1]^2*u[2]-bruss_B*u[1]
    +end;
    + + +
    begin
    +    
    +function ODESolver(system,inival,solver)
    +    problem = ODEProblem(system,inival,(0,bruss_tend))
    +    odesol = solve(problem,
    +                                         solver,
    +                                         dt=1.0e-5,reltol=1.0e-4)
    +    reshape(odesol,system)
    +end;
    +
    +    sys0=VoronoiFVM.System(simplexgrid(0:0.1:1),species=[1,2],flux=bruss_diffusion, storage=bruss_storage, reaction=bruss_reaction);
    +    problem0 = ODEProblem(sys0,unknowns(sys0),(0,0.1))
    +
    +    for method in diffeqmethods
    +        solve(problem0,method.second())#precompile
    +   end
    +end
    + + + +
    OrderedDict{String, UnionAll} with 4 entries:
    +  "Rosenbrock23 (Rosenbrock)"    => Rosenbrock23
    +  "QNDF2 (Like matlab's ode15s)" => QNDF2
    +  "FBDF"                         => FBDF
    +  "Implicit Euler"               => ImplicitEuler
    + +
    if bruss_dim==1
    +        bruss_X=-1:0.01:1
    +        bruss_grid=simplexgrid(bruss_X)
    +    else
    +        bruss_X=-1:0.1:1
    +        bruss_grid=simplexgrid(bruss_X,bruss_X)
    +end;
    + + +
    bruss_system=VoronoiFVM.System(bruss_grid,species=[1,2],
    +            flux=bruss_diffusion, storage=bruss_storage, reaction=bruss_reaction);
    + + +
    begin
    +    inival=unknowns(bruss_system,inival=0)
    +    coord=bruss_grid[Coordinates]
    +    fpeak(x)=exp(-norm(10*x)^2)
    +    for i=1:size(inival,2)
    +   	 		inival[1,i]=fpeak(coord[:,i])
    +   	 		inival[2,i]=0
    +            #
    +    end
    +end
    + + +
    t_run=@elapsed bruss_tsol=ODESolver(bruss_system,inival,diffeqmethods[bruss_method]());
    + + +
    (t_run=t_run,VoronoiFVM.details(bruss_system.history)...)
    +
    (t_run = 5.009043213, nd = 1926, njac = 855, nf = 2780)
    + + +

    dim:$\;$ method: $\;$ t: 150.0

    + + + + + + +
    +

    Built with Julia 1.10.2 and

    +CairoMakie 0.11.6
    +DataStructures 0.18.16
    +ExtendableGrids 1.2.3
    +GridVisualize 1.5.0
    +OrdinaryDiffEq 6.66.0
    +Pkg 1.10.0
    +PlutoUI 0.7.55
    +Revise 3.5.13
    +VoronoiFVM 1.17.1 +
    + +
    diff --git a/v1.19.1/plutostatichtml_examples/ode-diffusion1d/index.html b/v1.19.1/plutostatichtml_examples/ode-diffusion1d/index.html new file mode 100644 index 000000000..33b077039 --- /dev/null +++ b/v1.19.1/plutostatichtml_examples/ode-diffusion1d/index.html @@ -0,0 +1,146 @@ + +OrdinaryDiffEq.jl nonlinear diffusion · VoronoiFVM.jl
    + + + +
    begin
    +    import Pkg as _Pkg
    +    haskey(ENV, "PLUTO_PROJECT") && _Pkg.activate(ENV["PLUTO_PROJECT"])
    +    using Test
    +    using Revise
    +    using Printf
    +    using VoronoiFVM
    +    using OrdinaryDiffEq
    +    using LinearAlgebra
    +    using PlutoUI
    +    using DataStructures
    +    using GridVisualize,CairoMakie
    +end
    + + + +

    Solve the nonlinear diffusion equation

    $$\partial_t u -\Delta u^m = 0$$

    in $\Omega=(-1,1)$ with homogeneous Neumann boundary conditons using the implicit Euler method.

    This equation is also called "porous medium equation". The Barenblatt solution

    $$b(x,t)=\max\left(0,t^{-\alpha}\left(1-\frac{\alpha(m-1)r^2}{2dmt^{\frac{2\alpha}{d}}}\right)^{\frac{1}{m-1}}\right)$$

    is an exact solution of this problem which for m>1 has a finite support. We initialize this problem with the exact solution for $t=t_0=0.001$.

    (see Barenblatt, G. I. "On nonsteady motions of gas and fluid in porous medium." Appl. Math. and Mech.(PMM) 16.1 (1952): 67-78.)

    Here, we compare the implicit Euler approach in VoronoiFVM with the ODE solvers in DifferentialEquations.jl and demonstrate the possibility to use VoronoiFVM to define differential operators compatible with its ODEFunction interface.

    + +
    function barenblatt(x,t,m)
    +    tx=t^(-1.0/(m+1.0))
    +    xx=x*tx
    +    xx=xx*xx
    +    xx=1- xx*(m-1)/(2.0*m*(m+1));
    +    if xx<0.0
    +        xx=0.0
    +    end
    +    return tx*xx^(1.0/(m-1.0))
    +end
    +
    barenblatt (generic function with 1 method)
    + +
    function create_porous_medium_problem(n,m)
    +    h=1.0/convert(Float64,n/2)
    +    X=collect(-1:h:1)
    +    grid=VoronoiFVM.Grid(X)
    +
    +    function flux!(f,u,edge)
    +        f[1]=u[1,1]^m-u[1,2]^m
    +    end
    +
    +    storage!(f,u,node)= f[1]=u[1]
    +
    +    sys=VoronoiFVM.System(grid,flux=flux!,storage=storage!, species=1)
    +    sys,X
    +end
    +
    create_porous_medium_problem (generic function with 1 method)
    + +
    begin
    +function run_vfvm(;n=20,m=2,t0=0.001, tend=0.01,tstep=1.0e-6)
    +    sys,X=create_porous_medium_problem(n,m)
    +    inival=unknowns(sys)
    +    inival[1,:].=map(x->barenblatt(x,t0,m),X)
    +    sol=VoronoiFVM.solve(sys;inival,times=(t0,tend),Δt=tstep,Δu_opt=0.01,Δt_min=tstep,store_all=true,log=true, reltol=1.0e-3)
    +    err=norm(sol[1,:,end]-map(x->barenblatt(x,tend,m),X))
    +    sol,sys,err
    +end
    +run_vfvm(m=2,n=10) # "Precompile"
    +end;
    + + +
    begin
    +    function run_diffeq(;n=20,m=2, t0=0.001,tend=0.01,solver=nothing)
    +    sys,X=create_porous_medium_problem(n,m)
    +    inival=unknowns(sys)
    +    inival[1,:].=map(x->barenblatt(x,t0,m),X)
    +    problem = ODEProblem(sys,inival,(t0,tend))
    +    odesol = solve(problem,solver)
    +    sol=reshape(odesol,sys)
    +    err=norm(sol[1,:,end]-map(x->barenblatt(x,tend,m),X))
    +    sol, sys,err
    +    end
    +for method in diffeqmethods
    +    run_diffeq(m=2,n=10,solver=method.second()) # "Precompile"
    +end
    +    end;
    + + + +
    OrderedDict{String, UnionAll} with 4 entries:
    +  "Rosenbrock23 (Rosenbrock)"    => Rosenbrock23
    +  "QNDF2 (Like matlab's ode15s)" => QNDF2
    +  "FBDF"                         => FBDF
    +  "Implicit Euler"               => ImplicitEuler
    + +
    t1=@elapsed sol1,sys1,err1=run_vfvm(m=m,n=n);history_summary(sys1)
    +
    (seconds = 1.06, tasm = 0.244, tlinsolve = 0.012, steps = 832, iters = 1662, maxabsnorm = 2.65e-6, maxrelnorm = 0.000263, maxroundoff = 2.46e-16, iters_per_step = 2.0, facts_per_step = 0.0, liniters_per_step = 0.0)
    + + +

    method:

    + +
    m=2; n=50;
    + + +
    t2=@elapsed sol2,sys2,err2=run_diffeq(m=m,n=n,solver=diffeqmethods[method]());history_summary(sys2)
    +
    (nd = 166, njac = 82, nf = 248)
    + + + + + +

    Left: VoronoiFVM implicit Euler: 1124 ms e=5.17e-02

    Right: Rosenbrock23 (Rosenbrock): 24 ms, e=4.62e-02

    + +
    @test err2<err1
    +
    Test Passed
    +
    +

    Built with Julia 1.10.2 and

    +CairoMakie 0.11.6
    +DataStructures 0.18.16
    +GridVisualize 1.5.0
    +OrdinaryDiffEq 6.58.2
    +Pkg 1.10.0
    +PlutoUI 0.7.55
    +Revise 3.5.13
    +VoronoiFVM 1.17.1 +
    + +
    diff --git a/v1.19.1/plutostatichtml_examples/ode-nlstorage1d/index.html b/v1.19.1/plutostatichtml_examples/ode-nlstorage1d/index.html new file mode 100644 index 000000000..baed910b0 --- /dev/null +++ b/v1.19.1/plutostatichtml_examples/ode-nlstorage1d/index.html @@ -0,0 +1,295 @@ + +OrdinaryDiffEq.jl changing mass matrix · VoronoiFVM.jl
    + + + +
    begin
    +    import Pkg as _Pkg
    +    haskey(ENV, "PLUTO_PROJECT") && _Pkg.activate(ENV["PLUTO_PROJECT"])
    +    using Test	
    +    using Revise
    +    using Printf
    +    using VoronoiFVM
    +    using OrdinaryDiffEq
    +    using LinearAlgebra
    +    using PlutoUI, HypertextLiteral,UUIDs
    +    using DataStructures
    +    using GridVisualize,CairoMakie
    +    CairoMakie.activate!(type="svg")
    +end
    + + +

    1D Nonlinear Storage

    +
    + +
    TableOfContents(aside=false)
    + + + +

    This equation comes from the transformation of the nonlinear diffusion equation

    $$\partial_t v - \Delta v^m = 0$$

    to

    $$\partial_t u^\frac{1}{m} -\Delta u = 0$$

    in $\Omega=(-1,1)$ with homogeneous Neumann boundary conditions. We can derive an exact solution from the Barenblatt solution of the equation for u.

    + +
    function barenblatt(x,t,m)
    +    tx=t^(-1.0/(m+1.0))
    +    xx=x*tx
    +    xx=xx*xx
    +    xx=1- xx*(m-1)/(2.0*m*(m+1));
    +    if xx<0.0
    +        xx=0.0
    +    end
    +    return tx*xx^(1.0/(m-1.0))
    +end
    +
    barenblatt (generic function with 1 method)
    + +
    begin
    +    const m=2
    +    const ε=1.0e-10
    +    const n=50
    +    const t0=1.0e-3
    +    const tend=1.0e-2
    +end
    +
    0.01
    + +
    X=collect(-1:2.0/n:1)
    +
    51-element Vector{Float64}:
    + -1.0
    + -0.96
    + -0.92
    + -0.88
    + -0.84
    + -0.8
    + -0.76
    +  ⋮
    +  0.8
    +  0.84
    +  0.88
    +  0.92
    +  0.96
    +  1.0
    + +
    u0=map(x->barenblatt(x,t0,m)^m,X)
    +
    51-element Vector{Float64}:
    + 0.0
    + 0.0
    + 0.0
    + 0.0
    + 0.0
    + 0.0
    + 0.0
    + ⋮
    + 0.0
    + 0.0
    + 0.0
    + 0.0
    + 0.0
    + 0.0
    + +
    begin
    +    grid=VoronoiFVM.Grid(X)
    +end
    +
    ExtendableGrids.ExtendableGrid{Float64, Int32};
    +dim: 1 nodes: 51 cells: 50 bfaces: 2
    +
    +
    + + +

    Direct implementation with VoronoiFVM

    + +
        function flux!(f,u,edge)
    +        f[1]=u[1,1]-u[1,2]
    +    end
    +
    flux! (generic function with 1 method)
    + + +

    Storage term needs to be regularized as its derivative at 0 is infinity:

    + +
        function storage!(f,u,node)
    +        f[1]=(ε+u[1])^(1.0/m)
    +    end
    +
    storage! (generic function with 1 method)
    + +
    begin
    +    physics=VoronoiFVM.Physics(
    +        flux=flux!,
    +        storage=storage!)
    +    sys=VoronoiFVM.System(grid,physics,species=1)
    +        inival=unknowns(sys)
    +    inival[1,:]=u0
    +    control=VoronoiFVM.SolverControl()
    +    tsol=VoronoiFVM.solve(sys;inival,times=(t0,tend),Δt_min=1.0e-4,Δt=1.0e-4,Δu_opt=0.1,force_first_step=true,log=true)
    +    summary(sys.history)
    +end
    +
    (seconds = 3.27, tasm = 1.14, tlinsolve = 0.0397, steps = 731, iters = 2920, maxabsnorm = 1.73e-12, maxrelnorm = 1.75e-11, maxroundoff = 9.19e-15, iters_per_step = 4.0, facts_per_step = 0.0, liniters_per_step = 0.0)
    + + +

    Implementation as DAE

    + + +

    If we want to solve the problem with DifferentialEquations.jl solvers, we see that the problem structure does not fit into the setting of that package due to the nonlinearity under the time derivative. Here we propose a reformulation to a DAE as a way to achieve this possibility:

    $$\begin{cases} + \partial_t w -\Delta u &= 0\\ + w^m - u &=0 +\end{cases}$$

    + +
    function dae_storage!(y,u,node)
    +    y[1]=u[2]
    +end
    +
    dae_storage! (generic function with 1 method)
    + +
    function dae_reaction!(y,u,node)
    +    y[2]= u[2]^m-u[1]
    +end
    +
    dae_reaction! (generic function with 1 method)
    + + +

    First, we test this with the implicit Euler method of VoronoiFVM

    + +
    begin
    +    dae_physics=VoronoiFVM.Physics(
    +        flux=flux!,
    +       storage=dae_storage!,
    +        reaction=dae_reaction!
    +    )
    +    dae_sys=VoronoiFVM.System(grid,dae_physics,species=[1,2])
    +    dae_inival=unknowns(dae_sys)
    +    dae_inival[1,:].=u0
    +    dae_inival[2,:].=u0.^(1/m)
    +    dae_control=VoronoiFVM.SolverControl()
    +    dae_tsol=VoronoiFVM.solve(dae_sys;inival=dae_inival,times=(t0,tend),Δt_min=1.0e-4,Δt=1.0e-4,Δu_opt=0.1,force_first_step=true,log=true)
    +    summary(dae_sys.history)
    +end
    +
    (seconds = 2.27, tasm = 0.677, tlinsolve = 0.0343, steps = 732, iters = 2205, maxabsnorm = 9.72e-11, maxrelnorm = 9.82e-10, maxroundoff = 6.99e-13, iters_per_step = 3.02, facts_per_step = 0.0, liniters_per_step = 0.0)
    + + +

    Implementation via OrdinaryDiffEq.jl

    + + +
    OrderedDict{String, UnionAll} with 5 entries:
    +  "QNDF2 (Like matlab's ode15s)" => QNDF2
    +  "Rodas5"                       => Rodas5
    +  "Rosenbrock23 (Rosenbrock)"    => Rosenbrock23
    +  "FBDF"                         => FBDF
    +  "Implicit Euler"               => ImplicitEuler
    + + +

    method:

    + +
    begin
    +    de_sys=VoronoiFVM.System(grid,dae_physics,species=[1,2])
    +    problem = ODEProblem(de_sys,dae_inival,(t0,tend))
    +    de_odesol=solve(problem,
    +        diffeqmethods[method](),
    +        adaptive=true,
    +        reltol=1.0e-3,
    +        abstol=1.0e-3,
    +        initializealg=NoInit()
    +        )          
    +        de_tsol=reshape(de_odesol,de_sys)
    +end;
    + + + + + + +

    t=0.0019999

    + +
    exact=map(x->barenblatt(x,tend,m).^m,X)
    +
    51-element Vector{Float64}:
    + 0.0
    + 0.0
    + 0.0
    + 0.0
    + 0.0
    + 0.0
    + 0.0
    + ⋮
    + 0.0
    + 0.0
    + 0.0
    + 0.0
    + 0.0
    + 0.0
    + +
    @test norm(tsol[1,:,end]-exact,Inf)<0.1
    +
    Test Passed
    + +
    @test norm(dae_tsol[1,:,end]-exact,Inf)<0.1
    +
    Test Passed
    + +
     @test norm(de_tsol[1,:,end]-exact,Inf)<0.05
    +
    Test Passed
    + + +
    myaside (generic function with 1 method)
    + +
    function plotsolutions()
    +    vis=GridVisualizer(resolution=(380,200),dim=1,Plotter=CairoMakie,legend=:lt);
    +    u=tsol(t)
    +    u_dae=dae_tsol(t)
    +    u_de=de_tsol(t)
    +    scalarplot!(vis,X,map(x->barenblatt(x,t,m).^m,X),clear=true,color=:red,linestyle=:solid,flimits=(0,100),label="exact")
    +    scalarplot!(vis,grid,u_dae[1,:],clear=false,color=:green, linestyle=:solid,label="vfvm_dae")
    +    scalarplot!(vis,grid,u_de[1,:],clear=false,color=:blue, markershape=:cross,linestyle=:dot,label="vfvm_diffeq")
    +    scalarplot!(vis,grid,u[1,:],clear=false,color=:black,markershape=:none, linestyle=:dash,title="t=$(t)",label="vfvm_default")
    +    reveal(vis)
    +end
    +
    plotsolutions (generic function with 1 method)
    +
    +

    Built with Julia 1.10.2 and

    +CairoMakie 0.11.6
    +DataStructures 0.18.16
    +GridVisualize 1.5.0
    +HypertextLiteral 0.9.5
    +OrdinaryDiffEq 6.66.0
    +Pkg 1.10.0
    +PlutoUI 0.7.55
    +Revise 3.5.13
    +VoronoiFVM 1.17.1 +
    + +
    diff --git a/v1.19.1/plutostatichtml_examples/ode-wave1d/index.html b/v1.19.1/plutostatichtml_examples/ode-wave1d/index.html new file mode 100644 index 000000000..222d78759 --- /dev/null +++ b/v1.19.1/plutostatichtml_examples/ode-wave1d/index.html @@ -0,0 +1,290 @@ + +OrdinaryDiffEq.jl 1D wave equation · VoronoiFVM.jl
    + + + +
    begin
    +    import Pkg as _Pkg
    +    haskey(ENV, "PLUTO_PROJECT") && _Pkg.activate(ENV["PLUTO_PROJECT"])
    +    using Test
    +    using Revise
    +    using Printf
    +    using VoronoiFVM
    +    using OrdinaryDiffEq
    +    using LinearAlgebra
    +    using PlutoUI
    +    using ExtendableGrids
    +    using DataStructures
    +    using GridVisualize,CairoMakie
    +    CairoMakie.activate!(type="svg")
    +end
    + + +

    The wave equation as system of equations

    +
    + + +

    This is the n-dimensional wave equation:

    $$u_{tt}- c^2 \Delta u = 0$$

    We can create a system of first oder in time PDEs out of this:

    $$ \begin{aligned} + u_t - v&=0\\ + v_t -c^2\Delta u&=0 +\end{aligned}$$

    This allows for a quick implementation in VoronoiFVM (which may be not the optimal way, in particular with respect to time discretization).

    + +
    const iu=1; const iv=2;
    + + +
    storage(y,u,node,data)=y.=u;
    + + +
    reaction(y,u,node,data)= y[iu]=-u[iv];
    + + +
    flux(y,u,edge,data)=y[iv]=data.c^2*(u[iu,1]-u[iu,2]);
    + + + +

    Implementation of transparent or mirror bc

    + +
    function brea(y,u,node,data)
    +    if node.region==2 
    +        if data.bctype==:transparent
    +         	y[iu]=data.c*u[iu]
    +        elseif data.bctype==:mirror
    +            boundary_dirichlet!(y,u,node,species=1,region=2,value=0)
    +        end
    +    end
    +end;
    +
    + + + +

    Wave velocity:

    + +
    const c=0.1
    +
    0.1
    + + +

    Domain length:

    + +
    L=4
    +
    4
    + +
    N=151
    +
    151
    + +
    dt=1.0e-2; tend=100.0;
    + + +
    grid=simplexgrid(range(-L,L,length=N));
    + + +
    sys=VoronoiFVM.System(grid,storage=storage,flux=flux,breaction=brea, reaction=reaction,data=(c=c,bctype=Symbol(bc2type)), species=[1,2])
    +
    VoronoiFVM.System{Float64, Float64, Int32, Int64, Matrix{Int32}, Matrix{Float64}}(num_species=2)
    +
    + + +

    Perturbation in the center of the domain:

    + +
    begin
    +   inival=unknowns(sys,inival=0)	
    +   inival[1,:].=map(x->cos(κ*π*x)*exp(-x^2/0.1) ,grid)	
    +end;
    + + +
    problem = ODEProblem(sys,inival,(0.0,tend));
    + + +
    tsol=solve(problem,diffeqmethods[method]();  
    +                                   force_dtmin=true,
    +                                   adaptive=true,
    +                                   reltol=1.0e-4,
    +                                   abstol=1.0e-5,
    +                                   dtmin=dt,
    +                                   progress=true,
    +                                   progress_steps=1,
    +                                   dt=dt);
    + + + +

    Boundary condition at x=L:

    + + +

    Reflection (Neumann) bc $\partial_x u|_{x=L}=0$

    + + +

    Package wave number κ: method:

    t=49.95

    + +
    let
    +    u=tsol1(t)
    +    scalarplot(grid,u[1,:],flimits=(-1,1),clear=true,show=true,title="t=$(t)",Plotter=CairoMakie,resolution=(600,150))
    +end
    +
    +
    + + +
    let 
    +    vis=GridVisualizer(Plotter=CairoMakie)
    +    scalarplot!(vis,sys,tsol1,colormap=:bwr,limits=(-1,1), levels=(-0.9:0.2:0.9))
    +    reveal(vis)
    +end
    + + +
    tsol1=reshape(tsol,sys);
    + + +
    diffeqmethods=OrderedDict(
    +"QNDF2" =>  QNDF2,
    +"FBDF" => FBDF,
    +"Rosenbrock23 (Rosenbrock)" => Rosenbrock23,
    +"Implicit Euler" => ImplicitEuler,
    +"Implicit Midpoint" => ImplicitMidpoint,
    +)
    +
    OrderedDict{String, UnionAll} with 5 entries:
    +  "QNDF2"                     => QNDF2
    +  "FBDF"                      => FBDF
    +  "Rosenbrock23 (Rosenbrock)" => Rosenbrock23
    +  "Implicit Euler"            => ImplicitEuler
    +  "Implicit Midpoint"         => ImplicitMidpoint
    +
    +

    Built with Julia 1.10.2 and

    +CairoMakie 0.11.9
    +DataStructures 0.18.17
    +ExtendableGrids 1.3.1
    +GridVisualize 1.5.0
    +OrdinaryDiffEq 6.74.0
    +Pkg 1.10.0
    +PlutoUI 0.7.58
    +Revise 3.5.14
    +VoronoiFVM 1.19.1 +
    + +
    diff --git a/v1.19.1/plutostatichtml_examples/outflow/index.html b/v1.19.1/plutostatichtml_examples/outflow/index.html new file mode 100644 index 000000000..8fd4d2c73 --- /dev/null +++ b/v1.19.1/plutostatichtml_examples/outflow/index.html @@ -0,0 +1,278 @@ + +Outflow boundary conditions · VoronoiFVM.jl
    + + + +
    begin
    +    import Pkg as _Pkg
    +    haskey(ENV, "PLUTO_PROJECT") && _Pkg.activate(ENV["PLUTO_PROJECT"])
    +    using Revise
    +    using Test
    +    using VoronoiFVM
    +    using GridVisualize
    +    using CairoMakie
    +    CairoMakie.activate!(; type = "svg", visible = false)
    +    GridVisualize.default_plotter!(CairoMakie)
    +    using SimplexGridFactory, Triangulate
    +    using ExtendableGrids
    +    using PlutoUI, HypertextLiteral, UUIDs
    +end
    + + + +

    Outflow boundary conditions

    Source

    + + +

    We show how to implment outflow boundary conditions when the velocities at the boundary are calculated by another equation in the system. A typical case is solute transport in porous media where fluid flow is calculated by Darcy's law which defines the convective velocity in the solute transport equation.

    + + +

    Regard the following system of equations in domain $\Omega\subset \mathbb R^d$:

    $$\begin{aligned} + \nabla \cdot \vec v &=0\\ + \vec v&=-k\nabla p\\ + \nabla \cdot \vec j &=0\\ + \vec j&= - D\nabla c - c\vec v +\end{aligned}$$

    The variable p can be seen as a the pressure of a fluid in porous medium. c is the concentration of a transported species.

    We subdivide the boundary: $\partial\Omega=Γ_{in}\cup Γ_{out}\cup Γ_{noflow}$ abs set

    $$\begin{aligned} + p=&1 \quad & c&=c_{in} & \text{on}\quad Γ_{in}\\ + p=&0 \quad & \vec j\cdot \vec n &= c\vec v \cdot \vec n & \text{on}\quad Γ_{out}\\ + \vec v\cdot \vec n &=0 \quad & \vec j\cdot \vec n &= 0 & \text{on}\quad Γ_{noflow}\\ +\end{aligned}$$

    + +

    Discretization data

    +
    + +
    Base.@kwdef struct FlowTransportData
    +    k = 1.0
    +    v_in = 1.0
    +    c_in = 0.5
    +    D = 1.0
    +    Γ_in = 1
    +    Γ_out = 2
    +    ip = 1
    +    ic = 2
    +end
    +
    FlowTransportData
    + +
    X = 0:0.1:1
    +
    0.0:0.1:1.0
    + +
    darcyvelo(u, data) = data.k * (u[data.ip, 1] - u[data.ip, 2])
    +
    darcyvelo (generic function with 1 method)
    + +
    function flux(y, u, edge, data)
    +    vh = darcyvelo(u, data)
    +    y[data.ip] = vh
    +
    +    bp, bm = fbernoulli_pm(vh / data.D)
    +    y[data.ic] = data.D * (bm * u[data.ic, 1] - bp * u[data.ic, 2])
    +end
    +
    flux (generic function with 1 method)
    + +
    function bcondition(y, u, bnode, data)
    +    boundary_neumann!(y, u, bnode; species = data.ip, region = data.Γ_in, value = data.v_in)
    +    boundary_dirichlet!(y, u, bnode; species = data.ip, region = data.Γ_out, value = 0)
    +    boundary_dirichlet!(y, u, bnode; species = data.ic, region = data.Γ_in, value = data.c_in)
    +end
    +
    bcondition (generic function with 1 method)
    + + +

    This function describes the outflow boundary condition. It is called on edges (including interior ones) which have at least one ode on one of the outflow boundaries. Within this function outflownode can be used to identify that node. There is some ambiguity in the case that both nodes are outflow nodes, in that case it is assumed that the contribution is zero. In the present case this is guaranteed by the constant Dirichlet boundary condition for the pressure.

    + +
    function boutflow(y, u, edge, data)
    +    y[data.ic] = -darcyvelo(u, data) * u[data.ic, outflownode(edge)]
    +end
    +
    boutflow (generic function with 1 method)
    + +
    function flowtransportsystem(grid; kwargs...)
    +    data = FlowTransportData(; kwargs...)
    +    VoronoiFVM.System(grid;
    +                      flux,
    +                      bcondition,
    +                      boutflow,
    +                      data,
    +                      outflowboundaries = [data.Γ_out],
    +                      species = [1, 2],)
    +end
    +
    flowtransportsystem (generic function with 1 method)
    + +
    function checkinout(sys, sol)
    +    data = sys.physics.data
    +    tfact = TestFunctionFactory(sys)
    +    tf_in = testfunction(tfact, [data.Γ_out], [data.Γ_in])
    +    tf_out = testfunction(tfact, [data.Γ_in], [data.Γ_out])
    +    (; in = integrate(sys, tf_in, sol), out = integrate(sys, tf_out, sol))
    +end
    +
    checkinout (generic function with 1 method)
    + +

    1D Case

    +
    + +
    grid = simplexgrid(X)
    +
    ExtendableGrids.ExtendableGrid{Float64, Int32};
    +dim: 1 nodes: 11 cells: 10 bfaces: 2
    +
    +
    + +
    sys1 = flowtransportsystem(grid);
    + + +
    sol1 = solve(sys1; verbose = "n");
    + + + + + +
    t1 = checkinout(sys1, sol1)
    +
    (in = [1.0, 0.5000000000000002], out = [-1.0, -0.5000000000000002])
    + +
    @test t1.in ≈ -t1.out
    +
    Test Passed
    + +
    @test maximum(sol1[2, :]) ≈ 0.5
    +
    Test Passed
    + +
    @test minimum(sol1[2, :]) ≈ 0.5
    +
    Test Passed
    + +

    2D Case

    +
    + +
    begin
    +    g2 = simplexgrid(X, X)
    +    bfacemask!(g2, [1, 0.3], [1, 0.7], 5)
    +end
    +
    ExtendableGrids.ExtendableGrid{Float64, Int32};
    +dim: 2 nodes: 121 cells: 200 bfaces: 40
    +
    +
    + +
    gridplot(g2; size = (300, 300))
    + + +
    sys2 = flowtransportsystem(g2; Γ_in = 4, Γ_out = 5);
    + + +
    sol2 = solve(sys2; verbose = "n")
    +
    2×121 Matrix{Float64}:
    + 1.12521  1.02537  0.925877  0.826948  …  0.453027  0.377299  0.321519  0.298904
    + 0.5      0.5      0.5       0.5          0.5       0.5       0.5       0.5
    + +
    let
    +    vis = GridVisualizer(; size = (700, 300), layout = (1, 2))
    +    scalarplot!(vis[1, 1], g2, sol2[1, :])
    +    scalarplot!(vis[1, 2], g2, sol2[2, :]; limits = (0, 1))
    +    reveal(vis)
    +end
    + + +
    t2 = checkinout(sys2, sol2)
    +
    (in = [1.0000000000000013, 0.5000000000000006], out = [-1.0000000000000007, -0.5000000000000001])
    + +
    @test t2.in ≈ -t2.out
    +
    Test Passed
    + +
    @test maximum(sol2[2, :]) ≈ 0.5
    +
    Test Passed
    + +
    @test minimum(sol2[2, :]) ≈ 0.5
    +
    Test Passed
    + +

    3D Case

    +
    + +
    begin
    +    g3 = simplexgrid(X, X, X)
    +    bfacemask!(g3, [0.3, 0.3, 0], [0.7, 0.7, 0], 7)
    +end
    +
    ExtendableGrids.ExtendableGrid{Float64, Int32};
    +dim: 3 nodes: 1331 cells: 6000 bfaces: 1200
    +
    +
    + +
    gridplot(g3; size = (300, 300))
    + + +
    sys3 = flowtransportsystem(g3; Γ_in = 6, Γ_out = 7);
    + + +
    sol3 = solve(sys3; verbose = "n")
    +
    2×1331 Matrix{Float64}:
    + 0.547438  0.539229  0.516946  0.488884  …  1.32867  1.32914  1.32951  1.32966
    + 0.5       0.5       0.5       0.5          0.5      0.5      0.5      0.5
    + +
    let
    +    vis = GridVisualizer(; size = (700, 300), layout = (1, 2))
    +    scalarplot!(vis[1, 1], g3, sol3[1, :])
    +    scalarplot!(vis[1, 2], g3, sol3[2, :]; limits = (0, 1))
    +    reveal(vis)
    +end
    + + +
    t3 = checkinout(sys3, sol3)
    +
    (in = [1.000000000000037, 0.5000000000000188], out = [-1.0000000000000389, -0.5000000000000194])
    + +
    @test t3.in ≈ -t3.out
    +
    Test Passed
    + +
    @test maximum(sol3[2, :]) ≈ 0.5
    +
    Test Passed
    + +
    @test minimum(sol3[2, :]) ≈ 0.5
    +
    Test Passed
    + + +
    + + + + + + + + + + + + + + + +
    +

    Built with Julia 1.10.2 and

    +CairoMakie 0.10.12
    +ExtendableGrids 1.2.3
    +GridVisualize 1.1.7
    +HypertextLiteral 0.9.5
    +Pkg 1.10.0
    +PlutoUI 0.7.55
    +Revise 3.5.13
    +SimplexGridFactory 0.5.20
    +Triangulate 2.3.2
    +VoronoiFVM 1.16.0 +
    + +
    diff --git a/v1.19.1/plutostatichtml_examples/problemcase/index.html b/v1.19.1/plutostatichtml_examples/problemcase/index.html new file mode 100644 index 000000000..1b16f50f3 --- /dev/null +++ b/v1.19.1/plutostatichtml_examples/problemcase/index.html @@ -0,0 +1,436 @@ + +A case for caution · VoronoiFVM.jl
    + + + + +

    Some problems with Voronoi FVM

    Source

    Draft. J. Fuhrmann, Oct. 29. 2021. Updated Dec 19, 2021.

    We discuss one of the critical cases for application the Voronoi finite volume method. We provide some practical fix and opine that the finite element method probably has the same problems.

    + + + + +
    begin
    +    import Pkg as _Pkg
    +    haskey(ENV, "PLUTO_PROJECT") && _Pkg.activate(ENV["PLUTO_PROJECT"])
    +    using Revise
    +    using Test
    +    using VoronoiFVM
    +    using ExtendableGrids
    +    using PlutoUI
    +    using GridVisualize
    +    using CairoMakie
    +    CairoMakie.activate!(; type = "svg", visible = false)
    +    GridVisualize.default_plotter!(CairoMakie)
    +end;
    + + +

    Transient problem

    +

    This problem was suggested by R. Eymard.

    + + +

    Regard the following problem coupling Darcy's equation with Fick's law and transport:

    + + +

    $$ \begin{aligned} + \vec v &= k \nabla p \\ + \nabla \cdot \vec v &= 0\\ + \partial_t (\phi c) - \nabla \cdot (D\nabla c + c \vec v) &= 0 + \end{aligned}$$

    + + +

    The domain is described by the following discretization grid:

    + + + + + +

    In the center of the domain, we assume a layer with high permeability.

    As boundary conditions for the pressure $p$ we choose fixed pressure values at the left and right boundaries of the domain, triggering a constant pressure gradient throughout the domain.

    At the inlet of the high permeability layer, we set $c=1$, and at the outlet, we set $c=0$.

    The high permeability layer has length L=10 and width W= 0.5.

    We solve the time dependent problem on three types of rectangular grids with the same resolution in $x$ direction and different variants to to handle the high permeability layer.

    • grid_n - a "naive" grid which just resolves the permeability layer and the surrounding material with equally spaced (in y direction) grids

    • grid_1 - a 1D grid of the high permeability layer. With high permeability contrast, the solution of the 2D case at y=0 should coincide with the 1D solution

    • grid_f - a "fixed" 2D grid which resolves the permeability layer and the surrounding material with equally spaced (in y direction) grids and "protection layers" of width ε_fix=0.0001 correcting the size of high permeability control volumes

    + + + + + +

    Results

    In the calculations, we ramp up the inlet concentration and measure the amount of solute flowing through the outlet - the breaktrough curve.

    + +
    nref = 1
    +
    1
    + +
    tend = 100
    +
    100
    + +
    ε_fix = 1.0e-4
    +
    0.0001
    + +
    grid_n, sol_n, bt_n = trsolve(grid_2d(; nref = nref); tend = tend);
    + + +
    sum(bt_n)
    +
    18.143158169851787
    + +
    @test sum(bt_n) ≈ 18.143158169851787
    +
    Test Passed
    + +
    grid_1, sol_1, bt_1 = trsolve(grid_1d(; nref = nref); tend = tend);
    + + +
    @test sum(bt_1) ≈ 20.66209910195916
    +
    Test Passed
    + +
    grid_f, sol_f, bt_f = trsolve(grid_2d(; nref = nref, ε_fix = ε_fix); tend = tend);
    + + +
    @test sum(bt_f) ≈ 20.661131375044135
    +
    Test Passed
    + +
    grid_ϕ, sol_ϕ, bt_ϕ = trsolve(grid_2d(; nref = nref); ϕ = [1.0e-3, 1], tend = tend);
    + + +
    @test sum(bt_ϕ) ≈ 20.412256299447236
    +
    Test Passed
    + +
    begin
    +    p1 = GridVisualizer(; resolution = (500, 200),
    +                        xlabel = "t",
    +                        ylabel = "outflow",
    +                        legend = :rb,
    +                        title = "Breakthrough Curves")
    +    scalarplot!(p1, sol_n.t, bt_n; label = "naive grid", color = :red)
    +    scalarplot!(p1,
    +                sol_1.t,
    +                bt_1;
    +                label = "1D grid",
    +                markershape = :x,
    +                markersize = 10,
    +                clear = false,
    +                color = :green)
    +    scalarplot!(p1,
    +                sol_f.t,
    +                bt_f;
    +                label = "grid with fix",
    +                markershape = :circle,
    +                color = :green,
    +                clear = false)
    +    scalarplot!(p1,
    +                sol_ϕ.t,
    +                bt_ϕ;
    +                label = "modified ϕ",
    +                markershape = :cross,
    +                color = :blue,
    +                clear = false)
    +    reveal(p1)
    +end
    + + + +

    Here, we plot the solutions for the grid_n case and the grid_f case.

    + + + + + +

    Time: 10.0

    + +
    scalarplot(grid_n, sol_n(t)[ic, :]; resolution = (500, 200), show = true)
    + + +
    scalarplot(grid_f, sol_f(t)[ic, :]; resolution = (500, 200), show = true)
    + + +

    Reaction-Diffusion problem

    +

    Here we solve the following problem:

    $$ -\nabla D \nabla u + R u = 0$$

    where D is large in the high permeability region and small otherwise. R is a constant.

    + + +

    Results

    + +
    rdgrid_1, rdsol_1, of_1 = rdsolve(grid_1d(; nref = nref));
    + + +
    @test of_1 ≈ -0.013495959676585267
    +
    Test Passed
    + +
    rdgrid_n, rdsol_n, of_n = rdsolve(grid_2d(; nref = nref));
    + + +
    @test of_n ≈ -0.00023622450350365264
    +
    Test Passed
    + +
    rdgrid_f, rdsol_f, of_f = rdsolve(grid_2d(; nref = nref, ε_fix = ε_fix));
    + + +
    @test of_f ≈ -0.013466874615165499
    +
    Test Passed
    + +
    rdgrid_r, rdsol_r, of_r = rdsolve(grid_2d(; nref = nref); R = [0, 0.1]);
    + + +
    @test of_r ≈ -0.013495959676764535
    +
    Test Passed
    + + +

    We measure the outflow at the outlet. As a result, we obtain:

    • 1D case: -0.013495959676585255

    • 2D case, naive grid: -0.00023622450350365272

    • 2D case, grid with "protective layer": -0.013466874615165514

    • 2D case, naive grid, "modified" R: -0.013495959676764539

    + +
    scalarplot(rdgrid_1, rdsol_1; resolution = (300, 200))
    + + +
    scalarplot(rdgrid_n, rdsol_n; resolution = (500, 200))
    + + +
    scalarplot(rdgrid_f, rdsol_f; resolution = (500, 200))
    + + +

    Discussion

    +

    Transient case

    As there will be nearly no flow in y-direction, we should get the very same results in all four cases for small permeability values in the low permeability region.

    In the grid_n case, the heterogeneous control volumina ovrestimate the storage capacity which shows itself in a underestimation of the transferred solute.

    With the high permeability contrast, the results for heterogeneous domain should be essentially equal to those for 1D domain. However, with a coarse resolution in y-direction, we see large differences in the transient behaviour of the breaktrough curve compared to the 1D case. The introduction of a thin protection layer leads to reasonable results.

    Alternatively, the porosity of the low permeability region can be modified. Arguably, this is the case in practice, see e.g. Ackerer et al, Transport in Porous Media35:345–373, 1999 (eq. 7).

    Reaction diffusion case

    In this case, we look at a homogeneous reaction in a domain divided into a high and low diffusion region. With high contrast in the diffusion coefficients, the reasonable assumption is that the reaction takes place only in the high diffusion region, and the un-consumed share of species leaves at the outlet.

    In this case we observe a similar related problem which can be fixed by adding a thin layer of control volumes at the boundary. No problem occurs if the reaction rate at in the low diffusion region is zero.

    Conclusion

    Here, we indeed observe problem with the Voronoi approach: care must be taken to handle the case of hetero interfaces in connection with transient processes and/or homogeneous reactions. In these cases it should be analyzed if the problem occurs, and why, and it appears, that the discussion should not be had without reference to the correct physical models. A remedy based on meshing is available at least for straight interfaces.

    Opinion

    With standard ways of using finite elements, the issue described here will occur in a similar way, so the discussion is indeed with the alternative "cell centered" finite volume approach which places interfaces at the boundaries of the control volumes rather than along the edges of a underlying triangulation.

    Drawbacks of two point flux Voronoi methods based on simplicial meshes (as tested here):

    • Anisotropic diffusion is only correct with aligned meshes

    • Reliance on boundary conforming Delaunay property of the underlying mesh, thus narrowing the available meshing strategies

    • The issue described in the present notebook. However, in both cases discussed here, IMHO it might "go away" depending on the correct physics. There should be more discussions with relevant application problems at hand.

    Advantages (compared to the cell centered approach placing collocation points away from interfaces)

    • Availability of P1 interpolant on simplices for visualization, interpolation, coupling etc.

    • Mesh generators tend to place interfaces at triangle edges.

    • Dirichlet BC can be applied exactly

    • There is a straightforward way to link interface processes with bulk processes, e.g. an adsorption reaction is easily described by a reaction term at the boundary which involves interface and bulk value available at the same mesh node.

    + +

    Appendix

    +
    + + +

    Domain data

    + + +

    Sizes:

    + +
    begin
    +    L = 10   # length of the high perm layer
    +    W = 0.5  # width of high perm layer
    +    Wlow = 2 # width of adjacent low perm layers
    +end;
    + + + +

    Boundary conditions:

    + +
    begin
    +    const Γ_top = 3
    +    const Γ_bot = 1
    +    const Γ_left = 4
    +    const Γ_right = 2
    +    const Γ_in = 5
    +    const Γ_out = 2
    +end;
    + + +
    begin
    +    Ω_low = 1
    +    Ω_high = 2
    +end;
    + + +
    function grid_2d(; nref = 0, ε_fix = 0.0)
    +    nx = 10 * 2^nref
    +    ny = 1 * 2^nref
    +    nylow = 3 * 2^nref
    +    xc = linspace(0, L, nx + 1)
    +    y0 = linspace(-W / 2, W / 2, ny + 1)
    +    if ε_fix > 0.0
    +        yfix = [W / 2, W / 2 + ε_fix]
    +        ytop = glue(yfix, linspace(yfix[end], Wlow, nylow + 1))
    +    else
    +        ytop = linspace(W / 2, Wlow, nylow + 1)
    +    end
    +    yc = glue(-reverse(ytop), glue(y0, ytop))
    +    grid = simplexgrid(xc, yc)
    +    cellmask!(grid, [0, -W / 2], [L, W / 2], Ω_high)
    +    bfacemask!(grid, [0, -W / 2], [0, W / 2], Γ_in)
    +    bfacemask!(grid, [L, -W / 2], [L, W / 2], Γ_out)
    +end
    +
    grid_2d (generic function with 1 method)
    + +
    function grid_1d(; nref = 0)
    +    nx = 10 * 2^nref
    +    xc = linspace(0, L, nx + 1)
    +    grid = simplexgrid(xc)
    +    cellmask!(grid, [0], [L], Ω_high)
    +    bfacemask!(grid, [0], [0], Γ_in)
    +    bfacemask!(grid, [L], [L], Γ_out)
    +    grid
    +end
    +
    grid_1d (generic function with 1 method)
    + + +

    Transient solver

    + + +

    Pressure index in solution

    + +
    const ip = 1;
    + + + +

    Concentration index in solution

    + +
    const ic = 2;
    + + + +

    Generate breaktrough courve from transient solution

    + +
    function breakthrough(sys, tf, sol)
    +    of = similar(sol.t)
    +    t = sol.t
    +    of[1] = 0
    +    for i = 2:length(sol.t)
    +        of[i] = -integrate(sys, tf, sol[i], sol[i - 1], t[i] - t[i - 1])[ic]
    +    end
    +    of
    +end
    +
    breakthrough (generic function with 1 method)
    + + +

    Transient solver:

    + +
    function trsolve(grid;
    +                 κ = [1.0e-3, 5],
    +                 D = [1.0e-12, 1.0e-12],
    +                 Δp = 1.0,
    +                 ϕ = [1, 1],
    +                 tend = 100,)
    +    function flux(y, u, edge)
    +        y[ip] = κ[edge.region] * (u[ip, 1] - u[ip, 2])
    +        bp, bm = fbernoulli_pm(y[ip] / D[edge.region])
    +        y[ic] = D[edge.region] * (bm * u[ic, 1] - bp * u[ic, 2])
    +    end
    +
    +    function stor(y, u, node)
    +        y[ip] = 0
    +        y[ic] = ϕ[node.region] * u[ic]
    +    end
    +
    +    dim = dim_space(grid)
    +    function bc(y, u, bnode)
    +        c0 = ramp(bnode.time; dt = (0, 0.001), du = (0, 1))
    +        boundary_dirichlet!(y, u, bnode, ic, Γ_in, c0)
    +        boundary_dirichlet!(y, u, bnode, ic, Γ_out, 0)
    +
    +        boundary_dirichlet!(y, u, bnode, ip, Γ_in, Δp)
    +        boundary_dirichlet!(y, u, bnode, ip, Γ_out, 0)
    +        if dim > 1
    +            boundary_dirichlet!(y, u, bnode, ip, Γ_left, Δp)
    +            boundary_dirichlet!(y, u, bnode, ip, Γ_right, 0)
    +        end
    +    end
    +
    +    sys = VoronoiFVM.System(grid;
    +                            check_allocs = true,
    +                            flux = flux,
    +                            storage = stor,
    +                            bcondition = bc,
    +                            species = [ip, ic],)
    +
    +    inival = solve(sys; inival = 0, time = 0.0)
    +    factory = TestFunctionFactory(sys)
    +    tfc = testfunction(factory, [Γ_in, Γ_left, Γ_top, Γ_bot], [Γ_out])
    +
    +    sol = VoronoiFVM.solve(sys;
    +                           inival = inival,
    +                           times = [0, tend],
    +                           Δt = 1.0e-4,
    +                           Δt_min = 1.0e-6,)
    +
    +    bt = breakthrough(sys, tfc, sol)
    +    if dim == 1
    +        bt = bt * W
    +    end
    +
    +    grid, sol, bt
    +end
    +
    trsolve (generic function with 1 method)
    + + +

    Reaction-Diffusion solver

    + +
    function rdsolve(grid; D = [1.0e-12, 1.0], R = [1, 0.1])
    +    function flux(y, u, edge)
    +        y[1] = D[edge.region] * (u[1, 1] - u[1, 2])
    +    end
    +
    +    function rea(y, u, node)
    +        y[1] = R[node.region] * u[1]
    +    end
    +    function bc(args...)
    +        boundary_dirichlet!(args..., 1, Γ_in, 1)
    +        boundary_dirichlet!(args..., 1, Γ_out, 0)
    +    end
    +    sys = VoronoiFVM.System(grid;
    +                            flux = flux,
    +                            reaction = rea,
    +                            species = 1,
    +                            bcondition = bc,
    +                            check_allocs = true)
    +    dim = dim_space(grid)
    +
    +    sol = VoronoiFVM.solve(sys)
    +    factory = TestFunctionFactory(sys)
    +    tf = testfunction(factory, [Γ_in, Γ_left, Γ_top, Γ_bot], [Γ_out])
    +    of = integrate(sys, tf, sol)
    +    fac = 1.0
    +    if dim == 1
    +        fac = W
    +    end
    +    grid, sol[1, :], of[1] * fac
    +end
    +
    rdsolve (generic function with 1 method)
    + + +
    + + +
    +
    +

    Built with Julia 1.10.2 and

    +CairoMakie 0.11.5
    +ExtendableGrids 1.2.3
    +GridVisualize 1.5.0
    +Pkg 1.10.0
    +PlutoUI 0.7.55
    +Revise 3.5.13
    +VoronoiFVM 1.16.0 +
    + +
    diff --git a/v1.19.1/post/index.html b/v1.19.1/post/index.html new file mode 100644 index 000000000..bbafdc5d1 --- /dev/null +++ b/v1.19.1/post/index.html @@ -0,0 +1,96 @@ + +Postprocessing · VoronoiFVM.jl

    Postprocessing

    Plotting

    Plotting can be performed using the package GridVisualize.jl. This package extends the API with a couple of methods:

    GridVisualize.scalarplotFunction
    scalarplot(
    +    sys::VoronoiFVM.AbstractSystem,
    +    sol::AbstractMatrix;
    +    species,
    +    scale,
    +    kwargs...
    +) -> Any
    +

    Plot one species from solution

    source
    scalarplot(
    +    sys::VoronoiFVM.AbstractSystem,
    +    sol::VoronoiFVM.AbstractTransientSolution;
    +    species,
    +    scale,
    +    tscale,
    +    kwargs...
    +) -> Any
    +

    Plot one species from transient solution

    source
    GridVisualize.scalarplot!Function
    scalarplot!(
    +    vis,
    +    sys::VoronoiFVM.AbstractSystem,
    +    sol::AbstractMatrix;
    +    species,
    +    scale,
    +    kwargs...
    +) -> Any
    +

    Plot one species from solution

    source
    scalarplot!(
    +    vis,
    +    sys::VoronoiFVM.AbstractSystem,
    +    sol::VoronoiFVM.AbstractTransientSolution;
    +    species,
    +    scale,
    +    tscale,
    +    tlabel,
    +    kwargs...
    +) -> Any
    +

    Plot one species from transient solution

    source
    VoronoiFVM.plothistoryFunction
    plothistory(tsol; 
    +            plots=[:timestepsizes,
    +                   :timestepupdates,
    +                   :newtonsteps,
    +                   :newtonupdates], 
    +            size=(700,600), 
    +            logmin=1.0e-20,
    +            Plotter=GridVisualize.default_plotter(),
    +            kwargs...)

    Plot solution history stored in tsol. The plots argument allows to choose which plots are shown.

    source

    Norms & volumes

    LinearAlgebra.normFunction
    norm(system, u)
    +norm(system, u, p)
    +

    Calculate Euklidean norm of the degree of freedom vector.

    source
    VoronoiFVM.lpnormFunction
    lpnorm(sys, u, p)
    +lpnorm(sys, u, p, species_weights)
    +

    Calculate weighted discrete $L^p$ norm of a solution vector.

    source
    VoronoiFVM.l2normFunction
    l2norm(sys, u)
    +l2norm(sys, u, species_weights)
    +

    Calculate weigthed discrete $L^2(\Omega)$ norm of a solution vector.

    source
    VoronoiFVM.w1pseminormFunction
    w1pseminorm(sys, u, p)
    +w1pseminorm(sys, u, p, species_weights)
    +

    Calculate weighted discrete $W^{1,p}(\Omega)$ seminorm of a solution vector.

    source
    VoronoiFVM.h1seminormFunction
    h1seminorm(sys, u)
    +h1seminorm(sys, u, species_weights)
    +

    Calculate weighted discrete $H^1(\Omega)$ seminorm of a solution vector.

    source
    VoronoiFVM.w1pnormFunction
    w1pnorm(sys, u, p)
    +w1pnorm(sys, u, p, species_weights)
    +

    Calculate weighted discrete $W^{1,p}(\Omega)$ norm of a solution vector.

    source
    VoronoiFVM.h1normFunction
    h1norm(sys, u)
    +h1norm(sys, u, species_weights)
    +

    Calculate weighted discrete $H^1(\Omega)$ norm of a solution vector.

    source
    VoronoiFVM.lpw1pseminormFunction
    lpw1pseminorm(sys, u, p)
    +lpw1pseminorm(sys, u, p, species_weights)
    +

    Calculate weighted discrete $L^p([0,T];W^{1,p}(\Omega))$ seminorm of a transient solution.

    source
    VoronoiFVM.l2h1seminormFunction
    l2h1seminorm(sys, u)
    +l2h1seminorm(sys, u, species_weights)
    +

    Calculate weighted discrete $L^2([0,T];H^1(\Omega))$ seminorm of a transient solution.

    source
    VoronoiFVM.lpw1pnormFunction
    lpw1pnorm(sys, u, p)
    +lpw1pnorm(sys, u, p, species_weights)
    +

    Calculate weighted discrete $L^p([0,T];W^{1,p}(\Omega))$ norm of a transient solution.

    source
    VoronoiFVM.l2h1normFunction
    l2h1norm(sys, u)
    +l2h1norm(sys, u, species_weights)
    +

    Calculate weighted discrete $L^2([0,T];H^1(\Omega))$ norm of a transient solution.

    source

    Solution integrals

    VoronoiFVM.integrateMethod
    integrate(system,F,U; boundary=false)

    Integrate node function (same signature as reaction or storage) F of solution vector region-wise over domain or boundary. The result is an nspec x nregion vector.

    source
    VoronoiFVM.edgeintegrateFunction
    edgeintegrate(system,F,U; boundary=false)

    Integrate edge function (same signature as flux function) F of solution vector region-wise over domain or boundary. The result is an nspec x nregion vector.

    source

    Nodal flux reconstruction

    VoronoiFVM.nodefluxFunction
    nodeflux(system, U)
    +

    Reconstruction of edge flux as vector function on the nodes of the triangulation. The result can be seen as a piecewiesw linear vector function in the FEM space spanned by the discretization nodes exhibiting the flux density.

    The reconstruction is based on the "magic formula" R. Eymard, T. Gallouet, R. Herbin, IMA Journal of Numerical Analysis (2006) 26, 326−353, Lemma 2.4 .

    The return value is a dim x nspec x nnodes vector. The flux of species i can e.g. plotted via GridVisualize.vectorplot.

    Example:

        ispec=3
    +    vis=GridVisualizer(Plotter=Plotter)
    +    scalarplot!(vis,grid,solution[ispec,:],clear=true,colormap=:summer)
    +    vectorplot!(vis,grid,nf[:,ispec,:],clear=false)
    +    reveal(vis)

    CAVEAT: there is a possible unsolved problem with the values at domain corners in the code. Please see any potential boundary artifacts as a manifestation of this issue and report them.

    source

    Boundary flux calculation

    VoronoiFVM.TestFunctionFactoryType
    mutable struct TestFunctionFactory

    Data structure containing DenseSystem used to calculate test functions for boundary flux calculations.

    • system::VoronoiFVM.AbstractSystem: Original system
    • tfsystem::VoronoiFVM.System{Tv, Tc, Ti, Tm, Matrix{Ti}, Matrix{Tv}} where {Tv, Tc, Ti, Tm}: Test function system
    • control::SolverControl: Solver control
    source
    VoronoiFVM.TestFunctionFactoryMethod
    TestFunctionFactory(
    +    system::VoronoiFVM.AbstractSystem;
    +    control
    +) -> TestFunctionFactory
    +

    Constructor for TestFunctionFactory from System

    source
    VoronoiFVM.integrateMethod
    integrate(system, tf, U; kwargs...)
    +

    Calculate test function integral for steady state solution.

    source
    VoronoiFVM.integrateMethod
    integrate(system, tf, U, Uold, tstep; params)
    +

    Calculate test function integral for transient solution.

    source
    VoronoiFVM.testfunctionMethod
    testfunction(factory::TestFunctionFactory, bc0, bc1) -> Any
    +

    Create testfunction which has Dirichlet zero boundary conditions for boundary regions in bc0 and Dirichlet one boundary conditions for boundary regions in bc1.

    source

    Impedance calculatiom

    Impedance calculation can be seen as a postprocessing step after the solution of the unexcited stationary system.

    VoronoiFVM.ImpedanceSystemType
    mutable struct ImpedanceSystem{Tv} <: VoronoiFVM.AbstractImpedanceSystem{Tv}

    Concrete type for impedance system.

    • sysnzval::AbstractArray{Complex{Tv}, 1} where Tv: Nonzero pattern of time domain system matrix
    • storderiv::AbstractMatrix: Derivative of storage term
    • matrix::AbstractArray{Complex{Tv}, 2} where Tv: Complex matrix of impedance system
    • F::AbstractArray{Complex{Tv}, 2} where Tv: Right hand side of impedance system
    • U0::AbstractMatrix: Stationary state
    source
    VoronoiFVM.ImpedanceSystemMethod
    ImpedanceSystem(system, U0, excited_spec, excited_bc)
    +

    Construct impedance system from time domain system sys and steady state solution U0 under the assumption of a periodic perturbation of species excited_spec at boundary excited_bc.

    source
    VoronoiFVM.ImpedanceSystemMethod
    ImpedanceSystem(system, U0; λ0)
    +

    Construct impedance system from time domain system sys and steady state solution U0

    source
    VoronoiFVM.impedanceMethod
    impedance(impedance_system,ω, U0 ,
    +          excited_spec, excited_bc, excited_bcval,
    +           dmeas_stdy,
    +           dmeas_tran 
    +           )
    +    

    Calculate impedance.

    • ω: frequency
    • U0: steady state slution
    • dmeas_stdy: Derivative of steady state part of measurement functional
    • dmeas_tran Derivative of transient part of the measurement functional
    source
    VoronoiFVM.measurement_derivativeMethod
    measurement_derivative(system, measurement_functional, U0)
    +

    Calculate the derivative of the scalar measurement functional at steady state U0

    Usually, this functional is a test function integral. Initially, we assume that its value depends on all unknowns of the system.

    source
    diff --git a/v1.19.1/quantities/index.html b/v1.19.1/quantities/index.html new file mode 100644 index 000000000..d980bee8b --- /dev/null +++ b/v1.19.1/quantities/index.html @@ -0,0 +1,4 @@ + +Quantities · VoronoiFVM.jl

    Quantities

    The concept of quantities is implemented on top of the concept of species numbers. They have been introduces in order to be able to handle discontinuities at interfaces.

    VoronoiFVM.ContinuousQuantityType
    struct ContinuousQuantity{Ti} <: VoronoiFVM.AbstractQuantity{Ti}

    A continuous quantity is represented by exactly one species number

    • ispec::Any: Species number representing the quantity
    • id::Any: Quantity identifier allowing to use the quantity as index in parameter fields
    source
    VoronoiFVM.ContinuousQuantityMethod
     ContinuousQuantity(system,regions; ispec=0, id=0)

    Add continuous quantity to the regions listed in regions.

    Unless specified in ispec, the species number is generated automatically.

    Unless specified by id, the quantity ID is generated automatically.

    source
    VoronoiFVM.DiscontinuousQuantityType
    struct DiscontinuousQuantity{Ti} <: VoronoiFVM.AbstractQuantity{Ti}

    A discontinuous quantity is represented by different species in neighboring regions.

    • regionspec::Vector: Species numbers representing the quantity in each region
    • id::Any: Quantity identifier allowing to use the quantity as index in parameter fields
    source
    VoronoiFVM.DiscontinuousQuantityMethod
     DiscontinuousQuantity(system,regions; regionspec=nothing, id=0)

    Add discontinuous quantity to the regions listed in regions.

    Unless specified in regionspec, the species numbers for each region are generated automatically.

    Unless specified by id, the quantity ID is generated automatically.

    source
    VoronoiFVM.InterfaceQuantityType
    struct InterfaceQuantity{Ti} <: VoronoiFVM.AbstractQuantity{Ti}

    An interface quantity is represented by exactly one species number

    • ispec::Any: Species number representing the quantity
    • bregspec::Vector: boundary regions, where interface quantity is defined
    • id::Any: Quantity identifier allowing to use the quantity as index in parameter fields
    source
    VoronoiFVM.InterfaceQuantityMethod
     InterfaceQuantity(system,regions; ispec=0, id=0)

    Add interface quantity to the boundary regions given in breg.

    Unless specified in ispec, the species number is generated automatically.

    Unless specified by id, the quantity ID is generated automatically.

    source
    Base.getindexMethod
    A[q]

    Access columns of vectors A using id of quantity q. This is meant for vectors indexed by species.

    source
    Base.getindexMethod
    M[q,i]

    Access columns M using id of quantity q. This is meant for nspecies x nregions matrices e.g. defining parameters.

    source
    Base.getindexMethod
    bnode[quantity,ireg]

    Return species number of discontinuous quantity region ireg adjacent to BNode.

    source
    Base.getindexMethod
    bnode[quantity]

    Return species number of discontinuous quantity region ireg adjacent to BNode for outer boundary nodes.

    source
    Base.getindexMethod
    u[q,j]

    Return value of quantity in unknowns on edge in flux callbacks.

    source
    Base.getindexMethod
    u[q]

    Return value of quantity in unknowns on node in node callbacks.

    source
    Base.getindexMethod
    u[q,ireg]

    Return value of discontinuous quantity in unknowns adjacent to unknowns on boundary node.

    source
    Base.setindex!Method
    A[q]

    Set element of A using id of quantity q This is meant for vectors indexed by species.

    source
    Base.setindex!Method
    M[q,i]

    Set element of M using id of quantity q. This is meant for nspecies x nregions matrices e.g. defining parameters.

    source
    Base.setindex!Method
    f[q,ireg]=v

    Set rhs value for discontinuous quantity in adjacent regions of boundary node.

    source
    VoronoiFVM.subgridsMethod
    subgrids(quantity, system)

    Return a vector of subgrids containing a subgrid for each region where discontinuous quantity is defined.

    source
    VoronoiFVM.viewsMethod
    views(quantity, subgrids,system)

    Return a vector of solutions containing the solutions with respect tp each region where discontinuous quantity is defined.

    source
    diff --git a/v1.19.1/runexamples/index.html b/v1.19.1/runexamples/index.html new file mode 100644 index 000000000..d9b95e2bf --- /dev/null +++ b/v1.19.1/runexamples/index.html @@ -0,0 +1,18 @@ + +About the examples · VoronoiFVM.jl

    About the examples

    The examples have been designed with the following issues in mind:

    • they run from the Julia REPL
    • each example is a Julia module named similar to the basename of the example file.
    • an example can be used as the starting point for a project
    • the examples at the same time comprise the test suite for VoronoiFVM.

    Since the creation of these examples, the API has been updated and simplified.

    Running the examples

    Plotting is performed using the GridVisualize.jl package which interfaces PyPlot.jl, Plots.jl, Makie.jl.

    In order to run ExampleXXX, perform the following steps:

    • Download the example file (e.g. via the source code link at the top)
    • Call Julia with an Julia environment which contains VoronoiFVM.jl, ExtendableGrids.jl, GridVisualize.jl and e.g. PyPlot.jl
    • include("ExampleXXX.jl")
    • Run the example via ExampleXXX.main(Plotter=PyPlot)

    Due to the encapsulation into modules, you can load as many examples as you like.

    If you want to modify the example, consider using Revise.jl and includet.

    Performance with closures

    VoronoiFVM provides two flavors of calbacks for constitutive functions:

    • Callbacks with data parameoter. data is declared as part of Physics and passed down to the callbacks
    • Callbacks without data parameter. Here, the parameters of the physics callbacks are accessed via closures, i.e. from within the scope of the definition of the particular function.

    While the second method is very convenient to use, it comes with a serious performance pitfall: if a variable in the closure is assigned twice, Julia becomes unsure about it's type and therefore "boxes" it, i.e. it creates a wrapper struct around the variable value which is able to track its potentially changing type. The serious consequence of this is that assignments to a boxed variable lead to allocations, which are a serious performance hit if they occur in loops over grid nodes or edges.

    This behaviour is explained in the Julia documentation.

    Here is an example which comes close to the situation in VoronoiFVM:

    function ttype_boxed(n)
    +    u=rand(n)
    +    v=similar(u)
    +    a=2.0
    +    a=3.0
    +    dostuff(u)=a*u
    +    @allocated map!(dostuff,v,u)
    +end
    +ttype_boxed(10)
    320

    The remedy is to type-annotate variables from closures:

    function ttype_annotated(n)
    +    u=rand(n)
    +    v=similar(u)
    +    a::Float64=2.0
    +    a=3.0
    +    dostuff(u)=a*u
    +    @allocated map!(dostuff,v,u)
    +end
    +ttype_annotated(10)
    0
    diff --git a/v1.19.1/search_index.js b/v1.19.1/search_index.js new file mode 100644 index 000000000..807d858ad --- /dev/null +++ b/v1.19.1/search_index.js @@ -0,0 +1,3 @@ +var documenterSearchIndex = {"docs": +[{"location":"module_examples/Example221_EquationBlockPrecon/#221:-Equation-block-preconditioning","page":"221: Equation block preconditioning","title":"221: Equation block preconditioning","text":"","category":"section"},{"location":"module_examples/Example221_EquationBlockPrecon/","page":"221: Equation block preconditioning","title":"221: Equation block preconditioning","text":"(source code)","category":"page"},{"location":"module_examples/Example221_EquationBlockPrecon/","page":"221: Equation block preconditioning","title":"221: Equation block preconditioning","text":"module Example221_EquationBlockPrecon\n\nusing VoronoiFVM, GridVisualize, ExtendableGrids, Printf\nusing AMGCLWrap, ExtendableSparse\nusing Test\n\nfunction main(;dim=1, nref=0, Plotter = nothing, plot_grid = false, verbose = false,\n unknown_storage = :sparse, assembly = :edgewise,strategy = nothing)\n\n nx=30*2^nref+1\n ny=9*2^nref\n\n X = range(0,3,length=nx)\n Y = range(-0.5,0.5,length=ny)\n if dim==1\n grid = VoronoiFVM.Grid(X)\n Γ_in=1\n Γ_out=2\n elseif dim==2\n grid = VoronoiFVM.Grid(X,Y)\n Γ_in=4\n Γ_out=2\n else\n grid = VoronoiFVM.Grid(X,Y,Y)\n Γ_in=4\n Γ_out=2\n end\n cellmask!(grid, [0.0,-0.5,-0.5], [1.0,0.5,0.5], 1)\n cellmask!(grid, [1.0,-0.5,-0.5], [2.0,0.5,0.5], 2)\n cellmask!(grid, [2.0,-0.5,-0.5], [3.0,0.5,0.5], 3)\n\n\n subgrid1 = subgrid(grid, [1])\n subgrid2 = subgrid(grid, [1, 2, 3])\n subgrid3 = subgrid(grid, [3])\n\n if plot_grid\n return gridplot(grid; Plotter = Plotter)\n end\n\n eps = [1, 1, 1]\n k = [1, 1, 1]\n\n function reaction(f, u, node)\n if node.region == 1\n f[1] = k[1] * u[1]\n f[2] = -k[1] * u[1]\n elseif node.region == 3\n f[2] = k[3] * u[2]\n f[3] = -k[3] * u[2]\n else\n f[1] = 0\n end\n end\n\n function source(f, node)\n if node.region == 1\n f[1] = 1.0e-4 * (3.0 - node[1])\n end\n end\n\n flux = function (f, u, edge)\n if edge.region == 1\n f[1] = eps[1] * (u[1, 1] - u[1, 2])\n f[2] = eps[2] * (u[2, 1] - u[2, 2])\n elseif edge.region == 2\n f[2] = eps[2] * (u[2, 1] - u[2, 2])\n elseif edge.region == 3\n f[2] = eps[2] * (u[2, 1] - u[2, 2])\n f[3] = eps[3] * (u[3, 1] - u[3, 2])\n end\n end\n\n storage = function (f, u, node)\n if node.region == 1\n f[1] = u[1]\n f[2] = u[2]\n elseif node.region == 2\n f[2] = u[2]\n elseif node.region == 3\n f[2] = u[2]\n f[3] = u[3]\n end\n end\n\n sys = VoronoiFVM.System(grid; flux, reaction, storage, source,\n unknown_storage , assembly, is_linear=true)\n\n enable_species!(sys, 1, [1])\n enable_species!(sys, 2, [1, 2, 3])\n enable_species!(sys, 3, [3])\n\n boundary_dirichlet!(sys, 3, 2, 0.0)\n\n control = SolverControl(strategy, sys, verbose=\"l\")\n U=solve(sys;control)\n @info num_dof(U)\n\n p = GridVisualizer(; Plotter = Plotter, layout = (3,1),\n limits = (0, 1e-3),\n xlimits=(0,3))\n\n U1 = view(U[1, :], subgrid1)\n U2 = view(U[2, :], subgrid2)\n U3 = view(U[3, :], subgrid3)\n\n scalarplot!(p[1, 1], subgrid1, U1; title = \"spec1\", color = (0.5, 0, 0))\n scalarplot!(p[2, 1], subgrid2, U2; title = \"spec2\", color = (0.0, 0.5, 0))\n scalarplot!(p[3, 1], subgrid3, U3; title = \"spec3\", color = (0.0, 0.0, 0.5))\n reveal(p)\n U\nend\n\nfunction runtests()\n strategy=BICGstabIteration(AMGCL_AMGPreconditioner())\n @test sum(main(;dim=1,strategy, unknown_storage=:dense)[2,:]) ≈ 0.014100861021046823\n @test sum(main(;dim=1,strategy, unknown_storage=:sparse)[2,:]) ≈ 0.014100861021046823\n @test sum(main(;dim=2,strategy, unknown_storage=:dense)[2,:]) ≈ 0.12690774918944653\n @test sum(main(;dim=2,strategy, unknown_storage=:sparse)[2,:]) ≈ 0.12690774918944653\n @test sum(main(;dim=3,strategy, unknown_storage=:dense)[2,:]) ≈ 1.1423244466533444\n @test sum(main(;dim=3,strategy, unknown_storage=:sparse)[2,:]) ≈ 1.1423244466533444\nend\n\nend","category":"page"},{"location":"module_examples/Example221_EquationBlockPrecon/","page":"221: Equation block preconditioning","title":"221: Equation block preconditioning","text":"","category":"page"},{"location":"module_examples/Example221_EquationBlockPrecon/","page":"221: Equation block preconditioning","title":"221: Equation block preconditioning","text":"This page was generated using Literate.jl.","category":"page"},{"location":"system/#System","page":"System","title":"System","text":"","category":"section"},{"location":"system/","page":"System","title":"System","text":"The computational grid required is assumed to correspond to a domain Omega=cup_r=1^n_Omega Omega_r ","category":"page"},{"location":"system/","page":"System","title":"System","text":"Grids for VoronoiFVM are managed by the packages ExtendableGrids.jl and SimplexGridFactory.jl","category":"page"},{"location":"system/","page":"System","title":"System","text":"with boundary partialOmega=Gamma=cup_b=1^n_Gamma Gamma_b.","category":"page"},{"location":"system/","page":"System","title":"System","text":"The subdomains Omega_r are called \"regions\" and the boundary subdomains Gamma_b are called \"boundary regions\".","category":"page"},{"location":"system/","page":"System","title":"System","text":"On this complex of domains \"lives\" a number of species which are either attached to a number of regions or to a number of boundary regions.","category":"page"},{"location":"system/","page":"System","title":"System","text":"All these data, the matrix for the linear system and other things are hold together by a struct VoronoiFVM.System. This type is not exported to avoid name clashes.","category":"page"},{"location":"system/#System-constructors","page":"System","title":"System constructors","text":"","category":"section"},{"location":"system/","page":"System","title":"System","text":"VoronoiFVM.System(grid::ExtendableGrid; kwargs...)\nVoronoiFVM.System(X::AbstractVector; kwargs...)\nVoronoiFVM.System(X::AbstractVector,Y::AbstractVector; kwargs...)\nVoronoiFVM.System(X::AbstractVector,Y::AbstractVector,Z::AbstractVector; kwargs...)\nupdate_grid!\nphysics!","category":"page"},{"location":"system/#VoronoiFVM.System-Tuple{ExtendableGrid}","page":"System","title":"VoronoiFVM.System","text":"System(grid; kwargs...)\n\nCreate structure of type VoronoiFVM.System{Tv,Ti, Tm, TSpecMat<:AbstractMatrix, TSolArray<:AbstractMatrix} holding data for finite volume system solution. \n\nParameters: \n\ngrid::ExtendableGrid: 1, 2 or 3D computational grid\n\nKeyword arguments:\n\nspecies: vector of integer species indices. Added to all grid regions, avoiding the need to call enable_species! for this default case. If it is kept empty, species have be added to the system after creation via enable_species!.\nunknown_storage: string or symbol. Information on species distribution is kept in sparse or dense matrices matrices and, correspondingly, the solution array is of type SparseSolutionArray or matrix, respectively. In the case of sparse unknown storage, the system matrix handles exactly those degrees of freedom which correspond to unknowns. However, handling of the sparse matrix structures for the bookkeeping of the unknowns creates overhead.\n:dense : solution vector is an nspecies x nnodes dense matrix\n:sparse : solution vector is an nspecies x nnodes sparse matrix\nmatrixindextype: Integer type. Index type for sparse matrices created in the system.\nis_linear: whether the system is linear or not. If it is linear, only one Newton step is used to solve it.\nassembly: either :cellwise (default) or :edgewise. Determine, how the assembly loop is organized. :cellwise means that the outer loop goes over grid cells (triangles, tetrahedra), and contributions to edge fluxes and node reactions are calculated for each cell. As a consequence, e.g. im 2D for all interior edges, flux functions are callled twice, once for each adjacent cell. Especially in 3D, this becomes a significant overhead. With :edgewise, geometry factors of these edges are pre-assembled, and the outer assembly loops go over all grid edges resp. nodes, still with separate calls if neigboring cells belong to different regions.\n\nnote: Note\nIt is planned to make :edgewise the default in a later version.\n\nPhysics keyword arguments:\n\nflux: Function. Flux between neighboring control volumes: flux(f,u,edge) or flux(f,u,edge,data) should return in f[i] the flux of species i along the edge joining circumcenters of neighboring control volumes. For species i,u[i,1] and u[i,2] contain the unknown values at the corresponding ends of the edge.\nstorage: Function. Storage term (term under time derivative): storage(f,u,node) or storage(f,u,node,data) It should return in f[i] the storage term for the i-th equation. u[i] contains the value of the i-th unknown.\nreaction: Function. Reaction term: reaction(f,u,node) or reaction(f,u,node,data) It should return in f[i] the reaction term for the i-th equation. u[i] contains the value of the i-th unknown.\nedgereaction: Function. Edge reeaction term: edgereaction(f,u,edge) or edgereaction(f,u,edge,data) It should return in f[i] the reaction term for the i-th equation. For species i,u[i,1] and u[i,2] contain the unknown values at the corresponding ends of the edge.\nsource: Function. Source term: source(f,node) or source(f,node,data). It should return the in f[i] the value of the source term for the i-th equation.\nbflux: Function. Flux between neighboring control volumes on the boundary\nbreaction Function. Boundary reaction term: breaction(f,u,node) or breaction(f,u,node,data) Similar to reaction, but restricted to the inner or outer boundaries.\nbcondition Function. Alias for breaction.\nbsource: Function. Boundary source term: bsource(f,node) or bsource(f,node,data). It should return in f[i] the value of the source term for the i-th equation.\nbstorage: Function. Boundary storage term: bstorage(f,u,node) or bstorage(f,u,node,data) Similar to storage, but restricted to the inner or outer boundaries.\ngeneric_operator: Function. Generic operator generic_operator(f,u,sys). This operator acts on the full solution u of a system. Sparsity is detected automatically unless generic_operator_sparsity is given.\ngeneric_operator_sparsity: Function defining the sparsity structure of the generic operator. This should return the sparsity pattern of the generic_operator.\nnparams: number of parameters the system is depending on, and with respect to which the derivatives need to be obtained\ndata: User data (parameters). This allows to pass various parameters to the callback functions. If data is given, all callback functions should accept a last data argument. Otherwise, no data are passed explicitly, and constitutive callbacks can take parameters from the closure where the function is defined.\nmatrixtype: :default, :sparse, :tridiagonal, :banded\n\n\n\n\n\n","category":"method"},{"location":"system/#VoronoiFVM.System-Tuple{AbstractVector}","page":"System","title":"VoronoiFVM.System","text":"System(X; kwargs...)\n\nCreate an 1D grid from vector X and call VoronoiFVM.System(grid::ExtendableGrid; kwargs...).\n\n\n\n\n\n","category":"method"},{"location":"system/#VoronoiFVM.System-Tuple{AbstractVector, AbstractVector}","page":"System","title":"VoronoiFVM.System","text":"System(X,Y; kwargs...)\n\nCreate a 2D grid from vectors X,Y and call VoronoiFVM.System(grid::ExtendableGrid; kwargs...).\n\n\n\n\n\n","category":"method"},{"location":"system/#VoronoiFVM.System-Tuple{AbstractVector, AbstractVector, AbstractVector}","page":"System","title":"VoronoiFVM.System","text":"System(X,Y, Z; kwargs...)\n\nCreate a 3D grid from vectors X,Y,Z and call VoronoiFVM.System(grid::ExtendableGrid; kwargs...).\n\n\n\n\n\n","category":"method"},{"location":"system/#VoronoiFVM.update_grid!","page":"System","title":"VoronoiFVM.update_grid!","text":"update_grid!(system; grid=system.grid)\n\nUpdate grid (e.g. after rescaling of coordinates).\n\n\n\n\n\n","category":"function"},{"location":"system/#VoronoiFVM.physics!","page":"System","title":"VoronoiFVM.physics!","text":"physics!(system,physics)\n\nReplace System's physics data\n\n\n\n\n\nphysics!(system; kwargs...)\n\nReplace System's physics data.\n\n\n\n\n\n","category":"function"},{"location":"system/#Adding-species-by-species-numbers","page":"System","title":"Adding species by species numbers","text":"","category":"section"},{"location":"system/","page":"System","title":"System","text":"enable_species!(system::VoronoiFVM.AbstractSystem,ispec::Integer, regions::AbstractVector)\nenable_species!(system::VoronoiFVM.AbstractSystem; kwargs...)\nenable_boundary_species!\nVoronoiFVM.is_boundary_species\nVoronoiFVM.is_bulk_species","category":"page"},{"location":"system/#VoronoiFVM.enable_species!-Tuple{VoronoiFVM.AbstractSystem, Integer, AbstractVector}","page":"System","title":"VoronoiFVM.enable_species!","text":"enable_species!(system,ispec,regions)\n\nAdd species ispec to a list of bulk regions. Species numbers for bulk and boundary species have to be distinct. Once a species has been added, it cannot be removed.\n\n\n\n\n\n","category":"method"},{"location":"system/#VoronoiFVM.enable_species!-Tuple{VoronoiFVM.AbstractSystem}","page":"System","title":"VoronoiFVM.enable_species!","text":"enable_species!(system; kwargs...)\n\nKeyword arguments:\n\nspecies: Integer or vector of integers. Species to be added to the system.\nregions: Vector of integers. Regions, where these species shall be added.If nothing, they are added to all species.\n\nOnce a species has been added, it cannot be removed.\n\n\n\n\n\n","category":"method"},{"location":"system/#VoronoiFVM.enable_boundary_species!","page":"System","title":"VoronoiFVM.enable_boundary_species!","text":"enable_boundary_species!(system,ispec,regions)\n\nAdd species ispec to a list of boundary regions. Species numbers for bulk and boundary species have to be distinct. Once a species has been added, it cannot be removed.\n\n\n\n\n\n","category":"function"},{"location":"system/#VoronoiFVM.is_boundary_species","page":"System","title":"VoronoiFVM.is_boundary_species","text":"is_boundary_species(AbstractSystem, ispec) -> Bool\n\nCheck if species number corresponds to a boundary species.\n\n\n\n\n\n","category":"function"},{"location":"system/#VoronoiFVM.is_bulk_species","page":"System","title":"VoronoiFVM.is_bulk_species","text":"is_bulk_species(AbstractSystem, ispec) -> Bool\n\nCheck if species number corresponds to a bulk species.\n\n\n\n\n\n","category":"function"},{"location":"system/#Handling-boundary-conditions","page":"System","title":"Handling boundary conditions","text":"","category":"section"},{"location":"system/","page":"System","title":"System","text":"Boundary conditions are handled in the bcondition callback passed to the system constructor. For being called in this callback, the following functions are available","category":"page"},{"location":"system/","page":"System","title":"System","text":"boundary_dirichlet!(y,u,bnode,ispec,ireg,val)\nboundary_dirichlet!(y,u,bnode;kwargs...)\nboundary_neumann!(y,u,bnode,ispec,ireg,val)\nboundary_neumann!(y,u,bnode;kwargs...)\nboundary_robin!(y,u,bnode,ispec,ireg,fac,val)\nboundary_robin!(y,u,bnode;kwargs...)\nramp","category":"page"},{"location":"system/#VoronoiFVM.boundary_dirichlet!-NTuple{6, Any}","page":"System","title":"VoronoiFVM.boundary_dirichlet!","text":" boundary_dirichlet!(y,u,bnode,ispec,ireg,val)\n\nSet Dirichlet boundary condition for species ispec at boundary ibc.\n\n\n\n\n\n","category":"method"},{"location":"system/#VoronoiFVM.boundary_dirichlet!-Tuple{Any, Any, Any}","page":"System","title":"VoronoiFVM.boundary_dirichlet!","text":" boundary_dirichlet!(y,u,bnode, args...; kwargs...)\n\nKeyword argument version:\n\nspecies: species number. Default: 1\nregion: boundary region number. By default, all boundary regions.\nvalue: value\n\n\n\n\n\n","category":"method"},{"location":"system/#VoronoiFVM.boundary_neumann!-NTuple{6, Any}","page":"System","title":"VoronoiFVM.boundary_neumann!","text":" boundary_neumann!(y,u,bnode,ispec,ireg,val)\n\nSet Neumann boundary condition for species ispec at boundary ibc.\n\n\n\n\n\n","category":"method"},{"location":"system/#VoronoiFVM.boundary_neumann!-Tuple{Any, Any, Any}","page":"System","title":"VoronoiFVM.boundary_neumann!","text":" boundary_neumann!(y,u,bnode, args...; kwargs...)\n\nKeyword argument version:\n\nspecies: species number. Default: 1\nregion: boundary region number. By default, all boundary regions.\nvalue: value\n\n\n\n\n\n","category":"method"},{"location":"system/#VoronoiFVM.boundary_robin!-NTuple{7, Any}","page":"System","title":"VoronoiFVM.boundary_robin!","text":" boundary_robin!(y,u,bnode,ispec,ireg,fac,val)\n\nSet Robin boundary condition for species ispec at boundary ibc.\n\n\n\n\n\n","category":"method"},{"location":"system/#VoronoiFVM.boundary_robin!-Tuple{Any, Any, Any}","page":"System","title":"VoronoiFVM.boundary_robin!","text":" boundary_robin!(y,u,bnode, args...; kwargs...)\n\nKeyword argument version:\n\nspecies: species number. Default: 1\nregion: boundary region number. By default, all boundary regions.\nfactor: factor\nvalue: value\n\n\n\n\n\n","category":"method"},{"location":"system/#VoronoiFVM.ramp","page":"System","title":"VoronoiFVM.ramp","text":" ramp(t; kwargs...)\n\nRamp function for specifying time dependent boundary conditions\n\nKeyword arguments:\n\ndt: Tuple: start and end time of ramp. Default: (0,0.1)\ndu: Tuple: values at start and end time. Default: (0,0)\n\n\n\n\n\n","category":"function"},{"location":"system/#Outflow-boundary-conditions","page":"System","title":"Outflow boundary conditions","text":"","category":"section"},{"location":"system/","page":"System","title":"System","text":"These are characterized by the boutflow physics callback and and the outflowboundaries keyword argument in the system resp. physics constructor. See also the corresponding notebook","category":"page"},{"location":"system/","page":"System","title":"System","text":"hasoutflownode\nisoutflownode\noutflownode","category":"page"},{"location":"system/#VoronoiFVM.hasoutflownode","page":"System","title":"VoronoiFVM.hasoutflownode","text":"hasoutflownode(edge)\n\nCheck if one node of the edge is situated on a boundary region listed in outflowboundaries, see [struct Physics].\n\n\n\n\n\n","category":"function"},{"location":"system/#VoronoiFVM.isoutflownode","page":"System","title":"VoronoiFVM.isoutflownode","text":"isoutflownode(edge,inode)\n\nCheck if inode (1 or 2) is an outflow node.\n\n\n\n\n\nisoutflownode(edge,inode,irefgion)\n\nCheck if inode (1 or 2) is an outflow node on boundary region iregion.\n\n\n\n\n\n","category":"function"},{"location":"system/#VoronoiFVM.outflownode","page":"System","title":"VoronoiFVM.outflownode","text":"outflownode(edge)\n\nReturn outflow node of edge (1 or 2).\n\n\n\n\n\n","category":"function"},{"location":"system/#Allocation-warnings","page":"System","title":"Allocation warnings","text":"","category":"section"},{"location":"system/","page":"System","title":"System","text":"The code checks for allocations in the assembly loop. Care has been taken to ensure that allocations in the assembly loop don't emerge from VoronoiFVM.jl code.","category":"page"},{"location":"system/","page":"System","title":"System","text":"If allocations occur in the assembly loop, they happen in the physics callbacks. The corresponding warnings can bee switched off by passing a verbosity strings without 'a' to the solver. If no data are allocated in the physics callbacks, these allocations are probably due to type instabilities in physics callbacks, see the the discussion here. Type instabilities can be debugged via the @time macro applied to expressions in a physics callback.","category":"page"},{"location":"system/","page":"System","title":"System","text":"The following cases provide some ideas where to look for reasons of the problem and possible remedies:","category":"page"},{"location":"system/","page":"System","title":"System","text":"Case 1: a parameter changes its value, and Julia is not sure about the type.","category":"page"},{"location":"system/","page":"System","title":"System","text":"eps=1.0\n\nflux(f,u,edge)\n f[1]=eps*(u[1,1]-[1,2])\nend\n... solve etc ...\neps=2.0","category":"page"},{"location":"system/","page":"System","title":"System","text":"Remedy: use a type annotation eps::Float64=... to signalize your intent to Julia. This behaviour is explained in the Julia documentation.","category":"page"},{"location":"system/","page":"System","title":"System","text":"Case 2: variables in the closure have the same name as a variable introduced in a callback.","category":"page"},{"location":"system/","page":"System","title":"System","text":"flux(f,u,edge)\n x=(u[1,1]-[1,2])\n f[1]=x\nend\n\n... create etc ...\n\nx=solve(...)","category":"page"},{"location":"system/","page":"System","title":"System","text":"Remedy: rename e.g. x=solve() to sol=solve()","category":"page"},{"location":"system/#Various-tools","page":"System","title":"Various tools","text":"","category":"section"},{"location":"system/","page":"System","title":"System","text":"num_dof\nnum_species\nVoronoiFVM.unknowns(system::VoronoiFVM.AbstractSystem; kwargs...)\nVoronoiFVM.unknowns(Tu::Type, system::VoronoiFVM.AbstractSystem; kwargs...)\nBase.map\nBase.map!\nVoronoiFVM.isunknownsof\nBase.reshape(::AbstractVector, ::VoronoiFVM.AbstractSystem)","category":"page"},{"location":"system/#VoronoiFVM.num_dof","page":"System","title":"VoronoiFVM.num_dof","text":"num_dof(system)\n\n\nNumber of degrees of freedom for system.\n\n\n\n\n\n","category":"function"},{"location":"system/#VoronoiFVM.num_species","page":"System","title":"VoronoiFVM.num_species","text":"num_species(edge::VoronoiFVM.AbstractEdge) -> Any\n\n\nReturn number of species for edge\n\n\n\n\n\nnum_species(system)\n\n\nNumber of species in system\n\n\n\n\n\nnum_species(a)\n\n\nNumber of species (size of first dimension) of solution array.\n\n\n\n\n\n","category":"function"},{"location":"system/#VoronoiFVM.unknowns-Tuple{VoronoiFVM.AbstractSystem}","page":"System","title":"VoronoiFVM.unknowns","text":"unknowns(system; inival, inifunc)\n\n\nCreate a solution vector for system. If inival is not specified, the entries of the returned vector are undefined.\n\n\n\n\n\nunknowns(impedance_system)\n\n\nCreate a vector of unknowns of the impedance system\n\n\n\n\n\n","category":"method"},{"location":"system/#VoronoiFVM.unknowns-Tuple{Type, VoronoiFVM.AbstractSystem}","page":"System","title":"VoronoiFVM.unknowns","text":"unknowns(Tu, system; inival, inifunc)\n\n\nCreate a solution vector for system with elements of type Tu. If inival is not specified, the entries of the returned vector are undefined.\n\n\n\n\n\n","category":"method"},{"location":"system/#Base.map","page":"System","title":"Base.map","text":"map(inifunc, sys)\n\n\nCreate a solution vector for system using the callback inifunc which has the same signature as a source term.\n\n\n\n\n\nmap(inival, sys)\n\n\nCreate a solution vector for system using a constant initial value\n\n\n\n\n\n","category":"function"},{"location":"system/#Base.map!","page":"System","title":"Base.map!","text":"map!(inifunc, U, system)\n\n\nMap inifunc onto solution array U\n\n\n\n\n\n","category":"function"},{"location":"system/#VoronoiFVM.isunknownsof","page":"System","title":"VoronoiFVM.isunknownsof","text":"isunknownsof(u, sys)\n\n\nDetect if array fits to the system.\n\n\n\n\n\n","category":"function"},{"location":"system/#Base.reshape-Tuple{AbstractVector, VoronoiFVM.AbstractSystem}","page":"System","title":"Base.reshape","text":"reshape(v, system)\n\n\nReshape vector to fit as solution to system.\n\n\n\n\n\n","category":"method"},{"location":"system/#Types","page":"System","title":"Types","text":"","category":"section"},{"location":"system/","page":"System","title":"System","text":"VoronoiFVM.AbstractSystem\nVoronoiFVM.System{Tv,Ti, Tm, TSpecMat<:AbstractMatrix, TSolArray<:AbstractMatrix}","category":"page"},{"location":"system/#VoronoiFVM.AbstractSystem","page":"System","title":"VoronoiFVM.AbstractSystem","text":"abstract type AbstractSystem{Tv<:Number, Tc<:Number, Ti<:Integer, Tm<:Integer}\n\nAbstract type for finite volume system structure.\n\n\n\n\n\n","category":"type"},{"location":"system/#VoronoiFVM.System","page":"System","title":"VoronoiFVM.System","text":"mutable struct System{Tv, Tc, Ti, Tm, TSpecMat<:(AbstractMatrix), TSolArray<:(AbstractMatrix)} <: VoronoiFVM.AbstractSystem{Tv, Tc, Ti, Tm}\n\nStructure holding data for finite volume system.\n\n\n\n\n\n","category":"type"},{"location":"system/#Legacy-API","page":"System","title":"Legacy API","text":"","category":"section"},{"location":"system/","page":"System","title":"System","text":"boundary_dirichlet!(system::VoronoiFVM.AbstractSystem, ispec, ibc, v)\nboundary_dirichlet!(system::VoronoiFVM.AbstractSystem; kwargs...)\nboundary_neumann!(system::VoronoiFVM.AbstractSystem, ispec, ibc, v)\nboundary_neumann!(system::VoronoiFVM.AbstractSystem; kwargs...)\nboundary_robin!(system::VoronoiFVM.AbstractSystem, ispec, ibc,alpha, v)\nboundary_robin!(system::VoronoiFVM.AbstractSystem; kwargs...)\nVoronoiFVM.DenseSystem\nVoronoiFVM.SparseSystem\nviewK\nviewL","category":"page"},{"location":"system/#VoronoiFVM.boundary_dirichlet!-Tuple{VoronoiFVM.AbstractSystem, Any, Any, Any}","page":"System","title":"VoronoiFVM.boundary_dirichlet!","text":"boundary_dirichlet!(system, ispec, ibc, v)\n\n\nSet Dirichlet boundary condition for species ispec at boundary ibc:\n\nu_ispec=v on Gamma_ibc\n\ninfo: Info\nStarting with version 0.14, it is preferable to define boundary conditions within the bcondition physics callback\n\n\n\n\n\n","category":"method"},{"location":"system/#VoronoiFVM.boundary_dirichlet!-Tuple{VoronoiFVM.AbstractSystem}","page":"System","title":"VoronoiFVM.boundary_dirichlet!","text":" boundary_dirichlet!(system; kwargs...)\n\nKeyword argument version:\n\nspecies: species number\nregion: region number\nvalue: value\n\ninfo: Info\nStarting with version 0.14, it is preferable to define boundary conditions within the bcondition physics callback\n\n\n\n\n\n","category":"method"},{"location":"system/#VoronoiFVM.boundary_neumann!-Tuple{VoronoiFVM.AbstractSystem, Any, Any, Any}","page":"System","title":"VoronoiFVM.boundary_neumann!","text":"boundary_neumann!(system, ispec, ibc, v)\n\n\nSet Neumann boundary condition for species ispec at boundary ibc:\n\nmathrmflux_ispeccdot vec n=v on Gamma_ibc\n\ninfo: Info\nStarting with version 0.14, it is preferable to define boundary conditions within the bcondition physics callback\n\n\n\n\n\n","category":"method"},{"location":"system/#VoronoiFVM.boundary_neumann!-Tuple{VoronoiFVM.AbstractSystem}","page":"System","title":"VoronoiFVM.boundary_neumann!","text":" boundary_neumann!(system; kwargs...)\n\nKeyword argument version:\n\nspecies: species number\nregion: region number\nvalue: value\n\ninfo: Info\nStarting with version 0.14, it is preferable to define boundary conditions within the bcondition physics callback\n\n\n\n\n\n","category":"method"},{"location":"system/#VoronoiFVM.boundary_robin!-Tuple{VoronoiFVM.AbstractSystem, Vararg{Any, 4}}","page":"System","title":"VoronoiFVM.boundary_robin!","text":"boundary_robin!(system, ispec, ibc, α, v)\n\n\nSet Robin boundary condition for species ispec at boundary ibc:\n\nmathrmflux_ispeccdot vec n + alpha u_ispec=v on Gamma_ibc\n\ninfo: Info\nStarting with version 0.14, it is preferable to define boundary conditions within the bcondition physics callback\n\n\n\n\n\n","category":"method"},{"location":"system/#VoronoiFVM.boundary_robin!-Tuple{VoronoiFVM.AbstractSystem}","page":"System","title":"VoronoiFVM.boundary_robin!","text":" boundary_robin!(system; kwargs...)\n\nKeyword argument version:\n\nspecies: species number\nregion: region number\nfactor: factor\nvalue: value\n\ninfo: Info\nStarting with version 0.14, it is preferable to define boundary conditions within the bcondition physics callback\n\n\n\n\n\n","category":"method"},{"location":"system/#VoronoiFVM.DenseSystem","page":"System","title":"VoronoiFVM.DenseSystem","text":"const DenseSystem\n\nType alias for system with dense matrix based species management\n\n\n\n\n\n","category":"type"},{"location":"system/#VoronoiFVM.SparseSystem","page":"System","title":"VoronoiFVM.SparseSystem","text":"const SparseSystem\n\nType alias for system with sparse matrix based species management\n\n\n\n\n\n","category":"type"},{"location":"system/#VoronoiFVM.viewK","page":"System","title":"VoronoiFVM.viewK","text":"viewK(\n edge::VoronoiFVM.AbstractEdge,\n u\n) -> VoronoiFVM.VectorUnknowns\n\n\nSolution view on first edge node\n\n\n\n\n\n","category":"function"},{"location":"system/#VoronoiFVM.viewL","page":"System","title":"VoronoiFVM.viewL","text":"viewL(\n edge::VoronoiFVM.AbstractEdge,\n u\n) -> VoronoiFVM.VectorUnknowns\n\n\nSolution view on second edge node\n\n\n\n\n\n","category":"function"},{"location":"notebooks/#About-the-notebooks","page":"About the notebooks","title":"About the notebooks","text":"","category":"section"},{"location":"notebooks/","page":"About the notebooks","title":"About the notebooks","text":"Pluto.jl notebooks provide a great opportunity to put together code, text and graphics in a reproducible and accessible way. Therefore, the examples for this package are being amended by a series of Pluto notebooks. Like the example code, the notebook code is tested during CI.","category":"page"},{"location":"module_examples/Example107_NonlinearStorage1D/#107:-1D-Nonlinear-Storage","page":"107: 1D Nonlinear Storage","title":"107: 1D Nonlinear Storage","text":"","category":"section"},{"location":"module_examples/Example107_NonlinearStorage1D/","page":"107: 1D Nonlinear Storage","title":"107: 1D Nonlinear Storage","text":"(source code)","category":"page"},{"location":"module_examples/Example107_NonlinearStorage1D/","page":"107: 1D Nonlinear Storage","title":"107: 1D Nonlinear Storage","text":"This equation comes from the transformation of the nonlinear diffuision equation.","category":"page"},{"location":"module_examples/Example107_NonlinearStorage1D/","page":"107: 1D Nonlinear Storage","title":"107: 1D Nonlinear Storage","text":"partial_t u^frac1m -Delta u = 0","category":"page"},{"location":"module_examples/Example107_NonlinearStorage1D/","page":"107: 1D Nonlinear Storage","title":"107: 1D Nonlinear Storage","text":"in Omega=(-11) with homogeneous Neumann boundary conditions. We can derive an exact solution from the Barenblatt solution of the previous example.","category":"page"},{"location":"module_examples/Example107_NonlinearStorage1D/","page":"107: 1D Nonlinear Storage","title":"107: 1D Nonlinear Storage","text":"module Example107_NonlinearStorage1D\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\n\nfunction barenblatt(x, t, m)\n tx = t^(-1.0 / (m + 1.0))\n xx = x * tx\n xx = xx * xx\n xx = 1 - xx * (m - 1) / (2.0 * m * (m + 1))\n if xx < 0.0\n xx = 0.0\n end\n return tx * xx^(1.0 / (m - 1.0))\nend\n\nfunction main(; n = 20, m = 2.0, Plotter = nothing, verbose = false,\n unknown_storage = :sparse, tend = 0.01, tstep = 0.0001, assembly = :edgewise)\n\n # Create a one-dimensional discretization\n h = 1.0 / convert(Float64, n / 2)\n X = collect(-1:h:1)\n grid = VoronoiFVM.Grid(X)\n\n # Flux function which describes the flux\n # between neighboring control volumes\n function flux!(f, u, edge)\n f[1] = u[1, 1] - u[1, 2]\n end\n\n ϵ = 1.0e-10\n\n # Storage term\n # This needs to be regularized as its derivative\n # at 0 is infinity\n function storage!(f, u, node)\n f[1] = (ϵ + u[1])^(1.0 / m)\n end\n\n # Create a physics structure\n physics = VoronoiFVM.Physics(; flux = flux!,\n storage = storage!)\n\n # Create a finite volume system - either\n # in the dense or the sparse version.\n # The difference is in the way the solution object\n # is stored - as dense or as sparse matrix\n sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly)\n\n # Add species 1 to region 1\n enable_species!(sys, 1, [1])\n\n # Create a solution array\n inival = unknowns(sys)\n solution = unknowns(sys)\n t0 = 0.001\n\n # Broadcast the initial value\n inival[1, :] .= map(x -> barenblatt(x, t0, m)^m, X)\n\n # Create solver control info\n control = VoronoiFVM.NewtonControl()\n control.verbose = verbose\n control.Δu_opt = 0.1\n control.force_first_step = true\n tsol = solve(sys; inival, times = [t0, tend], control)\n\n if Plotter != nothing\n p = GridVisualizer(; Plotter = Plotter, layout = (1, 1), fast = true)\n for i = 1:length(tsol)\n time = tsol.t[i]\n scalarplot!(p[1, 1], grid, tsol[1, :, i]; title = @sprintf(\"t=%.3g\", time),\n color = :red, label = \"numerical\")\n scalarplot!(p[1, 1], grid, map(x -> barenblatt(x, time, m)^m, grid); clear = false,\n color = :green, label = \"exact\")\n reveal(p)\n sleep(1.0e-2)\n end\n end\n return sum(tsol[end])\nend\n\nusing Test\nfunction runtests()\n testval = 174.72418935404414\n @test main(; unknown_storage = :sparse, assembly = :edgewise)≈testval rtol=1.0e-5\n @test main(; unknown_storage = :dense, assembly = :edgewise)≈testval rtol=1.0e-5\n @test main(; unknown_storage = :sparse, assembly = :cellwise)≈testval rtol=1.0e-5\n @test main(; unknown_storage = :dense, assembly = :cellwise)≈testval rtol=1.0e-5\nend\n\nend","category":"page"},{"location":"module_examples/Example107_NonlinearStorage1D/","page":"107: 1D Nonlinear Storage","title":"107: 1D Nonlinear Storage","text":"","category":"page"},{"location":"module_examples/Example107_NonlinearStorage1D/","page":"107: 1D Nonlinear Storage","title":"107: 1D Nonlinear Storage","text":"This page was generated using Literate.jl.","category":"page"},{"location":"misc/#Miscellaneous","page":"Miscellaneous","title":"Miscellaneous","text":"","category":"section"},{"location":"misc/#Useful-helpers","page":"Miscellaneous","title":"Useful helpers","text":"","category":"section"},{"location":"misc/","page":"Miscellaneous","title":"Miscellaneous","text":"value","category":"page"},{"location":"misc/#VoronoiFVM.value","page":"Miscellaneous","title":"VoronoiFVM.value","text":"Extract value from dual number. Use to debug physics callbacks. Re-exported from ForwardDiff.jl\n\n\n\n\n\n","category":"function"},{"location":"misc/#Form-factor-calculatione","page":"Miscellaneous","title":"Form factor calculatione","text":"","category":"section"},{"location":"misc/#Velocity-projections","page":"Miscellaneous","title":"Velocity projections","text":"","category":"section"},{"location":"misc/","page":"Miscellaneous","title":"Miscellaneous","text":"edgevelocities\nbfacevelocities","category":"page"},{"location":"misc/#VoronoiFVM.edgevelocities","page":"Miscellaneous","title":"VoronoiFVM.edgevelocities","text":"edgevelocities(grid, velofunc)\n\n\nProject velocity onto grid edges,\n\n\n\n\n\n","category":"function"},{"location":"misc/#VoronoiFVM.bfacevelocities","page":"Miscellaneous","title":"VoronoiFVM.bfacevelocities","text":"bfacevelocities(grid, velofunc)\n\n\nProject velocity onto boundary face normals\n\n\n\n\n\n","category":"function"},{"location":"misc/#Additional-grid-methods","page":"Miscellaneous","title":"Additional grid methods","text":"","category":"section"},{"location":"misc/","page":"Miscellaneous","title":"Miscellaneous","text":"Modules = [VoronoiFVM]\nPages = [\"vfvm_xgrid.jl\"]","category":"page"},{"location":"misc/#VoronoiFVM.Grid","page":"Miscellaneous","title":"VoronoiFVM.Grid","text":"Grid=ExtendableGrids.simplexgrid\n\nRe-Export of ExtendableGrids.simplexgrid\n\n\n\n\n\n","category":"function"},{"location":"misc/#VoronoiFVM.cartesian!-Tuple{ExtendableGrid}","page":"Miscellaneous","title":"VoronoiFVM.cartesian!","text":"cartesian!(grid)\n\n\nSet coordinate system in grid to cartesian.\n\n\n\n\n\n","category":"method"},{"location":"misc/#VoronoiFVM.circular_symmetric!-Tuple{ExtendableGrid}","page":"Miscellaneous","title":"VoronoiFVM.circular_symmetric!","text":"circular_symmetric!(grid)\n\n\nSet coordinate system in grid to circular/cylindrical symmetry.\n\n\n\n\n\n","category":"method"},{"location":"misc/#VoronoiFVM.spherical_symmetric!-Tuple{ExtendableGrid}","page":"Miscellaneous","title":"VoronoiFVM.spherical_symmetric!","text":"spherical_symmetric!(grid)\n\n\nSet coordinate system in grid to spherical symmetry.\n\n\n\n\n\n","category":"method"},{"location":"misc/#Exception-types","page":"Miscellaneous","title":"Exception types","text":"","category":"section"},{"location":"misc/","page":"Miscellaneous","title":"Miscellaneous","text":"VoronoiFVM.AssemblyError\nVoronoiFVM.ConvergenceError\nVoronoiFVM.EmbeddingError\nVoronoiFVM.LinearSolverError","category":"page"},{"location":"misc/#VoronoiFVM.AssemblyError","page":"Miscellaneous","title":"VoronoiFVM.AssemblyError","text":"struct AssemblyError <: Exception\n\nException thrown if error occurred during assembly (e.g. domain error)\n\n\n\n\n\n","category":"type"},{"location":"misc/#VoronoiFVM.ConvergenceError","page":"Miscellaneous","title":"VoronoiFVM.ConvergenceError","text":"struct ConvergenceError <: Exception\n\nException thrown if Newton's method convergence fails.\n\n\n\n\n\n","category":"type"},{"location":"misc/#VoronoiFVM.EmbeddingError","page":"Miscellaneous","title":"VoronoiFVM.EmbeddingError","text":"struct EmbeddingError <: Exception\n\nException thrown if embedding fails\n\n\n\n\n\n","category":"type"},{"location":"misc/#VoronoiFVM.LinearSolverError","page":"Miscellaneous","title":"VoronoiFVM.LinearSolverError","text":"struct LinearSolverError <: Exception\n\nException thrown if error occurred during factorization.\n\n\n\n\n\n","category":"type"},{"location":"module_examples/Example311_HeatEquation_BoundaryDiffusion/#311:-Heat-Equation-with-boundary-diffusion","page":"311: Heat Equation with boundary diffusion","title":"311: Heat Equation with boundary diffusion","text":"","category":"section"},{"location":"module_examples/Example311_HeatEquation_BoundaryDiffusion/","page":"311: Heat Equation with boundary diffusion","title":"311: Heat Equation with boundary diffusion","text":"(source code)","category":"page"},{"location":"module_examples/Example311_HeatEquation_BoundaryDiffusion/","page":"311: Heat Equation with boundary diffusion","title":"311: Heat Equation with boundary diffusion","text":"module Example311_HeatEquation_BoundaryDiffusion\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\n\n\"\"\"\n We solve the following system\n\n ∂_tu - εΔu = 0 in [0,T] × Ω>\n ε∇u⋅ν = k(u-v) on [0,T] × Γ_1\n ε∇u⋅ν = 0 on [0,T] × (∂Ω ∖ Γ_1)\n ∂_tv -ε_ΓΔ_Γ v = f(x) +k(u-v) on [0,T] × Γ_1\n u(0) = 0.5 in {0} × Ω\n v(0) = 0.5 on {0} × Γ_1\n\"\"\"\n\nfunction main(n = 1; assembly = :edgewise)\n breg = 5 # boundary region number for surface diffusion\n\n hmin = 0.05 * 2.0^(-n + 1)\n hmax = 0.2 * 2.0^(-n + 1)\n XLeft = geomspace(0.0, 0.5, hmax, hmin)\n XRight = geomspace(0.5, 1.0, hmin, hmax)\n X = glue(XLeft, XRight)\n\n Z = geomspace(0.0, 1.0, hmin, 2 * hmax)\n\n grid = VoronoiFVM.Grid(X, X, Z)","category":"page"},{"location":"module_examples/Example311_HeatEquation_BoundaryDiffusion/","page":"311: Heat Equation with boundary diffusion","title":"311: Heat Equation with boundary diffusion","text":"parameters","category":"page"},{"location":"module_examples/Example311_HeatEquation_BoundaryDiffusion/","page":"311: Heat Equation with boundary diffusion","title":"311: Heat Equation with boundary diffusion","text":" eps = 1.0e0 # bulk heat conduction coefficient\n eps_surf = 1.0e-2 # surface diffusion coefficient\n k = 1.0 # transmission coefficient\n physics = VoronoiFVM.Physics(; flux = function (f, u, edge)\n f[1] = eps * (u[1, 1] - u[1, 2])\n end,\n bflux = function (f, u, edge)\n if edge.region == breg\n f[2] = eps_surf * (u[2, 1] - u[2, 2])\n else\n f[2] = 0.0\n end\n end,\n breaction = function (f, u, node)\n if node.region == breg\n f[1] = k * (u[1] - u[2])\n f[2] = k * (u[2] - u[1])\n else\n f[1] = 0.0\n f[2] = 0.0\n end\n end,\n bsource = function (f, bnode)\n x1 = bnode[1] - 0.5\n x2 = bnode[2] - 0.5\n x3 = bnode[3] - 0.5\n f[2] = 1.0e4 * exp(-20.0 * (x1^2 + x2^2 + x3^2))\n end, bstorage = function (f, u, node)\n if node.region == breg\n f[2] = u[2]\n end\n end, storage = function (f, u, node)\n f[1] = u[1]\n end)\n\n sys = VoronoiFVM.System(grid, physics; unknown_storage = :sparse, assembly)\n enable_species!(sys, 1, [1])\n enable_boundary_species!(sys, 2, [breg])\n\n function tran32!(a, b)\n a[1] = b[2]\n end\n\n bgrid2 = subgrid(grid, [breg]; boundary = true, transform = tran32!)\n\n U = unknowns(sys)\n U .= 0.5\n\n control = VoronoiFVM.NewtonControl()\n control.verbose = false\n control.reltol_linear = 1.0e-5\n control.max_lureuse = 10\n\n tstep = 0.1\n time = 0.0\n step = 0\n T = 1.0\n while time < T\n time = time + tstep\n U = solve(sys; inival = U, control, tstep)\n tstep *= 1.0\n step += 1\n end\n\n U_surf = view(U[2, :], bgrid2)\n sum(U_surf)\nend\n\nusing Test\nfunction runtests()\n testval = 1509.8109057757858\n testval = 1508.582565216869\n @test isapprox(main(; assembly = :edgewise), testval; rtol = 1.0e-12)\n @test isapprox(main(; assembly = :cellwise), testval; rtol = 1.0e-12)\nend\n\nend","category":"page"},{"location":"module_examples/Example311_HeatEquation_BoundaryDiffusion/","page":"311: Heat Equation with boundary diffusion","title":"311: Heat Equation with boundary diffusion","text":"","category":"page"},{"location":"module_examples/Example311_HeatEquation_BoundaryDiffusion/","page":"311: Heat Equation with boundary diffusion","title":"311: Heat Equation with boundary diffusion","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example106_NonlinearDiffusion1D/#106:-1D-Nonlinear-Diffusion-equation","page":"106: 1D Nonlinear Diffusion equation","title":"106: 1D Nonlinear Diffusion equation","text":"","category":"section"},{"location":"module_examples/Example106_NonlinearDiffusion1D/","page":"106: 1D Nonlinear Diffusion equation","title":"106: 1D Nonlinear Diffusion equation","text":"(source code)","category":"page"},{"location":"module_examples/Example106_NonlinearDiffusion1D/","page":"106: 1D Nonlinear Diffusion equation","title":"106: 1D Nonlinear Diffusion equation","text":"Solve the nonlinear diffusion equation","category":"page"},{"location":"module_examples/Example106_NonlinearDiffusion1D/","page":"106: 1D Nonlinear Diffusion equation","title":"106: 1D Nonlinear Diffusion equation","text":"partial_t u -Delta u^m = 0","category":"page"},{"location":"module_examples/Example106_NonlinearDiffusion1D/","page":"106: 1D Nonlinear Diffusion equation","title":"106: 1D Nonlinear Diffusion equation","text":"in Omega=(-11) with homogeneous Neumann boundary conditions using the implicit Euler method.","category":"page"},{"location":"module_examples/Example106_NonlinearDiffusion1D/","page":"106: 1D Nonlinear Diffusion equation","title":"106: 1D Nonlinear Diffusion equation","text":"This equation is also called \"porous medium equation\". The Barenblatt solution is an exact solution of this problem which for m>1 has a finite support. We initialize this problem with the exact solution for t=t_0=0001.","category":"page"},{"location":"module_examples/Example106_NonlinearDiffusion1D/","page":"106: 1D Nonlinear Diffusion equation","title":"106: 1D Nonlinear Diffusion equation","text":"(see Barenblatt, G. I. \"On nonsteady motions of gas and fluid in porous medium.\" Appl. Math. and Mech.(PMM) 16.1 (1952): 67-78.)","category":"page"},{"location":"module_examples/Example106_NonlinearDiffusion1D/","page":"106: 1D Nonlinear Diffusion equation","title":"106: 1D Nonlinear Diffusion equation","text":"module Example106_NonlinearDiffusion1D\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\n\nfunction barenblatt(x, t, m)\n tx = t^(-1.0 / (m + 1.0))\n xx = x * tx\n xx = xx * xx\n xx = 1 - xx * (m - 1) / (2.0 * m * (m + 1))\n if xx < 0.0\n xx = 0.0\n end\n return tx * xx^(1.0 / (m - 1.0))\nend\n\nfunction main(; n = 20, m = 2, Plotter = nothing, verbose = false,\n unknown_storage = :sparse, tend = 0.01, tstep = 0.0001, assembly = :edgewise)\n\n # Create a one-dimensional discretization\n h = 1.0 / convert(Float64, n / 2)\n X = collect(-1:h:1)\n grid = VoronoiFVM.Grid(X)\n\n # Flux function which describes the flux\n # between neighboring control volumes\n function flux!(f, u, edge)\n f[1] = u[1, 1]^m - u[1, 2]^m\n end\n\n # Storage term\n function storage!(f, u, node)\n f[1] = u[1]\n end\n\n # Create a physics structure\n physics = VoronoiFVM.Physics(; flux = flux!,\n storage = storage!)\n\n # Create a finite volume system - either\n # in the dense or the sparse version.\n # The difference is in the way the solution object\n # is stored - as dense or as sparse matrix\n sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly)\n\n # Add species 1 to region 1\n enable_species!(sys, 1, [1])\n\n # Create a solution array\n inival = unknowns(sys)\n t0 = 0.001\n\n # Broadcast the initial value\n inival[1, :] .= map(x -> barenblatt(x, t0, m), X)\n\n # Create solver control info for constant time step size\n control = VoronoiFVM.NewtonControl()\n control.verbose = verbose\n control.Δt_min = tstep\n control.Δt_max = tstep\n control.Δt = tstep\n control.Δu_opt = 1\n\n tsol = solve(sys; inival, times = [t0, tend], control)\n\n p = GridVisualizer(; Plotter = Plotter, layout = (1, 1), fast = true)\n for i = 1:length(tsol)\n time = tsol.t[i]\n scalarplot!(p[1, 1], grid, tsol[1, :, i]; title = @sprintf(\"t=%.3g\", time),\n color = :red, label = \"numerical\",\n markershape = :circle, markevery = 1)\n scalarplot!(p[1, 1], grid, map(x -> barenblatt(x, time, m), grid); clear = false,\n color = :green,\n label = \"exact\", markershape = :none)\n reveal(p)\n sleep(1.0e-2)\n end\n return sum(tsol[end])\nend\n\nusing Test\nfunction runtests()\n testval = 46.66666666647518\n @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval\nend\n\nend","category":"page"},{"location":"module_examples/Example106_NonlinearDiffusion1D/","page":"106: 1D Nonlinear Diffusion equation","title":"106: 1D Nonlinear Diffusion equation","text":"","category":"page"},{"location":"module_examples/Example106_NonlinearDiffusion1D/","page":"106: 1D Nonlinear Diffusion equation","title":"106: 1D Nonlinear Diffusion equation","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example151_Impedance1D/#151:-Impedance-calculation","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"","category":"section"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"(source code)","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"Same as Example150, but with new and more generic way of passing the parameter.","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"Impedance calculation for","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"C ut - (D ux)_x + Ru = 0 in (0,1) u(0,t)=1 + exp(iωt) u(1,t)=0","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"Measurement: I(t)= D u_x(1,t)","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"Steady state:","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"(D u0x)x + Ru0 = 0","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"u0(0,t)=1 u0(1,t)=0","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"Small signal ansatz for ω","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"u(x,t)= u0(x)+ ua(x) exp(iωt)","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"iωC ua - (D uax)x + R u_a =0 ua(0)=1 ua(1)=0","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"module Example151_Impedance1D\n\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\n\nfunction main(; nref = 0, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise,\n L = 1.0, R = 1.0, D = 1.0, C = 1.0,\n ω0 = 1.0e-3, ω1 = 5.0e1)","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"Create array which is refined close to 0","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":" h0 = 0.005 / 2.0^nref\n h1 = 0.1 / 2.0^nref\n\n X = VoronoiFVM.geomspace(0, L, h0, h1)","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"Create discretitzation grid","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":" grid = VoronoiFVM.Grid(X)","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"Create and fill data","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":" data = (R = R, D = D, C = C)","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"Declare constitutive functions","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":" flux = function (f, u, edge, data)\n f[1] = data.D * (u[1, 1] - u[1, 2])\n end\n\n storage = function (f, u, node, data)\n f[1] = data.C * u[1]\n end\n\n reaction = function (f, u, node, data)\n f[1] = data.R * u[1]\n end\n\n excited_bc = 1\n excited_bcval = 1.0\n excited_spec = 1\n meas_bc = 2\n\n bc = function (f, u, node, data)\n p = parameters(u)\n boundary_dirichlet!(f, u, node; region = excited_bc, value = p[1])\n boundary_dirichlet!(f, u, node; region = meas_bc, value = 0.0)\n end","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"Create discrete system and enable species","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":" sys = VoronoiFVM.System(grid; unknown_storage = unknown_storage,\n data = data,\n flux = flux,\n storage = storage,\n reaction = reaction,\n bcondition = bc,\n nparams = 1,\n species = 1, assembly = assembly)","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"Create test functions for current measurement","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":" factory = TestFunctionFactory(sys)\n measurement_testfunction = testfunction(factory, [excited_bc], [meas_bc])\n\n steadystate = solve(sys; inival = 0.0, params = [1.0])\n\n function meas_stdy(meas, U)\n u = reshape(U, sys)\n meas[1] = -VoronoiFVM.integrate_stdy(sys, measurement_testfunction, u)[excited_spec]\n nothing\n end\n\n function meas_tran(meas, U)\n u = reshape(U, sys)\n meas[1] = -VoronoiFVM.integrate_tran(sys, measurement_testfunction, u)[excited_spec]\n nothing\n end\n\n dmeas_stdy = measurement_derivative(sys, meas_stdy, steadystate)\n dmeas_tran = measurement_derivative(sys, meas_tran, steadystate)","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"Create Impeadancs system from steady state","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":" isys = VoronoiFVM.ImpedanceSystem(sys, steadystate)","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"Prepare recording of impedance results","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":" allomega = zeros(0)","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"for calculated data","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":" allI0 = zeros(Complex{Float64}, 0)\n allIL = zeros(Complex{Float64}, 0)","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"for exact data","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":" allIx0 = zeros(Complex{Float64}, 0)\n allIxL = zeros(Complex{Float64}, 0)\n\n ω = ω0\n\n UZ = unknowns(isys)\n while ω < ω1","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"solve impedance system","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":" solve!(UZ, isys, ω)","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"calculate approximate solution obtain measurement in frequency domain","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":" IL = impedance(isys, ω, steadystate, dmeas_stdy, dmeas_tran)","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"record approximate solution","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":" push!(allomega, ω)\n push!(allIL, IL)","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"record exact solution","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":" iω = 1im * ω\n z = sqrt(iω * data.C / data.D + data.R / data.D)\n eplus = exp(z * L)\n eminus = exp(-z * L)\n IxL = 2.0 * data.D * z / (eplus - eminus)\n\n push!(allIxL, 1 / IxL)","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"increase omega","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":" ω = ω * 1.1\n end\n\n p = GridVisualizer(; Plotter = Plotter)\n scalarplot!(p, real(allIxL), imag(allIxL); label = \"exact\", color = :red,\n linestyle = :dot)\n scalarplot!(p, real(allIL), imag(allIL); label = \"calc\", show = true, clear = false,\n color = :blue, linestyle = :solid)\n\n sum(allIL)\nend\n\nusing Test\nfunction runtests()\n testval = 57.92710286186797 + 23.163945443946027im\n @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval\nend\n\nend","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"","category":"page"},{"location":"module_examples/Example151_Impedance1D/","page":"151: Impedance calculation","title":"151: Impedance calculation","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example210_NonlinearPoisson2D_Reaction/#210:-2D-Nonlinear-Poisson-with-reaction","page":"210: 2D Nonlinear Poisson with reaction","title":"210: 2D Nonlinear Poisson with reaction","text":"","category":"section"},{"location":"module_examples/Example210_NonlinearPoisson2D_Reaction/","page":"210: 2D Nonlinear Poisson with reaction","title":"210: 2D Nonlinear Poisson with reaction","text":"(source code)","category":"page"},{"location":"module_examples/Example210_NonlinearPoisson2D_Reaction/","page":"210: 2D Nonlinear Poisson with reaction","title":"210: 2D Nonlinear Poisson with reaction","text":"module Example210_NonlinearPoisson2D_Reaction\n\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\n\nfunction main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise)\n h = 1.0 / convert(Float64, n)\n X = collect(0.0:h:1.0)\n Y = collect(0.0:h:1.0)\n\n grid = VoronoiFVM.Grid(X, Y)\n data = (eps = 1.0e-2, k = 1.0)\n\n function reaction!(f, u, node, data)\n f[1] = data.k * (u[1] - u[2])\n f[2] = data.k * (u[2] - u[1])\n end\n\n function flux!(f, u, edge, data)\n f[1] = data.eps * (u[1, 1] - u[1, 2])\n f[2] = data.eps * (u[2, 1] - u[2, 2])\n end\n\n function source!(f, node, data)\n x1 = node[1] - 0.5\n x2 = node[2] - 0.5\n f[1] = exp(-20 * (x1^2 + x2^2))\n end\n\n function storage!(f, u, node, data)\n f[1] = u[1]\n f[2] = u[2]\n end\n\n physics = VoronoiFVM.Physics(; data = data,\n flux = flux!,\n storage = storage!,\n reaction = reaction!,\n source = source!)\n\n sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly)\n\n enable_species!(sys, 1, [1])\n enable_species!(sys, 2, [1])\n\n inival = unknowns(sys)\n inival .= 0.0\n\n control = VoronoiFVM.NewtonControl()\n control.verbose = verbose\n tstep = 0.01\n time = 0.0\n istep = 0\n u15 = 0\n p = GridVisualizer(; Plotter = Plotter, layout = (2, 1))\n while time < 1\n time = time + tstep\n U = solve(sys; inival, control, tstep)\n inival .= U\n if verbose\n @printf(\"time=%g\\n\", time)\n end\n u15 = U[15]\n tstep *= 1.0\n istep = istep + 1\n scalarplot!(p[1, 1], grid, U[1, :]; clear = true)\n scalarplot!(p[2, 1], grid, U[2, :]; show = true)\n end\n return u15\nend\n\nusing Test\nfunction runtests()\n testval = 0.014566189535134827\n @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval\nend\n\nend","category":"page"},{"location":"module_examples/Example210_NonlinearPoisson2D_Reaction/","page":"210: 2D Nonlinear Poisson with reaction","title":"210: 2D Nonlinear Poisson with reaction","text":"","category":"page"},{"location":"module_examples/Example210_NonlinearPoisson2D_Reaction/","page":"210: 2D Nonlinear Poisson with reaction","title":"210: 2D Nonlinear Poisson with reaction","text":"This page was generated using Literate.jl.","category":"page"},{"location":"internal/#Internal-API","page":"Internal API","title":"Internal API","text":"","category":"section"},{"location":"internal/","page":"Internal API","title":"Internal API","text":"Besides of the interface methods for VoronoiFVMDiffEq, these are not exported and therefore should not be used outside of the package","category":"page"},{"location":"internal/#Wrapping-evaluators-for-physics-callbacks","page":"Internal API","title":"Wrapping evaluators for physics callbacks","text":"","category":"section"},{"location":"internal/","page":"Internal API","title":"Internal API","text":"VoronoiFVM.hasdata\nVoronoiFVM.AbstractEvaluator\nVoronoiFVM.ResEvaluator\nVoronoiFVM.ResEvaluator(physics::Any,symb::Symbol,uproto::Vector{Tv},geom::Any,nspec::Int) where Tv\nVoronoiFVM.evaluate!(e::VoronoiFVM.ResEvaluator)\nVoronoiFVM.evaluate!(e::VoronoiFVM.ResEvaluator, u::Any)\nVoronoiFVM.res(e::VoronoiFVM.ResEvaluator)\nVoronoiFVM.ResJacEvaluator\nVoronoiFVM.ResJacEvaluator(physics::Any,symb::Symbol,uproto::Vector{Tv},geom::Any,nspec::Int) where Tv\nVoronoiFVM.evaluate!(e::VoronoiFVM.ResJacEvaluator, u::Any)\nVoronoiFVM.res(e::VoronoiFVM.ResJacEvaluator)\nVoronoiFVM.jac(e::VoronoiFVM.ResJacEvaluator)\nVoronoiFVM.isnontrivial","category":"page"},{"location":"internal/#VoronoiFVM.hasdata","page":"Internal API","title":"VoronoiFVM.hasdata","text":"hasdata(physics)\n\n\nCheck if physics object has data\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.AbstractEvaluator","page":"Internal API","title":"VoronoiFVM.AbstractEvaluator","text":"abstract type AbstractEvaluator\n\nAbstract type for evaluator.\n\n\n\n\n\n","category":"type"},{"location":"internal/#VoronoiFVM.ResEvaluator","page":"Internal API","title":"VoronoiFVM.ResEvaluator","text":"struct ResEvaluator{Tv<:Number, Func<:Function, G} <: VoronoiFVM.AbstractEvaluator\n\nEvaluator for functions from physics. Allows to call different types of physic functions (flux, reaction, source) an provides a common interface to different function formats (with data, without data etc.)\n\nfwrap::Function: wrapper function in Format ready for Diffetential equations\ny::Vector{Tv} where Tv<:Number: pre-allocated result\ngeom::Any: Geometry object # geometry (node, edge...)\nnspec::Int64: number of species\nisnontrivial::Bool: Is the function not one of nofunc ot nofunc2\n\n\n\n\n\n","category":"type"},{"location":"internal/#VoronoiFVM.ResEvaluator-Union{Tuple{Tv}, Tuple{Any, Symbol, Vector{Tv}, Any, Int64}} where Tv","page":"Internal API","title":"VoronoiFVM.ResEvaluator","text":" ResEvaluator(physics,symb,uproto,geom,nspec)\n\nConstructor for ResEvaluator\n\nphysics Physics object\nsymb: symbol naming one of the functions in physics to be wrapped.\nuproto: solution vector prototype,\ngeom: node, edge...\nnspec: number of species\n\n\n\n\n\n","category":"method"},{"location":"internal/#VoronoiFVM.evaluate!-Tuple{VoronoiFVM.ResEvaluator}","page":"Internal API","title":"VoronoiFVM.evaluate!","text":"evaluate!(e::VoronoiFVM.ResEvaluator)\n\n\nCall function in evaluator, store result in predefined memory.\n\n\n\n\n\n","category":"method"},{"location":"internal/#VoronoiFVM.evaluate!-Tuple{VoronoiFVM.ResEvaluator, Any}","page":"Internal API","title":"VoronoiFVM.evaluate!","text":"evaluate!(e::VoronoiFVM.ResEvaluator, u)\n\n\nCall function in evaluator, store result in predefined memory.\n\n\n\n\n\n","category":"method"},{"location":"internal/#VoronoiFVM.res-Tuple{VoronoiFVM.ResEvaluator}","page":"Internal API","title":"VoronoiFVM.res","text":"res(\n e::VoronoiFVM.ResEvaluator\n) -> Vector{Tv} where Tv<:Number\n\n\nRetrieve evaluation result\n\n\n\n\n\n","category":"method"},{"location":"internal/#VoronoiFVM.ResJacEvaluator","page":"Internal API","title":"VoronoiFVM.ResJacEvaluator","text":"struct ResJacEvaluator{Tv<:Number, Func<:Function, Cfg, Res, G} <: VoronoiFVM.AbstractEvaluator\n\nEvaluator for functions from physics and their Jacobians. Allows to call different types of physic functions (flux, reaction, source) an provides a common interface to different function formats (with data, without data etc.)\n\nfwrap::Function: wrapper function in Format ready for Differential equations\nconfig::Any: ForwardDiff.JacobianConfig\nresult::Any: DiffResults.JacobianResult\ny::Vector{Tv} where Tv<:Number: pre-allocated result\ngeom::Any: Geometry object # geometry (node, edge...)\nnspec::Int64: number of species\nisnontrivial::Bool: Is the function not one of nofunc ot nofunc2\n\n\n\n\n\n","category":"type"},{"location":"internal/#VoronoiFVM.ResJacEvaluator-Union{Tuple{Tv}, Tuple{Any, Symbol, Vector{Tv}, Any, Int64}} where Tv","page":"Internal API","title":"VoronoiFVM.ResJacEvaluator","text":"ResJacEvaluator(physics, symb, uproto, geom, nspec)\n\n\nConstructor for ResJEvaluator\n\nphysics Physics object\nsymb: symbol naming one of the functions in physics to be wrapped.\nuproto: solution vector prototype,\ngeom: node, edge...\nnspec: number of species\n\n\n\n\n\n","category":"method"},{"location":"internal/#VoronoiFVM.evaluate!-Tuple{VoronoiFVM.ResJacEvaluator, Any}","page":"Internal API","title":"VoronoiFVM.evaluate!","text":"evaluate!(e::VoronoiFVM.ResJacEvaluator, u)\n\n\nCall function in evaluator, store result and jacobian in predefined memory.\n\n\n\n\n\n","category":"method"},{"location":"internal/#VoronoiFVM.res-Tuple{VoronoiFVM.ResJacEvaluator}","page":"Internal API","title":"VoronoiFVM.res","text":"res(e::VoronoiFVM.ResJacEvaluator) -> Any\n\n\nRetrieve evaluation result\n\n\n\n\n\n","category":"method"},{"location":"internal/#VoronoiFVM.jac-Tuple{VoronoiFVM.ResJacEvaluator}","page":"Internal API","title":"VoronoiFVM.jac","text":"jac(e::VoronoiFVM.ResJacEvaluator) -> Any\n\n\nRetrieve Jacobian\n\n\n\n\n\n","category":"method"},{"location":"internal/#VoronoiFVM.isnontrivial","page":"Internal API","title":"VoronoiFVM.isnontrivial","text":"isnontrivial(e::VoronoiFVM.AbstractEvaluator) -> Any\n\n\nDoes calling the evaluator giva nontrivial (nonzero) result?\n\n\n\n\n\n","category":"function"},{"location":"internal/#Global-node-and-edge-assembly-loops","page":"Internal API","title":"Global node and edge assembly loops","text":"","category":"section"},{"location":"internal/","page":"Internal API","title":"Internal API","text":"VoronoiFVM.AbstractAssemblyData\nVoronoiFVM.CellwiseAssemblyData\nVoronoiFVM.EdgewiseAssemblyData\nVoronoiFVM.nodebatch\nVoronoiFVM.noderange\nVoronoiFVM.edgebatch\nVoronoiFVM.edgerange\nVoronoiFVM._fill!","category":"page"},{"location":"internal/#VoronoiFVM.AbstractAssemblyData","page":"Internal API","title":"VoronoiFVM.AbstractAssemblyData","text":"abstract type AbstractAssemblyData{Tv, Ti}\n\nAssembly of residual and Jacobian comes in two flavors, cellwise and edgewise assembly loops, see VoronoiFVM.System(grid;kwargs...). The necessary data for assembly are held in structs which are subtypes of AbstractAssemblyData.\n\n\n\n\n\n","category":"type"},{"location":"internal/#VoronoiFVM.CellwiseAssemblyData","page":"Internal API","title":"VoronoiFVM.CellwiseAssemblyData","text":"struct CellwiseAssemblyData{Tv, Ti} <: VoronoiFVM.AbstractAssemblyData{Tv, Ti}\n\nData for cellwise assembly.\n\nnodefactors::Matrix: Precomputed geometry factors for cell nodes. This is a ncells x nnodes_per_cell full matrix.\n\nedgefactors::Matrix: Precomputed geometry factors for cell edges This is a ncells x nedge_per_cell full matrix.\n\n\n\n\n\n","category":"type"},{"location":"internal/#VoronoiFVM.EdgewiseAssemblyData","page":"Internal API","title":"VoronoiFVM.EdgewiseAssemblyData","text":"struct EdgewiseAssemblyData{Tv, Ti} <: VoronoiFVM.AbstractAssemblyData{Tv, Ti}\n\nnodefactors::SparseArrays.SparseMatrixCSC{Tv, Ti} where {Tv, Ti}: Precomputed geometry factors for nodes. This is a nnodes x nregions sparse matrix.\n\nedgefactors::SparseArrays.SparseMatrixCSC{Tv, Ti} where {Tv, Ti}: Precomputed geometry factors for edges This is a nedges x nregions sparse matrix.\n\n\n\n\n\n","category":"type"},{"location":"internal/#VoronoiFVM.nodebatch","page":"Internal API","title":"VoronoiFVM.nodebatch","text":"nodebatch(assemblydata)\n\nOuter range for node assembly loop. \n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.noderange","page":"Internal API","title":"VoronoiFVM.noderange","text":"noderange(assemblydata, i)\n\nInner range for node assembly loop. \n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.edgebatch","page":"Internal API","title":"VoronoiFVM.edgebatch","text":"nodebatch(assemblydata)\n\nOuter range for edge assembly loop. \n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.edgerange","page":"Internal API","title":"VoronoiFVM.edgerange","text":"edgerange(assemblydata, i)\n\nInner range for edge assembly loop. \n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM._fill!","page":"Internal API","title":"VoronoiFVM._fill!","text":"_fill!(node, asmdata, inode, icell)\n\n\nFill node with the help of assemblydata.\n\n\n\n\n\n_fill!(node, asmdata, ibnode, ibface)\n\n\nFill boundary node with the help of assemblydata.\n\n\n\n\n\n_fill!(edge, asmdata, iedge, icell)\n\n\nFill edge with the help of assemblydata.\n\n\n\n\n\n_fill!(bedge, asmdata, ibedge, ibface)\n\n\nFill boundary edge with the help of assemblydata.\n\n\n\n\n\n_fill!(node, asmdata, k, inode)\n\n\nFill node with the help of assemblydata.\n\n\n\n\n\n_fill!(edge, asmdata, k, iedge)\n\n\nFill edge with the help of assemblydata.\n\n\n\n\n\n","category":"function"},{"location":"internal/#Local-node-and-edge-assembly-loops","page":"Internal API","title":"Local node and edge assembly loops","text":"","category":"section"},{"location":"internal/","page":"Internal API","title":"Internal API","text":"Local assembly methods organize the assembly of data to those degrees of freedom (dofs) which are defined for a given node or edge. E.g. for an node residual for nspec defined species, only those entries need to be assembled into the global residual vector which correspond to actually defined degrees of freedom. ","category":"page"},{"location":"internal/","page":"Internal API","title":"Internal API","text":"Similarly for nspec x nspec node Jacobian, an for the nparam x nspec parameter derivatives.","category":"page"},{"location":"internal/","page":"Internal API","title":"Internal API","text":"These local assembly methods organize the correct loops and call back to the concrete assembly methods passed to them. These receive global degrees of freedom and the local species numbers to be handled. The callbacks can be used as well for other purposes than assembly","category":"page"},{"location":"internal/","page":"Internal API","title":"Internal API","text":"VoronoiFVM.assemble_res_jac\nVoronoiFVM.assemble_res","category":"page"},{"location":"internal/#VoronoiFVM.assemble_res_jac","page":"Internal API","title":"VoronoiFVM.assemble_res_jac","text":"assemble_res_jac(node, system, asm_res, asm_jac, asm_param)\n\n\nAssemble residual and jacobian for node functions. Parameters:\n\nsystem: System to be worked with\nnode: node\nasm_jac(idof,jdof,ispec,jspec): e.g. assemble entry ispec,jspec of local jacobian into entry idof,jdof of global matrix\nasm_param(idof,ispec,iparam) shall assemble parameter derivatives\n\n\n\n\n\nassemble_res_jac(bnode, system, asm_res, asm_jac, asm_param)\n\n\nAssemble residual and jacobian for boundary node functions. See assemble_res_jac for more explanations.\n\n\n\n\n\nassemble_res_jac(edge, system, asm_res, asm_jac, asm_param)\n\n\nAssemble residual and jacobian for edge (flux) functions. Parameters:\n\nsystem: System to be worked with\nedge: edge\nasm_res(idofK,idofL,ispec): e.g. assemble local ispec to global degrees of freedom in unknowns\nasm_jac(idofK,jdofK,idofL,jdofL,ispec,jspec): e.g. assemble entry ispec,jspec of local jacobian into entry four entries defined by idofK and idofL of global matrix\nasm_param(idofK,idofL,ispec,iparam) shall assemble parameter derivatives\n\n\n\n\n\nassemble_res_jac(bedge, system, asm_res, asm_jac, asm_param)\n\n\nAssemble residual and jacobian for boundary edge (flux) functions. See assemble_res_jac for more explanations.\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.assemble_res","page":"Internal API","title":"VoronoiFVM.assemble_res","text":"assemble_res(node, system, asm_res)\n\n\nAssemble residual for node functions. See assemble_res_jac for more explanations.\n\n\n\n\n\nassemble_res(bnode, system, asm_res)\n\n\nAssemble residual for boundary node functions. See assemble_res_jac for more explanations.\n\n\n\n\n\nassemble_res(edge, system, asm_res)\n\n\nAssemble residual for edge (flux) functions. See assemble_res_jac for more explanations.\n\n\n\n\n\nassemble_res(bedge, system, asm_res)\n\n\nAssemble residual for boundary edge (flux) functions. See assemble_res_jac for more explanations.\n\n\n\n\n\n","category":"function"},{"location":"internal/#Degree-of-Freedom-management","page":"Internal API","title":"Degree of Freedom management","text":"","category":"section"},{"location":"internal/","page":"Internal API","title":"Internal API","text":"We distinguish","category":"page"},{"location":"internal/","page":"Internal API","title":"Internal API","text":"active degrees of freedom: these are the actual degrees of freedom \ndegrees of freedom (dof) potential degrees of freedom - the may be active dofs or dummy ones With sparse arrays there are no dummy ones, with dense arrays dummy are maske in the node_dof field\nspecies: each degree of freedom has associated the species it represents and the node index where it is localized ","category":"page"},{"location":"internal/","page":"Internal API","title":"Internal API","text":"VoronoiFVM.isnodespecies\nVoronoiFVM.isregionspecies\nVoronoiFVM.firstnodedof\nVoronoiFVM.lastnodedof\nVoronoiFVM.getspecies\nVoronoiFVM.getnodedof","category":"page"},{"location":"internal/#VoronoiFVM.isnodespecies","page":"Internal API","title":"VoronoiFVM.isnodespecies","text":"isnodespecies(system, ispec, inode)\n\n\nCheck if species is defined in node.\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.isregionspecies","page":"Internal API","title":"VoronoiFVM.isregionspecies","text":"isregionspecies(system, ispec, ireg)\n\n\nCheck if species is defined in region.\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.firstnodedof","page":"Internal API","title":"VoronoiFVM.firstnodedof","text":"firstnodedof(system, inode)\n\nGet first degree of freedom associated with node.\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.lastnodedof","page":"Internal API","title":"VoronoiFVM.lastnodedof","text":"lastnodedof(system, inode)\n\nGet last degree of freedom associated with node.\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.getspecies","page":"Internal API","title":"VoronoiFVM.getspecies","text":"getspecies(system,idof)\n\nGet species associated to degree of freedom\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.getnodedof","page":"Internal API","title":"VoronoiFVM.getnodedof","text":"getnodedof(system,ispec,inode)\n\nGet active or dummy degree of freedom associated with node and species\n\n\n\n\n\n","category":"function"},{"location":"internal/#Abstract-geometry-data","page":"Internal API","title":"Abstract geometry data","text":"","category":"section"},{"location":"internal/","page":"Internal API","title":"Internal API","text":"VoronoiFVM.AbstractGeometryItem\nVoronoiFVM.AbstractNode\nVoronoiFVM.AbstractNodeData\nVoronoiFVM.AbstractEdge\nVoronoiFVM.AbstractEdgeData","category":"page"},{"location":"internal/#VoronoiFVM.AbstractGeometryItem","page":"Internal API","title":"VoronoiFVM.AbstractGeometryItem","text":"abstract type AbstractGeometryItem{Tc<:Number, Tp<:Number, Ti<:Integer}\n\nAbstract type for geometry items (node,bnode,edge, bedge)\n\n\n\n\n\n","category":"type"},{"location":"internal/#VoronoiFVM.AbstractNode","page":"Internal API","title":"VoronoiFVM.AbstractNode","text":"abstract type AbstractNode{Tc<:Number, Tp<:Number, Ti<:Integer} <: VoronoiFVM.AbstractGeometryItem{Tc<:Number, Tp<:Number, Ti<:Integer}\n\nAbstract type for nodes. \n\nnode[idim] gives the the corresponding coordinate.\n\n\n\n\n\n","category":"type"},{"location":"internal/#VoronoiFVM.AbstractNodeData","page":"Internal API","title":"VoronoiFVM.AbstractNodeData","text":"abstract type AbstractNodeData{Tv<:Number} <: AbstractArray{Tv<:Number, 1}\n\nAbstract type for data on nodes. u[ispec] accesses value of species at this node.\n\n\n\n\n\n","category":"type"},{"location":"internal/#VoronoiFVM.AbstractEdge","page":"Internal API","title":"VoronoiFVM.AbstractEdge","text":"abstract type AbstractEdge{Tv<:Number, Tp<:Number, Ti<:Integer} <: VoronoiFVM.AbstractGeometryItem{Tv<:Number, Tp<:Number, Ti<:Integer}\n\nAbstract type for edges \n\nedge[idim,inode] gives coordinate of node.\n\n\n\n\n\n","category":"type"},{"location":"internal/#VoronoiFVM.AbstractEdgeData","page":"Internal API","title":"VoronoiFVM.AbstractEdgeData","text":"abstract type AbstractEdgeData{Tv<:Number} <: AbstractArray{Tv<:Number, 2}\n\nAbstract type for data on edges. u[ispec,inode] accesses value of species at corresponding node.\n\n\n\n\n\n","category":"type"},{"location":"internal/#Global-assembly-and-helpers","page":"Internal API","title":"Global assembly & helpers","text":"","category":"section"},{"location":"internal/","page":"Internal API","title":"Internal API","text":"VoronoiFVM.eval_and_assemble\nVoronoiFVM._addnz\nVoronoiFVM._add","category":"page"},{"location":"internal/#VoronoiFVM.eval_and_assemble","page":"Internal API","title":"VoronoiFVM.eval_and_assemble","text":"eval_and_assemble(\n system,\n U,\n UOld,\n F,\n time,\n tstep,\n λ,\n params;\n edge_cutoff\n)\n\n\nMain assembly method.\n\nEvaluate solution with result in right hand side F and assemble Jacobi matrix into system.matrix.\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM._addnz","page":"Internal API","title":"VoronoiFVM._addnz","text":"_addnz(matrix, i, j, v, fac)\n\n\nAdd value v*fac to matrix if v is nonzero\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM._add","page":"Internal API","title":"VoronoiFVM._add","text":"_add(U::Array{Tv, 2}, idof, val) -> Any\n\n\nAdd residual value into global degree of freedom\n\n\n\n\n\n_add(U::VoronoiFVM.SparseSolutionArray, idof, val) -> Any\n\n\nAdd residual value into global degree of freedom\n\n\n\n\n\n","category":"function"},{"location":"internal/#Interface-methods-for-VoronoiFVMDiffEq.jl","page":"Internal API","title":"Interface methods for VoronoiFVMDiffEq.jl","text":"","category":"section"},{"location":"internal/","page":"Internal API","title":"Internal API","text":"VoronoiFVM._eval_res_jac!\neval_rhs!\neval_jacobian!\nmass_matrix\nprepare_diffeq!","category":"page"},{"location":"internal/#VoronoiFVM._eval_res_jac!","page":"Internal API","title":"VoronoiFVM._eval_res_jac!","text":"_eval_res_jac!(sys, u, t)\n\n\nEvaluate functiaon and Jacobian at u if they have not been evaluated before at u. See https://github.com/SciML/DifferentialEquations.jl/issues/521 for discussion of another way to do this.\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.eval_rhs!","page":"Internal API","title":"VoronoiFVM.eval_rhs!","text":"eval_rhs!(du, u, sys, t)\n\n\nInterpret the discrete problem as an ODE/DAE problem. Provide the rhs function for ODEFunction.\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.eval_jacobian!","page":"Internal API","title":"VoronoiFVM.eval_jacobian!","text":"eval_jacobian!(J, u, sys, t)\n\n\nInterpret the discrete problem as an ODE/DAE problem. Provide the jacobi matrix calculation function for ODEFunction\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.mass_matrix","page":"Internal API","title":"VoronoiFVM.mass_matrix","text":"mass_matrix(system)\n\n\nCalculate the mass matrix for use with ODEFunction. Return a Diagonal matrix if it occurs to be diagonal, otherwise return a SparseMatrixCSC.\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.prepare_diffeq!","page":"Internal API","title":"VoronoiFVM.prepare_diffeq!","text":"prepare_diffeq!(sys, jacval, tjac)\n\n\nPrepare system for use with VoronoiFVMDiffEq.\n\njacval: value at which to evaluate jacobian to obtatin prototype\ntjac: time moment for jacobian\n\nReturns a prototype for the jacobian.\n\n\n\n\n\n","category":"function"},{"location":"module_examples/Example105_NonlinearPoisson1D/#105:-1D-Nonlinear-Poisson-equation","page":"105: 1D Nonlinear Poisson equation","title":"105: 1D Nonlinear Poisson equation","text":"","category":"section"},{"location":"module_examples/Example105_NonlinearPoisson1D/","page":"105: 1D Nonlinear Poisson equation","title":"105: 1D Nonlinear Poisson equation","text":"(source code)","category":"page"},{"location":"module_examples/Example105_NonlinearPoisson1D/","page":"105: 1D Nonlinear Poisson equation","title":"105: 1D Nonlinear Poisson equation","text":"Solve the nonlinear Poisson equation","category":"page"},{"location":"module_examples/Example105_NonlinearPoisson1D/","page":"105: 1D Nonlinear Poisson equation","title":"105: 1D Nonlinear Poisson equation","text":"-nabla varepsilon nabla u + e^u-e^-u = f","category":"page"},{"location":"module_examples/Example105_NonlinearPoisson1D/","page":"105: 1D Nonlinear Poisson equation","title":"105: 1D Nonlinear Poisson equation","text":"in Omega=(01) with boundary condition u(0)=0 and u(1)=1 with","category":"page"},{"location":"module_examples/Example105_NonlinearPoisson1D/","page":"105: 1D Nonlinear Poisson equation","title":"105: 1D Nonlinear Poisson equation","text":"f(x)=\n begincases\n 1x05\n -1 x05\n endcases","category":"page"},{"location":"module_examples/Example105_NonlinearPoisson1D/","page":"105: 1D Nonlinear Poisson equation","title":"105: 1D Nonlinear Poisson equation","text":"This stationary problem is an example of a nonlinear Poisson equation or Poisson-Boltzmann equation. Such equation occur e.g. in simulations of electrochemical systems and semiconductor devices.","category":"page"},{"location":"module_examples/Example105_NonlinearPoisson1D/","page":"105: 1D Nonlinear Poisson equation","title":"105: 1D Nonlinear Poisson equation","text":"module Example105_NonlinearPoisson1D\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\n\nfunction main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise)\n\n # Create a one-dimensional discretization\n h = 1.0 / convert(Float64, n)\n grid = VoronoiFVM.Grid(collect(0:h:1))\n\n # A parameter which is \"passed\" to the flux function via scope\n ϵ = 1.0e-3\n\n # Flux function which describes the flux\n # between neighboring control volumes\n function flux!(f, u, edge)\n f[1] = ϵ * (u[1, 1] - u[1, 2])\n end\n\n # Source term\n function source!(f, node)\n if node[1] <= 0.5\n f[1] = 1\n else\n f[1] = -1\n end\n end\n\n # Reaction term\n function reaction!(f, u, node)\n f[1] = exp(u[1]) - exp(-u[1])\n end\n\n # Create a physics structure\n physics = VoronoiFVM.Physics(; flux = flux!,\n source = source!,\n reaction = reaction!)\n\n # Create a finite volume system - either\n # in the dense or the sparse version.\n # The difference is in the way the solution object\n # is stored - as dense or as sparse matrix\n\n sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly)\n\n # Add species 1 to region 1\n enable_species!(sys, 1, [1])\n\n # Set boundary conditions\n boundary_dirichlet!(sys, 1, 1, 0.0)\n boundary_dirichlet!(sys, 1, 2, 1.0)\n\n # Create a solution array\n inival = unknowns(sys; inival = 0.5)\n\n # Stationary solution of the problem\n solution = solve(sys; inival, verbose)\n\n scalarplot(grid, solution[1, :]; title = \"Nonlinear Poisson\", Plotter = Plotter)\n\n return sum(solution)\nend\n\nusing Test\nfunction runtests()\n testval = 1.5247901344230088\n @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval\nend\n\nend","category":"page"},{"location":"module_examples/Example105_NonlinearPoisson1D/","page":"105: 1D Nonlinear Poisson equation","title":"105: 1D Nonlinear Poisson equation","text":"","category":"page"},{"location":"module_examples/Example105_NonlinearPoisson1D/","page":"105: 1D Nonlinear Poisson equation","title":"105: 1D Nonlinear Poisson equation","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example120_ThreeRegions1D/#120:-Differing-species-sets-in-regions,-1D","page":"120: Differing species sets in regions, 1D","title":"120: Differing species sets in regions, 1D","text":"","category":"section"},{"location":"module_examples/Example120_ThreeRegions1D/","page":"120: Differing species sets in regions, 1D","title":"120: Differing species sets in regions, 1D","text":"(source code)","category":"page"},{"location":"module_examples/Example120_ThreeRegions1D/","page":"120: Differing species sets in regions, 1D","title":"120: Differing species sets in regions, 1D","text":"module Example120_ThreeRegions1D\n\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\nusing LinearSolve\n\nfunction main(; n = 30, Plotter = nothing, plot_grid = false, verbose = false,\n unknown_storage = :sparse, tend = 10,\n rely_on_corrections = false, assembly = :edgewise)\n h = 3.0 / (n - 1)\n X = collect(0:h:3.0)\n grid = VoronoiFVM.Grid(X)\n cellmask!(grid, [0.0], [1.0], 1)\n cellmask!(grid, [1.0], [2.1], 2)\n cellmask!(grid, [1.9], [3.0], 3)\n\n subgrid1 = subgrid(grid, [1])\n subgrid2 = subgrid(grid, [1, 2, 3])\n subgrid3 = subgrid(grid, [3])\n\n if plot_grid\n plotgrid(grid; Plotter = Plotter)\n return\n end\n\n eps = [1, 1, 1]\n k = [1, 1, 1]\n\n function reaction(f, u, node)\n if node.region == 1\n f[1] = k[1] * u[1]\n f[2] = -k[1] * u[1]\n elseif node.region == 3\n f[2] = k[3] * u[2]\n f[3] = -k[3] * u[2]\n else\n f[1] = 0\n end\n end\n\n function source(f, node)\n if node.region == 1\n f[1] = 1.0e-4 * (3.0 - node[1])\n end\n end\n\n if rely_on_corrections\n # Since 0.17.0 one can\n # write into the result also where\n # the corresponding species has not been enabled\n # Species information is used to prevent the assembly.\n flux = function (f, u, edge)\n for i = 1:3\n f[i] = eps[i] * (u[i, 1] - u[i, 2])\n end\n end\n\n storage = function (f, u, node)\n f .= u\n end\n else\n # This is the \"old\" way:\n # Write into result only where\n # the corresponding species has been enabled\n flux = function (f, u, edge)\n if edge.region == 1\n f[1] = eps[1] * (u[1, 1] - u[1, 2])\n f[2] = eps[2] * (u[2, 1] - u[2, 2])\n elseif edge.region == 2\n f[2] = eps[2] * (u[2, 1] - u[2, 2])\n elseif edge.region == 3\n f[2] = eps[2] * (u[2, 1] - u[2, 2])\n f[3] = eps[3] * (u[3, 1] - u[3, 2])\n end\n end\n\n storage = function (f, u, node)\n if node.region == 1\n f[1] = u[1]\n f[2] = u[2]\n elseif node.region == 2\n f[2] = u[2]\n elseif node.region == 3\n f[2] = u[2]\n f[3] = u[3]\n end\n end\n end\n\n sys = VoronoiFVM.System(grid; flux, reaction, storage, source,\n unknown_storage = unknown_storage, assembly = assembly)\n\n enable_species!(sys, 1, [1])\n enable_species!(sys, 2, [1, 2, 3])\n enable_species!(sys, 3, [3])\n\n boundary_dirichlet!(sys, 3, 2, 0.0)\n\n testval = 0\n p = GridVisualizer(; Plotter = Plotter, layout = (1, 1))\n\n testval = 0.0\n function plot_timestep(U, Uold, time, Δt)\n U1 = view(U[1, :], subgrid1)\n U2 = view(U[2, :], subgrid2)\n U3 = view(U[3, :], subgrid3)\n\n testval += sum(U2)\n\n if Plotter == nothing\n return\n end\n\n scalarplot!(p[1, 1], subgrid1, U1; label = \"spec1\", color = (0.5, 0, 0),\n xlimits = (0, 3), flimits = (0, 1e-3),\n title = @sprintf(\"three regions t=%.3g\", time))\n scalarplot!(p[1, 1], subgrid2, U2; label = \"spec2\", color = (0.0, 0.5, 0),\n clear = false)\n scalarplot!(p[1, 1], subgrid3, U3; label = \"spec3\", color = (0.0, 0.0, 0.5),\n clear = false, show = true)\n end\n\n tsol = solve(sys; inival = 0, times = (0, tend), post = plot_timestep, verbose = verbose, Δu_opt = 1.0e-5,\n method_linear=KLUFactorization())\n\n return testval\nend\n\nusing Test\n\nfunction runtests()\n testval = 0.359448515181824\n @test main(; unknown_storage = :sparse, rely_on_corrections = false, assembly = :edgewise) ≈ testval\n @test main(; unknown_storage = :dense, rely_on_corrections = false, assembly = :edgewise) ≈ testval\n @test main(; unknown_storage = :sparse, rely_on_corrections = true, assembly = :edgewise) ≈ testval\n @test main(; unknown_storage = :dense, rely_on_corrections = true, assembly = :edgewise) ≈ testval\n @test main(; unknown_storage = :sparse, rely_on_corrections = false, assembly = :cellwise) ≈ testval\n @test main(; unknown_storage = :dense, rely_on_corrections = false, assembly = :cellwise) ≈ testval\n @test main(; unknown_storage = :sparse, rely_on_corrections = true, assembly = :cellwise) ≈ testval\n @test main(; unknown_storage = :dense, rely_on_corrections = true, assembly = :cellwise) ≈ testval\nend\n\nend","category":"page"},{"location":"module_examples/Example120_ThreeRegions1D/","page":"120: Differing species sets in regions, 1D","title":"120: Differing species sets in regions, 1D","text":"","category":"page"},{"location":"module_examples/Example120_ThreeRegions1D/","page":"120: Differing species sets in regions, 1D","title":"120: Differing species sets in regions, 1D","text":"This page was generated using Literate.jl.","category":"page"},{"location":"changes/#Changes","page":"Changes","title":"Changes","text":"","category":"section"},{"location":"changes/#v1.19.0-Feb-01-2024","page":"Changes","title":"v1.19.0 Feb 01 2024","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Enable equation block preconditioning for sparse unknown storage","category":"page"},{"location":"changes/#v1.18.0-Feb-01-2024","page":"Changes","title":"v1.18.0 Feb 01 2024","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Re-shoring of OrdinaryDiffEq interface, no need of VoronoiFVMDiffEq.jl anymore It appeared that it is sufficient to depend on SciMLBase for this, and all tests can be done with OrdinaryDiffEq.jl","category":"page"},{"location":"changes/#v1.17.1-Jan-30,-2024","page":"Changes","title":"v1.17.1 Jan 30, 2024","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Bugfix for boundary node factors\nBugfix with types for RecursiveArrayTools","category":"page"},{"location":"changes/#v1.16.0-Dec-15,-2023","page":"Changes","title":"v1.16.0 Dec 15, 2023","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Bugfix for assembly of outflow bc\nBugfix for matrixtype=:banded\nUpdated plothistory: ","category":"page"},{"location":"changes/#v1.15.0-Dec-1,-2023","page":"Changes","title":"v1.15.0 Dec 1, 2023","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Adjusted time/embedding stepping scheme, added num_final_steps to VoronoiFVM.SolverControl. This may lead to sligthly different results when solving time dependent problems. Some unit test values have been adapted. Before, accidentally, very small time steps at the end of an evolution were possible.","category":"page"},{"location":"changes/#v1.14.0-Nov-27,-2023","page":"Changes","title":"v1.14.0 Nov 27, 2023","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Add Δu_max_factor and Δt_decrease to VoronoiFVM.SolverControl. Before (with values 2.0 and 0.5, respectively) the were fixed. New Δu_max_factor=1.2 default.\nAdd history to VoronoiFVM.TransientSolution, prepare deprecation of system.history\nAdd plothistory method","category":"page"},{"location":"changes/#v1.13.0-July-24,-2023","page":"Changes","title":"v1.13.0 July 24, 2023","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Add nodevolumes method","category":"page"},{"location":"changes/#v1.12.0-July-22,-2023","page":"Changes","title":"v1.12.0 July 22, 2023","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Add functionality for outflow boundary conditions","category":"page"},{"location":"changes/#v1.11.0-July-17,-2023","page":"Changes","title":"v1.11.0 July 17, 2023","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Add calc_divergences method for checking velocity field divergences\nFix form factor calculation and velocity projecion for unstructructured grids and cylindrical symmetry","category":"page"},{"location":"changes/#v1.10.0-July-11,-2023","page":"Changes","title":"v1.10.0 July 11, 2023","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Use AbstractTransientSolution in gridvisualize stuff","category":"page"},{"location":"changes/#v1.9.0-June-27,-2023","page":"Changes","title":"v1.9.0 June 27, 2023","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"With control.handle_exceptions=true, in case of a failing step, time stepping and embeding now returns the solution calculated so far instead of throwing an error","category":"page"},{"location":"changes/#v1.8.0-June-20,-2023","page":"Changes","title":"v1.8.0 June 20, 2023","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"LinearSolve 2.x","category":"page"},{"location":"changes/#v1.7.0-May-17,-2023","page":"Changes","title":"v1.7.0 May 17, 2023","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Discrete Sobolev norms","category":"page"},{"location":"changes/#v1.6.0-May-12,-2023","page":"Changes","title":"v1.6.0 May 12, 2023","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Rework linear solver strategies - prevent combinatorial explosion. E.g. gmres_iluzero is now GMRESIteration(ILUZeroPreconditioner()) etc.","category":"page"},{"location":"changes/#v1.5.0-May-9,-2023","page":"Changes","title":"v1.5.0 May 9, 2023","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Introduced solver strategies like gmres_iluzero(), direct_umfpack() etc. See documentation of the module VoronoiFVM.SolverStrategies. More to come.\nedgewise assembly - faster in particular for 3D\nPlan: edgewise assembly seems to be non-breaking, if this is confirmed, will be made default in 1.6 or (if it appears to be breaking for some) in 2.0.","category":"page"},{"location":"changes/#v1.4.0-May-3,-2023","page":"Changes","title":"v1.4.0 May 3, 2023","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"equation-block preconditioning support with the help of ExtendableSparse.jl","category":"page"},{"location":"changes/#v1.3.0-April-13,-2023","page":"Changes","title":"v1.3.0 April 13, 2023","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"inplace_linsolve! for dense linear system solution in flux functions\nMixture flow example 510","category":"page"},{"location":"changes/#v1.2.0-March-17,-2023","page":"Changes","title":"v1.2.0 March 17, 2023","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Initialization of quantities, create unknowns using Base.map","category":"page"},{"location":"changes/#v1.1.0-Feb-22,-2023","page":"Changes","title":"v1.1.0 Feb 22, 2023","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Edge reactions, Joule heating","category":"page"},{"location":"changes/#v1.0.0-Feb-22,-2023","page":"Changes","title":"v1.0.0 Feb 22, 2023","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"full LinearSolve compatibility","category":"page"},{"location":"changes/#v0.19.0-Jan-31,-2023","page":"Changes","title":"v0.19.0 Jan 31, 2023","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"This is a breaking release. Implementations using default solver settings should continue to work without modifications, albeit possibly showing deprecation and allocation warnings. Really breaking is control of iterative linear solvers and allocation checks.","category":"page"},{"location":"changes/#Breaking","page":"Changes","title":"Breaking","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Make solve a method of CommonSolve.solve (and re-export it). \nRely on LinearSolve.jl for linear system solution including control of iterative solvers.\nNew verbosity handling. verbose can now be a Bool or a String of flag characters, allowing for control of different output categories. I would love to do this via logging, but there is still a long way to go IMHO \nAllocation check is active by default with warnings instead of throwing an error. These warnings can be muted by passing a verbose string without 'a'. This is now the only control in this respect. All check_allocs methods/kwargs, control via environment variables have been removed.\nDeprecation warnings can be switched off by passing a verbose string without 'd'.\nImprove iteration logging etc., allow for logging of linear iterations ('l' flag character)","category":"page"},{"location":"changes/#Deprecations","page":"Changes","title":"Deprecations","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Deprecated all VoronoiFVM.solve methods with signatures others than solve(system; kwargs...) which renders them incompatible to the philosophy of `CommonSolve. Updated examples accordingly.\nDeprecated the following entries of SolverControl/solve kwargs: :tol_absolute => :abstol, :tol_relative => :reltol, :damp => :damp_initial, :damp_grow => :damp_growth :max_iterations => e:maxiters :tol_linear => :reltol_linear :max_lureuse =>\nNewtonControl","category":"page"},{"location":"changes/#v0.18.8-0.18.10-Dec-11,-2022-Jan-5,-2023","page":"Changes","title":"v0.18.8 - 0.18.10 Dec 11, 2022 - Jan 5, 2023","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Internal restructuring: remove @create_physics_wrappers macro, reduce boilerplate in assembly, wrap repeating pattenrns into functions. The price in the moment is a bit of a slowdown of assembly.\nFix parameter dependency handling (now we can get parameter derivative without solving in dual numbers; see the runh() example in Example430. However in the moment the advantatge is not very clear, so this is on hold...","category":"page"},{"location":"changes/#v0.18.7-Dec-7,-2022","page":"Changes","title":"v0.18.7 Dec 7, 2022","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"bump gridvisualize compat","category":"page"},{"location":"changes/#v0.18.6-Dec-3,-2022","page":"Changes","title":"v0.18.6 Dec 3, 2022","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"enable non-diagonal mass matrices for VoronoiFVMDiffEq","category":"page"},{"location":"changes/#v0.18.5-Nov-30,-2022","page":"Changes","title":"v0.18.5 Nov 30, 2022","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"ready for Julia 1.9, re-enable CI on nighly","category":"page"},{"location":"changes/#v0.18.4-Nov-29,-2022","page":"Changes","title":"v0.18.4 Nov 29, 2022","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Add API methods used by VoronoiFVMDiffEq.jl","category":"page"},{"location":"changes/#v0.18.3-Oct-18-2022","page":"Changes","title":"v0.18.3 Oct 18 2022","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Removed some allocations","category":"page"},{"location":"changes/#v0.18.2-Oct-13-2022","page":"Changes","title":"v0.18.2 Oct 13 2022","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Emerging capability of differencing wrt. parameters (experimental, see example 430)\nAllow iterative methods from Krylov.jl\nProper Dirichlet initialization with bcondition\nAllow for more general matrix structures (banded, tridiagonal, multidiagonal)","category":"page"},{"location":"changes/#v0.18.1-September-25-2022","page":"Changes","title":"v0.18.1 September 25 2022","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Working spherical symmetry case","category":"page"},{"location":"changes/#v0.18-September-12-2022","page":"Changes","title":"v0.18 September 12 2022","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Remove DifferentialEquations interface (move this to a glue package)","category":"page"},{"location":"changes/","page":"Changes","title":"Changes","text":"The current method of activating it through require is too brittle with respect to versioning.","category":"page"},{"location":"changes/#v0.17.1-August-20-2022","page":"Changes","title":"v0.17.1 August 20 2022","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Fix DifferentialEquations interface, start transition to LinearSolve","category":"page"},{"location":"changes/#v0.17.0-July-1-2022","page":"Changes","title":"v0.17.0 July 1 2022","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"ensure not to assemble data for species where they are not enabled This change should be breaking only for incorrect code where physics callbacks write into degrees of freedom which are not enabled","category":"page"},{"location":"changes/#v0.16.5-June-30,-2022","page":"Changes","title":"v0.16.5 June 30, 2022","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"add iteration to solver options, allow to choose :cg, :bicgstab.\nallow setting penalty with boundary_dirichlet!","category":"page"},{"location":"changes/#v0.16.4-May-25,-2022","page":"Changes","title":"v0.16.4 May 25, 2022","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"fix x-t plots ","category":"page"},{"location":"changes/#v0.16.3-March-18,-2022","page":"Changes","title":"v0.16.3 March 18, 2022","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Linearization API\nrelax some type constraints","category":"page"},{"location":"changes/#v0.16.2-Feb-18,-2022","page":"Changes","title":"v0.16.2 Feb 18, 2022","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"ExtendableGrids 0.9","category":"page"},{"location":"changes/#v0.16.1-Feb-17,-2022","page":"Changes","title":"v0.16.1 Feb 17, 2022","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"fix quantity postprocessing\ndefine unknown access for abstract vectors instead of vectors\npass rhs/unknowns wrappers in postprocessing methods\nintegrals as a wrapper type with proper quantity handling","category":"page"},{"location":"changes/#v0.16.0-Feb-13,-2022","page":"Changes","title":"v0.16.0 Feb 13, 2022","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Expose ODEProblem (and possibly ODEFunc) from VoronoiFVM.System.\nBreaking: Remove solve wrapper for DifferentialEquations.solve, instead recommend to call that directly\nBreaking: Handle DifferentialEquations.jl via Requires.jl.","category":"page"},{"location":"changes/#v0.15.1-Jan-15,-2022","page":"Changes","title":"v0.15.1 Jan 15, 2022","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Documentation fixes\nFix OrdinaryDiffEq interface\nadded example for current calculation with Quantities\nFixed type instabilities in quantities interface ","category":"page"},{"location":"changes/#v0.15.0-Jan-1,-2022","page":"Changes","title":"v0.15.0 Jan 1, 2022","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Breaking: History is not anymore returned by solve, instead it can be accessed via history after the solution.\nCleaned API:\nVoronoiFVM.solve(system::VoronoiFVM.AbstractSystem; kwargs...) is now the main method of solve which allows to access stationary, transient, embedding and DifferentialEquations based solvers.\nJoint implementation for implicit Euler timestepping and parameter embedding\nHandle more kwargs via SolverControl (e.g. log)\nUse Parameters.jl in struct definition\nAdd history types NewtonSolverHistory, TransientSolverHistory\ndetailed and summary methods for both history types\nNonlinear solver example notebook (under development): nonlinear-solvers.jl\nOrdinaryDiffEq solver now in CI\nscalarplot for 1D transient solutions\nSparsity detection via Symbolics.jl instead of the sunsetted SparsityDetection.jl","category":"page"},{"location":"changes/#v0.14.0-Dec-24,-2021","page":"Changes","title":"v0.14.0 Dec 24, 2021","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Backward compatible, hopefully nonbreaking API simplification","category":"page"},{"location":"changes/","page":"Changes","title":"Changes","text":"Boundary conditions are now specified in breaction. Advantages:\neasy x/t dependency\nunified (upcoming) interface for parameters\nunified handling of standard and nonstandard boundary conditions\nsimpler documentation\nMade NewtonControl alias of SolverControl, continue to work with SolverControl\nSystem constructor now directly takes physics callback functions, no need anymore to work with extra physics struct\nsolve() now takes \"SolverControl\" parameters as kwargs,no need anymore to work with extra NewtonControl/SolverControl struct\nNotebooks as part of documentation and CI\nSee also the pluto notebook api-update.jl","category":"page"},{"location":"changes/#v0.13.2-Oct-29,-2021","page":"Changes","title":"v0.13.2 Oct 29, 2021","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Bernoulli function overhaul","category":"page"},{"location":"changes/#v0.13.1","page":"Changes","title":"v0.13.1","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"sorted things with ExtendableGrids\nnodal flux reconstruction (e.g. for visualization)","category":"page"},{"location":"changes/#v0.13.0,-Oct-13,-2021","page":"Changes","title":"v0.13.0, Oct 13, 2021","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"various bug fixes, explicit numbering of edge nodes","category":"page"},{"location":"changes/#v0.12.3,-July-7,-2021","page":"Changes","title":"v0.12.3, July 7, 2021","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Add quantity id\nDocument quantities","category":"page"},{"location":"changes/#v0.12.2,-July-7,-2021","page":"Changes","title":"v0.12.2, July 7, 2021","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Introduce the notion of quantities which can be continuous or discontinuous at interfaces.","category":"page"},{"location":"changes/","page":"Changes","title":"Changes","text":"Quantity handling is implemented on top of species handling\nUnknowns u and rhs y now passed to callbacks as wrapper types, and can be indexed by quantity or by species numbers. Moreover, this will allow to abstract parameters, gradients etc. in future versions.","category":"page"},{"location":"changes/#v0.12.0,-July-2-2021","page":"Changes","title":"v0.12.0, July 2 2021","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"By default, the u parameter in flux callbacks is now a nspec x 2 array\nunknowns(edge,u), viewK, viewL are obsolete, they still work for backward compatibility\nphysics.num_species is now meaningless, num_species is automatically detected.\nSparseSystem and DenseSystem are now type aliases of a parametrized type instead of two independent subtypes of System","category":"page"},{"location":"changes/#v0.11.8","page":"Changes","title":"v0.11.8","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"increase chunk size threshold to match argument length in calls to vectormodejacobian","category":"page"},{"location":"changes/#v0.11.7","page":"Changes","title":"v0.11.7","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"First attempts on surface flux","category":"page"},{"location":"changes/#v0.11.1,-April-13,-2021","page":"Changes","title":"v0.11.1, April 13, 2021","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Assembly loops cleaned from type instabilities\nOptionally check for allocations due to type instabilities introduced in physics callbacks. See check_allocs! for more information.","category":"page"},{"location":"changes/#v0.11,-April-12,-2021","page":"Changes","title":"v0.11, April 12, 2021","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Depending on Julia 1.5 now\nLineaer solvers now based on factorize! from ExtendableSparse 0.5\nDocumentation overhaul\nRe-checking impedance calculation","category":"page"},{"location":"changes/#v0.10.13-April-1,-2021","page":"Changes","title":"v0.10.13 April 1, 2021","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Outflow boundary conditions","category":"page"},{"location":"changes/#v0.10.8-March-22,-2021","page":"Changes","title":"v0.10.8 March 22, 2021","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"TransientSolution structure, transient solve\nSolve compatible with DifferentialEquations.jl","category":"page"},{"location":"changes/#v0.10.3-Feb-11,-2021","page":"Changes","title":"v0.10.3 Feb 11, 2021","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Introduce non-mutating solve\nOptionally record history if log kw is true in solve.","category":"page"},{"location":"changes/#v0.10.0-Jan-9-2021","page":"Changes","title":"v0.10.0 Jan 9 2021","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Moving visualization to the package GridVisualize.jl, emerging from the visualization methods in ExtendableGrids","category":"page"},{"location":"changes/#v0.9.0-Dec-21-2020","page":"Changes","title":"v0.9.0 Dec 21 2020","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Add the possibility to interface with DifferentialEquations.jl\nBreaking: The API change to passing the unknowns to the an edge callback as a matrix turned out to be a dead end in the strategic sense. In order to extend functionality, we need to be able to pass more data to which we can apply differetiation. Particular plans involve bifurcation parameters and reconstructed gradients. So we return to the viewK/viewL pattern we had before. However, these are now aliases:\nviewK(edge,u)=unknowns(edge,u,1)\nviewL(edge,u)=unknowns(edge,u,2)\nIn order to ease refactoring in the case where models have been implemented with Matrix access to the unknowns, unknowns(edge,u) returns a matrix of the edge unknowns. For refactoring, just rewrite e.g.","category":"page"},{"location":"changes/","page":"Changes","title":"Changes","text":" function flux(y,u,edge)\n for ispec=1:nspec\n y[ispec]=u[ispec,1]-u[ispec,2]\n end\n end","category":"page"},{"location":"changes/","page":"Changes","title":"Changes","text":"to","category":"page"},{"location":"changes/","page":"Changes","title":"Changes","text":" function flux(y,u0,edge)\n u=unknowns(edge,u0)\n for ispec=1:nspec\n y[ispec]=u[ispec,1]-u[ispec,2]\n end\n end","category":"page"},{"location":"changes/#v0.8.5-Sep-1-2020","page":"Changes","title":"v0.8.5 Sep 1 2020","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"allow any object in Physics.data (thanks Jan Weidner)\nadd generic operator for non-canonical problem structures","category":"page"},{"location":"changes/#v0.8.4-July-25-2020","page":"Changes","title":"v0.8.4 July 25 2020","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Update ExtendableGrids + ExtendableSparse","category":"page"},{"location":"changes/#v0.8.3-June-25-2020","page":"Changes","title":"v0.8.3 June 25 2020","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Replace splatting by dispatch on availability of data record","category":"page"},{"location":"changes/#v0.8.2-May-15-2020","page":"Changes","title":"v0.8.2 May 15 2020","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Form factors are now pre-calculated and stored\nIntroduced update_grid! for triggering re-calculation if coordinates have changed","category":"page"},{"location":"changes/#v0.8.1-May-2-2020","page":"Changes","title":"v0.8.1 May 2 2020","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Introduce evolve! : time solver with automatic timestep control","category":"page"},{"location":"changes/#v0.8-Apr-28,-2020","page":"Changes","title":"v0.8 Apr 28, 2020","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Replaced VoronoiFVM grid module by ExtendableGrids.jl\nMoved grid generation, modification, plotting over to ExtendableGrids\nNecessary changes in codes using VoronoiFVM:\nReplace grid.coord by coord obtained via coord=coordinates(grid) or coord=grid[Coordinates] after importing ExtendableGrids\nReplace VoronoiFVM.plot by ExtendableGrids.plot.\nIn the plot method, Plotter is now a keyword argument\nVoronoiFVM.Grid() now returns a ExtendableGrids.ExtendableGrid, in fact it is just an alias to ExtendableGrids.simplexgrid\nFor using any methods on grids like cellmask! one needs to use ExtendableGrids\nSubgrids now are of the same type ExtendableGrids, views are currently defined for vectors only.","category":"page"},{"location":"changes/#v0.7-Feb-28-2020","page":"Changes","title":"v0.7 Feb 28 2020","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"API modification:\nBreaking:\ndata parameter passed to physics callbacks only if Physics object is created with data parameter.\nThis makes the API more consistent in the case that parameters are just taken from the closure (the scope where the physics functions are defined) and no data object has been created.\nReplace node.coord[i] by node[i].\nReplace edge.coordK[i] by edge[i,1].\nReplace edge.coordL[i] by edge[i,2].\nThis now directly accesses the coordinate field of the grid and avoids copying of the coordinates\nBackward compatible: \nNo need for viewK and viewL in edge callbacks (they also make trouble with allocations...)\nReplace uk[i] by u[i,1]\nReplace ul[i] by u[i,2]\nReplace VoronoiFVM.DenseSystem(...) by VoronoiFVM.System(..., unknown_storage=:dense)\nReplace VoronoiFVM.SparseSystem(...) by VoronoiFVM.System(..., unknown_storage=:sparse)\nNo allocations anymore in assembly loop:\nReplaced ElasticArray in Grid by normal one - this was the largest regression\nReturn nothing from mutating methods to avoid some allocations \nIndexing in formfactors.jl with Int","category":"page"},{"location":"changes/#v0.6.5-Jan-25-2020","page":"Changes","title":"v0.6.5 Jan 25 2020","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"use updateindex! for matrix, depend on ExtendableSparse 0.2.6","category":"page"},{"location":"changes/#v0.6.4-2020-01-20","page":"Changes","title":"v0.6.4 2020-01-20","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Rearranged + commented boundary assembly loop\nReworked + renamed some examples\nDocument that unknowns doesn't initialize values.","category":"page"},{"location":"changes/#v0.6.3-2019-12-21","page":"Changes","title":"v0.6.3 2019-12-21","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"remove xcolptrs call Update dependency on ExtendableSparse","category":"page"},{"location":"changes/#v0.6.2-2019-12-20","page":"Changes","title":"v0.6.2 2019-12-20","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Updated dependency list (Triangulate ^0.4.0)","category":"page"},{"location":"changes/#v0.6.1,-2019-12-17","page":"Changes","title":"v0.6.1, 2019-12-17","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"return \"plotted\" for being able to place colormap\nrequire Triangulate >= 0.3.0","category":"page"},{"location":"changes/#v0.6.0,-Dec-15-2019","page":"Changes","title":"v0.6.0, Dec 15 2019","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Removed Triangle submodule, depend on new Triangulate.jl Triangle wrapper\nlink to source code in examples\nboundary_dirichlet! etc methods for setting boundary conditions","category":"page"},{"location":"changes/#v0.5.6-Dec-5-2019","page":"Changes","title":"v0.5.6 Dec 5 2019","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Bug fixes\ncheck triangle input for min 3 points\ncheck triangle edgelist for C_NULL\nvoronoi plot","category":"page"},{"location":"changes/#v0.5.5-Dec-4-2019","page":"Changes","title":"v0.5.5 Dec 4 2019","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"(Temporary) Copy of TriangleRaw as Triangle submodule. To be replaced by dependency on evisioned package","category":"page"},{"location":"changes/#v0.5.4-Dec-3-2019","page":"Changes","title":"v0.5.4 Dec 3 2019","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Re-enabled ElasticArrays in grid structure (for the time being)\nAdded potkink example: this adds an inner boundary","category":"page"},{"location":"changes/#v0.5.3-Dec-1-2019","page":"Changes","title":"v0.5.3 Dec 1 2019","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"triangle in optional submodule\nModified API for plotting\nRemoved formal dependency on Plots and PyPlot\nUse Plotter module as first parameter to plot methods - replaces fvmplot and fvmpyplot functions. Use VoronoiFVM.plot(PyPlot,...) resp. VoronoiFVM.plot(Plots,...)\nNo more complaints when package is used in environment with plots or pyplot installed\nModified API for impedance","category":"page"},{"location":"changes/#v0.5.2-Nov-19,-2019","page":"Changes","title":"v0.5.2 Nov 19, 2019","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Reorganized grid stuff\nIncluded triangle (after Ideas from TriangleMesh.jl)","category":"page"},{"location":"changes/#v0.5.1-Nov-13,-2019","page":"Changes","title":"v0.5.1 Nov 13, 2019","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Fixed performance regression: AbstractArrays for Grid components were slow.\nAdded handling of cylindrical coordinates","category":"page"},{"location":"changes/#V0.5,-November-10,-2019","page":"Changes","title":"V0.5, November 10, 2019","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Velocity projections\nAdded edge handling to grid struct","category":"page"},{"location":"changes/#V0.4.2,-November-6,-2019","page":"Changes","title":"V0.4.2, November 6, 2019","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Replaced PyPlot by Plots\nBetter and more examples","category":"page"},{"location":"changes/#V0.4,-July-12,-2019","page":"Changes","title":"V0.4, July 12, 2019","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Registered with Julia ecosystem\nEnhance Newton solver by embedding, exception handling\nReplace SparseMatrixCSC with ExtendableSparseMatrix\nfixed allocation issues in assembly\nassured that users get allocation stuff right via typed functions in physics structure\nmore julianic API","category":"page"},{"location":"changes/#V0.3,-April-9-2019","page":"Changes","title":"V0.3, April 9 2019","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Renamed from TwoPointFluxFVM to VoronoiFVM\nComplete rewrite of assembly allowing sparse or dense matrix to store degree of freedom information\nSolution is a nnodes x nspecies sparse or dense matrix\nThe wonderful array interface of Julia still provides slicing etc in order to access species without need to write any bulk_solution stuff or whatever when using the sparse variant\nRe-export value() for debugging in physics functions\nTest function handling for flux calculation\nFirst working steps to impedance handling\nAbolished Graph in favor of Grid, Graph was premature optimization...","category":"page"},{"location":"changes/#V0.2,-Feb-20,-2019","page":"Changes","title":"V0.2, Feb 20, 2019","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Changed signature of all callback functions: This also allows to pass user defined arrays etc. to the callback functions. In particular, velocity vectors can be passed this way.\nBesides of flux!(), they now all have node::VoronoiFVM.Node as a second argument.\nflux!() has edge::VoronoiFVM.Edge as a second argument\nthe x argument in source!() is omitted, the same data are now found in node.coord","category":"page"},{"location":"changes/","page":"Changes","title":"Changes","text":"New method edgelength(edge::VoronoiFVM.Edge)","category":"page"},{"location":"changes/#V0.1,-Dec.-2018","page":"Changes","title":"V0.1, Dec. 2018","text":"","category":"section"},{"location":"changes/","page":"Changes","title":"Changes","text":"Initial release","category":"page"},{"location":"runexamples/#About-the-examples","page":"About the examples","title":"About the examples","text":"","category":"section"},{"location":"runexamples/","page":"About the examples","title":"About the examples","text":"The examples have been designed with the following issues in mind:","category":"page"},{"location":"runexamples/","page":"About the examples","title":"About the examples","text":"they run from the Julia REPL\neach example is a Julia module named similar to the basename of the example file.\nan example can be used as the starting point for a project \nthe examples at the same time comprise the test suite for VoronoiFVM.","category":"page"},{"location":"runexamples/","page":"About the examples","title":"About the examples","text":"Since the creation of these examples, the API has been updated and simplified.","category":"page"},{"location":"runexamples/#Running-the-examples","page":"About the examples","title":"Running the examples","text":"","category":"section"},{"location":"runexamples/","page":"About the examples","title":"About the examples","text":"Plotting is performed using the GridVisualize.jl package which interfaces PyPlot.jl, Plots.jl, Makie.jl.","category":"page"},{"location":"runexamples/","page":"About the examples","title":"About the examples","text":"In order to run ExampleXXX, perform the following steps:","category":"page"},{"location":"runexamples/","page":"About the examples","title":"About the examples","text":"Download the example file (e.g. via the source code link at the top)\nCall Julia with an Julia environment which contains VoronoiFVM.jl, ExtendableGrids.jl, GridVisualize.jl and e.g. PyPlot.jl\ninclude(\"ExampleXXX.jl\")\nRun the example via ExampleXXX.main(Plotter=PyPlot)","category":"page"},{"location":"runexamples/","page":"About the examples","title":"About the examples","text":"Due to the encapsulation into modules, you can load as many examples as you like.","category":"page"},{"location":"runexamples/","page":"About the examples","title":"About the examples","text":"If you want to modify the example, consider using Revise.jl and includet. ","category":"page"},{"location":"runexamples/#Performance-with-closures","page":"About the examples","title":"Performance with closures","text":"","category":"section"},{"location":"runexamples/","page":"About the examples","title":"About the examples","text":"VoronoiFVM provides two flavors of calbacks for constitutive functions: ","category":"page"},{"location":"runexamples/","page":"About the examples","title":"About the examples","text":"Callbacks with data parameoter. data is declared as part of Physics and passed down to the callbacks\nCallbacks without data parameter. Here, the parameters of the physics callbacks are accessed via closures, i.e. from within the scope of the definition of the particular function.","category":"page"},{"location":"runexamples/","page":"About the examples","title":"About the examples","text":"While the second method is very convenient to use, it comes with a serious performance pitfall: if a variable in the closure is assigned twice, Julia becomes unsure about it's type and therefore \"boxes\" it, i.e. it creates a wrapper struct around the variable value which is able to track its potentially changing type. The serious consequence of this is that assignments to a boxed variable lead to allocations, which are a serious performance hit if they occur in loops over grid nodes or edges.","category":"page"},{"location":"runexamples/","page":"About the examples","title":"About the examples","text":"This behaviour is explained in the Julia documentation.","category":"page"},{"location":"runexamples/","page":"About the examples","title":"About the examples","text":"Here is an example which comes close to the situation in VoronoiFVM:","category":"page"},{"location":"runexamples/","page":"About the examples","title":"About the examples","text":"function ttype_boxed(n)\n u=rand(n)\n v=similar(u)\n a=2.0\n a=3.0\n dostuff(u)=a*u\n @allocated map!(dostuff,v,u)\nend\nttype_boxed(10) # hide\nttype_boxed(10)","category":"page"},{"location":"runexamples/","page":"About the examples","title":"About the examples","text":"The remedy is to type-annotate variables from closures:","category":"page"},{"location":"runexamples/","page":"About the examples","title":"About the examples","text":"function ttype_annotated(n)\n u=rand(n)\n v=similar(u)\n a::Float64=2.0\n a=3.0\n dostuff(u)=a*u\n @allocated map!(dostuff,v,u)\nend\nttype_annotated(10) # hide\nttype_annotated(10)","category":"page"},{"location":"module_examples/Example101_Laplace1D/#101:-1D-Laplace-equation","page":"101: 1D Laplace equation","title":"101: 1D Laplace equation","text":"","category":"section"},{"location":"module_examples/Example101_Laplace1D/","page":"101: 1D Laplace equation","title":"101: 1D Laplace equation","text":"(source code)","category":"page"},{"location":"module_examples/Example101_Laplace1D/","page":"101: 1D Laplace equation","title":"101: 1D Laplace equation","text":"Let Omega=(gamma_1gamma_2) with gamma_1=0, gamma_2=1. This is the simplest second order boundary value problem (BVP) for a partial differential equation (PDE):","category":"page"},{"location":"module_examples/Example101_Laplace1D/","page":"101: 1D Laplace equation","title":"101: 1D Laplace equation","text":"-Delta u =0\nu(gamma_1)=g_1\nu(gamma_2)=g_2","category":"page"},{"location":"module_examples/Example101_Laplace1D/","page":"101: 1D Laplace equation","title":"101: 1D Laplace equation","text":"We replace the Dirichlet boundary condition by a Robin boundary condition with a penalty parameter frac1varepsilon:","category":"page"},{"location":"module_examples/Example101_Laplace1D/","page":"101: 1D Laplace equation","title":"101: 1D Laplace equation","text":"nabla u(gamma_1) + frac1varepsilon(u(gamma_1)-g_1)=0 \n-nabla u(gamma_2) + frac1varepsilon(u(gamma_2)-g_2)\n=0","category":"page"},{"location":"module_examples/Example101_Laplace1D/","page":"101: 1D Laplace equation","title":"101: 1D Laplace equation","text":"This penalty method for the implementation of Dirichlet boundary conditions is used throughout VoronoiFVM.","category":"page"},{"location":"module_examples/Example101_Laplace1D/","page":"101: 1D Laplace equation","title":"101: 1D Laplace equation","text":"In order to discretize it, we choose collocation points gamma_1=x_1 x_2 dots x_n=gamma_2.","category":"page"},{"location":"module_examples/Example101_Laplace1D/","page":"101: 1D Laplace equation","title":"101: 1D Laplace equation","text":"For instance, we can choose 6 collocation points in (01): From these, we create a discretization grid structure for working with the method.","category":"page"},{"location":"module_examples/Example101_Laplace1D/","page":"101: 1D Laplace equation","title":"101: 1D Laplace equation","text":"This implicitly creates a number of control volumes omega_k around each discretization point x_k: Let sigma_kk+1=fracx_k+x_k+12. Then omega_1=(gamma_1sigma_12), omega_k= (sigma_k-1k sigma_kk+1) for k=2dots n-1, omega_n=(sigma_n-1ngamma_2).","category":"page"},{"location":"module_examples/Example101_Laplace1D/","page":"101: 1D Laplace equation","title":"101: 1D Laplace equation","text":" x1 x2 x3 x4 x5 x6\n o-----o-----o-----o-----o-----o\n |--|-----|-----|-----|-----|--|\n ω1 ω2 ω3 ω4 ω5 ω6","category":"page"},{"location":"module_examples/Example101_Laplace1D/","page":"101: 1D Laplace equation","title":"101: 1D Laplace equation","text":"For each omega_k, we integrate the equation","category":"page"},{"location":"module_examples/Example101_Laplace1D/","page":"101: 1D Laplace equation","title":"101: 1D Laplace equation","text":"beginaligned\n0=int_omega_k -Delta u domega= -int_partial omega_k nabla u ds\n= begincases\nu(sigma_12) - u(0) k=1\nu(sigma_kk+1) - u(sigma_k-1k) 1kn\nu(1)- u(sigma_nn+1)k=n\nendcases\napprox begincases\nfrac1x_2-x_1 g(u_1u_2) + frac1varepsilon(u_1-0) k=1\nfrac1x_k-x_k-1g(u_ku_k-1) -frac1x_k+1-x_kg(u_k+1u_k) 1kn\nfrac1varepsilon(u_n-1)+ frac1x_n-x_n-1 g(u_nu_n-1)k=n\nendcases\nendaligned","category":"page"},{"location":"module_examples/Example101_Laplace1D/","page":"101: 1D Laplace equation","title":"101: 1D Laplace equation","text":"In the last equation, we wrote u_k=u(x_k) and g(u_ku_l)=u_k-u_l. For the interior interfaces between control volumes, we replaced u by a difference quotient. In the boundary control volumes, we replaced u by the boundary conditions.","category":"page"},{"location":"module_examples/Example101_Laplace1D/","page":"101: 1D Laplace equation","title":"101: 1D Laplace equation","text":"In the example below, we fix a number of species and write a Julia function describing g, we create a physics record, and a finite volume system with one unknown species and a dense matrix to describe it's degrees of freedom (the matrix used to calculate the solution is sparse). We give the species the number 1 and enable it for grid region number one 1. Then, we set boundary conditions for species 1 at gamma_1 gamma_2.","category":"page"},{"location":"module_examples/Example101_Laplace1D/","page":"101: 1D Laplace equation","title":"101: 1D Laplace equation","text":"We create a zero initial value and a solution vector and initialize them.","category":"page"},{"location":"module_examples/Example101_Laplace1D/","page":"101: 1D Laplace equation","title":"101: 1D Laplace equation","text":"With these data, we solve the system.","category":"page"},{"location":"module_examples/Example101_Laplace1D/","page":"101: 1D Laplace equation","title":"101: 1D Laplace equation","text":"We wrap this example and all later ones into a module structure. This allows to load all of them at once into the REPL without name clashes. We shouldn't forget the corresponding end statement.","category":"page"},{"location":"module_examples/Example101_Laplace1D/","page":"101: 1D Laplace equation","title":"101: 1D Laplace equation","text":"module Example101_Laplace1D\n\nusing VoronoiFVM\n\nfunction main()\n ispec = 1 ## Index of species we are working with\n\n # Flux function which describes the flux\n # between neighboring control volumes\n function flux!(f, u, edge)\n f[1] = u[1, 1] - u[1, 2]\n end\n\n function bcond!(args...)\n boundary_dirichlet!(args...; region = 1, value = 0)\n boundary_dirichlet!(args...; region = 2, value = 1)\n end\n\n # Create a one dimensional discretization grid\n # Each grid cell belongs to a region marked by a region number\n # By default, there is only one region numbered with 1\n grid = VoronoiFVM.Grid(0:0.2:1)\n\n # Create a finite volume system\n sys = VoronoiFVM.System(grid; flux = flux!, breaction = bcond!, species = ispec)\n\n # Solve stationary problem\n solution = solve(sys; inival = 0)\n\n # Return test value\n return sum(solution)\nend\n\nusing Test\nfunction runtests()\n @test main() ≈ 3.0\nend\n\nend","category":"page"},{"location":"module_examples/Example101_Laplace1D/","page":"101: 1D Laplace equation","title":"101: 1D Laplace equation","text":"","category":"page"},{"location":"module_examples/Example101_Laplace1D/","page":"101: 1D Laplace equation","title":"101: 1D Laplace equation","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/#225:-Terminal-flux-calculation-via-test-functions,-nD","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"","category":"section"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"(source code)","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"After calculating solutions based on the finite volume method, it may be interesting to obtain information about the solution besides of the graphical representation.","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"Here, we focus on the following data:","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"integrals of the solution\nflux through parts of the boundary","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"Let us define the following reaction - diffusion system in a domain Omega:","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"beginaligned\npartial_t u_1 - nabla cdot nabla u_1 + r(u_1 u_2) = f=10\npartial_t u_2 - nabla cdot nabla u_1 - r(u_1 u_2) = 0\nendaligned","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"with boundary conditions u_2=0 on Gamma_2subsetpartialOmega and r(u_1u_2)=u_1 + 01 u_2","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"The source f creates species u_1 which reacts to u_2, u_2 then leaves the domain at boundary Gamma_2.","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/#Stationary-problem","page":"225: Terminal flux calculation via test functions, nD","title":"Stationary problem","text":"","category":"section"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"For the stationary problem, we have the following flux balances derived from the equations and from Gauss theorem:","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"beginaligned\nint_Omega r(u_1u_2) domega = int_Omega f domega \nint_Omega -r(u_1u_2) domega = int_Gamma_2 nabla u cdot vec n ds \nendaligned","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"The volume integrals can be approximated based on the finite volume subdivision Omega=cup_iin mathcal N omega_i:","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"beginaligned\nint_Omega r(u_1u_2) domega approx sum_iin mathcal N omega_i r(u_1iu_2i)\nint_Omega f domega approx sum_iin mathcal N omega_i f_i\nendaligned","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"But what about the boundary integral ? Here, we use a trick to cast the surface integral to the integral to a volume integral with the help of a test function:","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"Let T(x) be the solution of the Laplace problem -nabla^2 T =0 in Omega and the boundary conditions","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"beginaligned\nT =0 quad textat Gamma_4\nT =1 quad textat Gamma_2\npartial_n T =0quad textat Gamma_1Gamma_3\nendaligned","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"Write vec j=-nabla u. and assume nablacdot vec j + r =f.","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"beginaligned\nint_Gamma_2 vec j cdot vec n ds=int_Gamma_2 Tvec j cdot vec n ds quad textdue to T=1 texton Gamma_2\n =int_partialOmega Tvec j cdot vec n dsquad textdue to T=0 texton Gamma_4 quadvec jcdot vec n=0 texton Gamma_1 Gamma_3\n= int_Omega nabla cdot (T vec j) domega quad text(Gauss)\n= int_Omega nabla T cdot vec j domega + int_Omega T nablacdot j domega\n= int_Omega nabla T cdot vec j domega + int_Omega T(f-r)dω\nendaligned","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"and we approximate","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"beginaligned\nint_Omega nabla T cdot vec j domega approx sum_kl\nfracomega_kcapomega_lh_klg(u_k u_l) (T_k-T_l)\nendaligned","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"where the sum runs over pairs of neighboring control volumes.","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"The integrate method with a test function parameter returns a value for each species, the sign convention assumes that species leaving the domain lead to negative values.","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/#Transient-problem","page":"225: Terminal flux calculation via test functions, nD","title":"Transient problem","text":"","category":"section"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"The amount of species created via the source term (measured in F) integrated over time should be equal to the sum of the amount of species left in the domain at the very end of the evolution and the amount of species which left the domain:","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"int_t_0^t_end int_Omega f domega dt= int_Omega (u_1+u_2)dω + int_t_0^t_end int_Gamma_2 nabla u_2 cdot vec n ds","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"Literature references:","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"H. Gajewski \"Analysis und Numerik von Ladungstransport in Halbleitern\", WIAS Berlin, Report No.6\nYoder, P. D., K. Gärtner, and W. Fichtner. \"A generalized Ramo–Shockley theorem for classical to quantum transport at arbitrary frequencies.\" Journal of Applied Physics 79.4 (1996): 1951-1954.\nP. Farrell, N. Rotundo, D. H. Doan, M. Kantner, J. Fuhrmann, and T. Koprucki, \"Numerical methods for drift-diffusion models\", in Handbook of optoelectronic device modeling and simulation: Lasers, modulators, photodetectors, solar cells, and numerical methods, vol. 2, J. Piprek, Ed. Boca Raton: CRC Press, 2017, pp. 733–771.","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"module Example225_TestFunctions2D\n\nusing VoronoiFVM, GridVisualize, ExtendableGrids\n\nfunction main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise,\n dim = 2, tend = 5, dt = 0.2)\n n = [101, 21, 5]\n X = collect(range(0.0, 1; length = n[dim]))\n if dim == 1\n grid = simplexgrid(X)\n Γ_where_T_equal_1 = [2]\n Γ_where_T_equal_0 = [1]\n elseif dim == 2\n grid = simplexgrid(X, X)\n Γ_where_T_equal_1 = [2]\n Γ_where_T_equal_0 = [4]\n elseif dim == 3\n grid = simplexgrid(X, X, X)\n Γ_where_T_equal_1 = [2]\n Γ_where_T_equal_0 = [4]\n end\n\n function storage(f, u, node)\n f .= u\n end\n\n function flux(f, u, edge)\n f[1] = u[1, 1] - u[1, 2]\n f[2] = u[2, 1] - u[2, 2]\n end\n\n r(u1, u2) = u1 - 0.1 * u2\n\n function reaction(f, u, node)\n f[1] = r(u[1], u[2])\n f[2] = -r(u[1], u[2])\n end\n\n function source(f, node)\n f[1] = 1.0\n end\n\n physics = VoronoiFVM.Physics(; flux = flux,\n storage = storage,\n reaction = reaction,\n source = source)\n\n system = VoronoiFVM.System(grid, physics; assembly = assembly)\n\n enable_species!(system, 1, [1])\n enable_species!(system, 2, [1])\n boundary_dirichlet!(system, 2, 2, 0.0)\n\n sol = solve(system; inival = 0.0)\n\n vis = GridVisualizer(; Plotter = Plotter, layout = (1, 2), resolution = (600, 300),\n fignumber = 1)\n scalarplot!(vis[1, 1], grid, sol[1, :]; flimits = (0, 1.5), title = \"u_1\")\n scalarplot!(vis[1, 2], grid, sol[2, :]; flimits = (0, 1.5), title = \"u_2\", show = true)\n\n \"\"\"\n The `integrate` method of `VoronoiFVM` provides a possibility to calculate\n the volume integral of a function of a solution as described above.\n It returns a `num_specie` x `num_regions` matrix of the integrals\n of the function of the unknowns over the different subdomains (here, we have only one):\n \"\"\"\n\n \"\"\"\n Amount of u_1 and u_2 in the domain aka integral over identity storage function:\n \"\"\"\n U = integrate(system, storage, sol)\n\n \"\"\"\n Amount of species created by source term per unit time:\n \"\"\"\n F = integrate(system, (f, u, node) -> source(f, node), sol)\n\n \"\"\"\n Amount of reaction per unit time:\n \"\"\"\n R = integrate(system, reaction, sol)\n\n tf = TestFunctionFactory(system)\n T = testfunction(tf, Γ_where_T_equal_0, Γ_where_T_equal_1)\n\n I = integrate(system, T, sol)\n\n t0 = 0.0\n\n control = fixed_timesteps!(VoronoiFVM.NewtonControl(), dt)\n\n tsol = solve(system; inival = 0.0, times = [t0, tend], control)\n\n vis1 = GridVisualizer(; Plotter = Plotter, layout = (1, 2), resolution = (600, 300),\n fignumber = 4)\n\n for i = 1:length(tsol)\n sol = tsol[i]\n scalarplot!(vis1[1, 1], grid, sol[1, :]; flimits = (0, 1.5), clear = true)\n scalarplot!(vis1[1, 2], grid, sol[2, :]; flimits = (0, 1.5), show = true)\n end\n\n outflow_rate = Float64[]\n for i = 2:length(tsol)\n ofr = integrate(system, T, tsol[i], tsol[i - 1], tsol.t[i] - tsol.t[i - 1])\n push!(outflow_rate, ofr[2])\n end\n\n vis2 = GridVisualizer(; Plotter = Plotter, layout = (1, 1), resolution = (600, 300),\n fignumber = 2)\n scalarplot!(vis2[1, 1], [0, tend], -[I[2], I[2]]; label = \"stationary\", clear = true)\n scalarplot!(vis2[1, 1], tsol.t[2:end], -outflow_rate; label = \"transient\", show = true)\n\n all_outflow = 0.0\n for i = 1:(length(tsol) - 1)\n all_outflow -= outflow_rate[i] * (tsol.t[i + 1] - tsol.t[i])\n end\n\n Uend = integrate(system, storage, tsol[end])\n isapprox(F[1], R[1]; rtol = 1.0e-12) ? true : return false\n isapprox(I[1], 0.0; atol = 1.0e-12) ? true : return false\n isapprox(R[2], I[2]; rtol = 1.0e-12) ? true : return false\n isapprox(F[1] * (tend - t0), (Uend[1] + Uend[2] + all_outflow); rtol = 1.0e-12) ? true :\n return false\nend\n\nusing Test\nfunction runtests()\n @test main(; dim = 1, unknown_storage = :sparse, assembly = :edgewise)\n @test main(; dim = 1, unknown_storage = :dense, assembly = :edgewise)\n @test main(; dim = 2, unknown_storage = :sparse, assembly = :edgewise)\n @test main(; dim = 2, unknown_storage = :dense, assembly = :edgewise)\n @test main(; dim = 3, unknown_storage = :sparse, assembly = :edgewise)\n @test main(; dim = 3, unknown_storage = :dense, assembly = :edgewise)\n\n @test main(; dim = 1, unknown_storage = :sparse, assembly = :cellwise)\n @test main(; dim = 1, unknown_storage = :dense, assembly = :cellwise)\n @test main(; dim = 2, unknown_storage = :sparse, assembly = :cellwise)\n @test main(; dim = 2, unknown_storage = :dense, assembly = :cellwise)\n @test main(; dim = 3, unknown_storage = :sparse, assembly = :cellwise)\n @test main(; dim = 3, unknown_storage = :dense, assembly = :cellwise)\nend\n\nend","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"","category":"page"},{"location":"module_examples/Example225_TestFunctions2D/","page":"225: Terminal flux calculation via test functions, nD","title":"225: Terminal flux calculation via test functions, nD","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example230_BoundaryFlux/#103:-Boundary-flux","page":"103: Boundary flux","title":"103: Boundary flux","text":"","category":"section"},{"location":"module_examples/Example230_BoundaryFlux/","page":"103: Boundary flux","title":"103: Boundary flux","text":"(source code)","category":"page"},{"location":"module_examples/Example230_BoundaryFlux/","page":"103: Boundary flux","title":"103: Boundary flux","text":"We consider two test problems.","category":"page"},{"location":"module_examples/Example230_BoundaryFlux/","page":"103: Boundary flux","title":"103: Boundary flux","text":"Testproblem A: Consider in Omega_1=(01)","category":"page"},{"location":"module_examples/Example230_BoundaryFlux/","page":"103: Boundary flux","title":"103: Boundary flux","text":"- d_1 Delta u_1 + k_1 u_1 = c_1","category":"page"},{"location":"module_examples/Example230_BoundaryFlux/","page":"103: Boundary flux","title":"103: Boundary flux","text":"in with homogeneous Neumann boundary conditions.","category":"page"},{"location":"module_examples/Example230_BoundaryFlux/","page":"103: Boundary flux","title":"103: Boundary flux","text":"Testproblem B: Consider in \\Omega_2=(0,1) x (0, 1) $","category":"page"},{"location":"module_examples/Example230_BoundaryFlux/","page":"103: Boundary flux","title":"103: Boundary flux","text":"- d_2 Delta u_2 + k_2 u_2 = c_2","category":"page"},{"location":"module_examples/Example230_BoundaryFlux/","page":"103: Boundary flux","title":"103: Boundary flux","text":"in with homogeneous Neumann boundary conditions and at the right boundary, i.e. $ {1} x (0, 1) $","category":"page"},{"location":"module_examples/Example230_BoundaryFlux/","page":"103: Boundary flux","title":"103: Boundary flux","text":"- d_b Delta v + k_b v = c_b","category":"page"},{"location":"module_examples/Example230_BoundaryFlux/","page":"103: Boundary flux","title":"103: Boundary flux","text":"If d1 = db, k1 = kb and c1 = cb, then u and v should coincide.","category":"page"},{"location":"module_examples/Example230_BoundaryFlux/","page":"103: Boundary flux","title":"103: Boundary flux","text":"module Example230_BoundaryFlux\n\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\n\nfunction main(; n = 2 * 10, # n musst be an even number\n d1 = 5.0, db = 5.0, # prefactors (before diffusive part)\n kmax = 2.0, cmax = 3.0,\n Plotter = nothing,\n unknown_storage = :sparse, assembly = :edgewise)\n\n ###########################################################################\n ###################### 1D problem ######################\n ###########################################################################\n\n ispec_1D = 1\n bulk_1D = 1\n\n X = range(0.0; stop = 1.0, length = n)\n length_x = length(X)\n length_x_half = Int(length_x / 2)\n\n grid_1D = simplexgrid(X)\n\n k1 = zeros(length_x)\n c1 = zeros(length_x)\n\n k1[1:length_x_half] .= kmax\n k1[(length_x_half + 1):length_x] .= 0.0 # prefactor before reactive part\n c1[1:length_x_half] .= 0.0\n c1[(length_x_half + 1):length_x] .= cmax # source term\n\n #### discretization functions ####\n\n function flux!(f, u, edge)\n f[1] = d1 * (u[1, 1] - u[1, 2])\n end\n\n function reaction!(f, u, node)\n f[1] = k1[node.index] * u[1]\n end\n\n function source!(f, node::VoronoiFVM.Node)\n f[1] = c1[node.index]\n end\n\n sys_1D = VoronoiFVM.System(grid_1D,\n VoronoiFVM.Physics(; flux = flux!, reaction = reaction!,\n source = source!))","category":"page"},{"location":"module_examples/Example230_BoundaryFlux/","page":"103: Boundary flux","title":"103: Boundary flux","text":"enable species in only region","category":"page"},{"location":"module_examples/Example230_BoundaryFlux/","page":"103: Boundary flux","title":"103: Boundary flux","text":" enable_species!(sys_1D, ispec_1D, [bulk_1D])\n\n # Stationary solution of both problems\n sol_1D = solve(sys_1D; inival = 0)\n\n p = GridVisualizer(; Plotter = Plotter, layout = (2, 1), clear = true,\n resolution = (800, 500))\n\n scalarplot!(p[1, 1], grid_1D, sol_1D[1, :]; show = true,\n title = \"1D calculation (d1 = $d1, kmax = $kmax, cmax = $cmax)\")\n\n ###########################################################################\n ###################### 2D problem ######################\n ###########################################################################\n\n grid_2D = simplexgrid(X, X)\n\n ispec_2D = 1\n ispec_boundary = 2\n bulk_2D = 1\n active_boundary = 2","category":"page"},{"location":"module_examples/Example230_BoundaryFlux/","page":"103: Boundary flux","title":"103: Boundary flux","text":"parameters for the bulk problem","category":"page"},{"location":"module_examples/Example230_BoundaryFlux/","page":"103: Boundary flux","title":"103: Boundary flux","text":" d2 = 1.0\n k2 = 1.0\n c2 = 1.0\n\n #### discretization functions for bulk species ####\n function flux2D!(f, u, edge)\n f[ispec_2D] = d2 * (u[ispec_2D, 1] - u[ispec_2D, 2])\n end\n\n function reaction2D!(f, u, node)\n f[ispec_2D] = k2 * u[ispec_2D]\n end\n\n function source2D!(f, node)\n f[ispec_2D] = c2\n end\n\n #### discretization functions for boundary species at active boundary ####\n function bflux!(f, u, bedge)\n if bedge.region == active_boundary\n f[ispec_boundary] = db * (u[ispec_boundary, 1] - u[ispec_boundary, 2])\n end\n end\n\n function breaction!(f, u, bnode)\n if bnode.region == active_boundary\n if bnode.coord[2, bnode.index] <= 0.5\n kb = kmax\n else\n kb = 0.0\n end\n\n f[ispec_boundary] = kb * u[ispec_boundary]\n end\n end\n\n function bsource!(f, bnode)\n if bnode.region == active_boundary\n if bnode.coord[2, bnode.index] <= 0.5\n cb = 0.0\n else\n cb = cmax\n end\n\n f[ispec_boundary] = cb\n end\n end\n\n sys_2D = VoronoiFVM.System(grid_2D,\n VoronoiFVM.Physics(; flux = flux2D!, reaction = reaction2D!,\n source = source2D!,\n bflux = bflux!, breaction = breaction!,\n bsource = bsource!);\n unknown_storage = unknown_storage, assembly = assembly)","category":"page"},{"location":"module_examples/Example230_BoundaryFlux/","page":"103: Boundary flux","title":"103: Boundary flux","text":"enable species in only region","category":"page"},{"location":"module_examples/Example230_BoundaryFlux/","page":"103: Boundary flux","title":"103: Boundary flux","text":" enable_species!(sys_2D, ispec_2D, [bulk_2D])\n enable_boundary_species!(sys_2D, ispec_boundary, [active_boundary])\n\n sol_2D = solve(sys_2D; inival = 0)","category":"page"},{"location":"module_examples/Example230_BoundaryFlux/","page":"103: Boundary flux","title":"103: Boundary flux","text":"this is for variable transformation, since we consider right outer boundary and want to transform to x-axis.","category":"page"},{"location":"module_examples/Example230_BoundaryFlux/","page":"103: Boundary flux","title":"103: Boundary flux","text":" function tran32!(a, b)\n a[1] = b[2]\n end","category":"page"},{"location":"module_examples/Example230_BoundaryFlux/","page":"103: Boundary flux","title":"103: Boundary flux","text":"note that if adjusting active_boundary to 3 or 4, then transform needs to be deleted.","category":"page"},{"location":"module_examples/Example230_BoundaryFlux/","page":"103: Boundary flux","title":"103: Boundary flux","text":" bgrid_2D = subgrid(grid_2D, [active_boundary]; boundary = true, transform = tran32!)\n sol_bound = view(sol_2D[ispec_boundary, :], bgrid_2D)\n\n scalarplot!(p[2, 1], bgrid_2D, sol_bound; show = true, cellwise = true,\n title = \"Active boundary in 2D (db = $db, kb = $kmax, cb = $cmax)\")\n\n errorsol = VoronoiFVM.norm(sys_1D, sol_bound - sol_1D', 2)\n\n return errorsol\nend # main\n\nusing Test\nfunction runtests()\n @test main(; unknown_storage = :dense, assembly = :edgewise) < 1.0e-14 &&\n main(; unknown_storage = :sparse, assembly = :edgewise) < 1.0e-14 &&\n main(; unknown_storage = :dense, assembly = :cellwise) < 1.0e-14 &&\n main(; unknown_storage = :sparse, assembly = :cellwise) < 1.0e-14\nend\n\nend # module","category":"page"},{"location":"module_examples/Example230_BoundaryFlux/","page":"103: Boundary flux","title":"103: Boundary flux","text":"","category":"page"},{"location":"module_examples/Example230_BoundaryFlux/","page":"103: Boundary flux","title":"103: Boundary flux","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example226_BoundaryIntegral/#226:-Terminal-flux-calculation-via-test-functions,-nD,-boundary-reaction","page":"226: Terminal flux calculation via test functions, nD, boundary reaction","title":"226: Terminal flux calculation via test functions, nD, boundary reaction","text":"","category":"section"},{"location":"module_examples/Example226_BoundaryIntegral/","page":"226: Terminal flux calculation via test functions, nD, boundary reaction","title":"226: Terminal flux calculation via test functions, nD, boundary reaction","text":"(source code)","category":"page"},{"location":"module_examples/Example226_BoundaryIntegral/","page":"226: Terminal flux calculation via test functions, nD, boundary reaction","title":"226: Terminal flux calculation via test functions, nD, boundary reaction","text":"module Example226_BoundaryIntegral\n\nusing VoronoiFVM, GridVisualize, ExtendableGrids\n\nfunction main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse,\n dim = 2, assembly = :edgewise)\n n = [101, 21, 5]\n X = collect(range(0.0, 1; length = n[dim]))\n if dim == 1\n grid = simplexgrid(X)\n Γ_where_T_equal_1 = [2]\n Γ_where_T_equal_0 = [1]\n elseif dim == 2\n grid = simplexgrid(X, X)\n Γ_where_T_equal_1 = [2]\n Γ_where_T_equal_0 = [4]\n elseif dim == 3\n grid = simplexgrid(X, X, X)\n Γ_where_T_equal_1 = [2]\n Γ_where_T_equal_0 = [4]\n end\n\n function storage(f, u, node)\n f .= u\n end\n\n function flux(f, u, edge)\n f[1] = u[1, 1] - u[1, 2]\n end\n\n function breaction(f, u, node)\n if node.region == Γ_where_T_equal_1[1]\n f[1] = u[1]^2\n end\n end\n\n physics = VoronoiFVM.Physics(; flux = flux,\n storage = storage,\n breaction = breaction)\n\n system = VoronoiFVM.System(grid, physics; assembly = assembly)\n enable_species!(system, 1, [1])\n boundary_dirichlet!(system, 1, Γ_where_T_equal_0[1], 1.0)\n\n U = solve(system; inival = 0)\n\n tf = TestFunctionFactory(system)\n T = testfunction(tf, Γ_where_T_equal_0, Γ_where_T_equal_1)\n\n scalarplot(grid, U[1, :]; Plotter = Plotter, zplane = 0.50001)\n I = integrate(system, T, U)\n B = integrate(system, breaction, U; boundary = true)\n isapprox(-I[1], B[Γ_where_T_equal_1[1]]; rtol = 1.0e-12)\nend\n\nusing Test\nfunction runtests()\n @test main(; dim = 1, unknown_storage = :sparse, assembly = :edgewise)\n @test main(; dim = 1, unknown_storage = :dense, assembly = :edgewise)\n @test main(; dim = 2, unknown_storage = :sparse, assembly = :edgewise)\n @test main(; dim = 2, unknown_storage = :dense, assembly = :edgewise)\n @test main(; dim = 3, unknown_storage = :sparse, assembly = :edgewise)\n @test main(; dim = 3, unknown_storage = :dense, assembly = :edgewise)\n\n @test main(; dim = 1, unknown_storage = :sparse, assembly = :cellwise)\n @test main(; dim = 1, unknown_storage = :dense, assembly = :cellwise)\n @test main(; dim = 2, unknown_storage = :sparse, assembly = :cellwise)\n @test main(; dim = 2, unknown_storage = :dense, assembly = :cellwise)\n @test main(; dim = 3, unknown_storage = :sparse, assembly = :cellwise)\n @test main(; dim = 3, unknown_storage = :dense, assembly = :cellwise)\nend\n\nend","category":"page"},{"location":"module_examples/Example226_BoundaryIntegral/","page":"226: Terminal flux calculation via test functions, nD, boundary reaction","title":"226: Terminal flux calculation via test functions, nD, boundary reaction","text":"","category":"page"},{"location":"module_examples/Example226_BoundaryIntegral/","page":"226: Terminal flux calculation via test functions, nD, boundary reaction","title":"226: Terminal flux calculation via test functions, nD, boundary reaction","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example201_Laplace2D/#201:-2D-Laplace-equation","page":"201: 2D Laplace equation","title":"201: 2D Laplace equation","text":"","category":"section"},{"location":"module_examples/Example201_Laplace2D/","page":"201: 2D Laplace equation","title":"201: 2D Laplace equation","text":"(source code)","category":"page"},{"location":"module_examples/Example201_Laplace2D/","page":"201: 2D Laplace equation","title":"201: 2D Laplace equation","text":"module Example201_Laplace2D\n\nusing VoronoiFVM, ExtendableGrids\nusing GridVisualize\nusing LinearAlgebra\n# Flux function which describes the flux\n# between neighboring control volumes\nfunction g!(f, u, edge)\n f[1] = u[1, 1] - u[1, 2]\nend\n\nfunction main(; Plotter = nothing, n = 5, is_linear = true, assembly = :edgewise)\n nspecies = 1\n ispec = 1\n X = collect(0:(1.0 / n):1)\n grid = VoronoiFVM.Grid(X, X)\n\n physics = VoronoiFVM.Physics(; flux = g!)\n sys = VoronoiFVM.System(grid, physics; is_linear = is_linear, assembly = assembly)\n enable_species!(sys, ispec, [1])\n boundary_dirichlet!(sys, ispec, 1, 0.0)\n boundary_dirichlet!(sys, ispec, 3, 1.0)\n solution = solve(sys; inival = 0)\n nf = nodeflux(sys, solution)\n vis = GridVisualizer(; Plotter = Plotter)\n scalarplot!(vis, grid, solution[1, :]; clear = true, colormap = :summer)\n vectorplot!(vis, grid, nf[:, 1, :]; clear = false, spacing = 0.1, vscale = 0.5)\n reveal(vis)\n return norm(solution) + norm(nf)\nend\n\n# Called by unit test\n\nusing Test\nfunction runtests()\n testval = 9.63318042491699\n\n @test main(; assembly = :edgewise) ≈ testval &&\n main(; assembly = :cellwise) ≈ testval\nend\n\nend","category":"page"},{"location":"module_examples/Example201_Laplace2D/","page":"201: 2D Laplace equation","title":"201: 2D Laplace equation","text":"","category":"page"},{"location":"module_examples/Example201_Laplace2D/","page":"201: 2D Laplace equation","title":"201: 2D Laplace equation","text":"This page was generated using Literate.jl.","category":"page"},{"location":"plutostatichtml_examples/ode-wave1d/","page":"OrdinaryDiffEq.jl 1D wave equation","title":"OrdinaryDiffEq.jl 1D wave equation","text":"\n\n\n\n
    begin\n    import Pkg as _Pkg\n    haskey(ENV, \"PLUTO_PROJECT\") && _Pkg.activate(ENV[\"PLUTO_PROJECT\"])\n    using Test\n    using Revise\n    using Printf\n    using VoronoiFVM\n    using OrdinaryDiffEq\n    using LinearAlgebra\n    using PlutoUI\n    using ExtendableGrids\n    using DataStructures\n    using GridVisualize,CairoMakie\n    CairoMakie.activate!(type=\"svg\")\nend
    \n\n\n","category":"page"},{"location":"plutostatichtml_examples/ode-wave1d/#The-wave-equation-as-system-of-equations","page":"OrdinaryDiffEq.jl 1D wave equation","title":"The wave equation as system of equations","text":"","category":"section"},{"location":"plutostatichtml_examples/ode-wave1d/","page":"OrdinaryDiffEq.jl 1D wave equation","title":"OrdinaryDiffEq.jl 1D wave equation","text":"
    \n
    \n\n\n

    This is the n-dimensional wave equation:

    $$u_{tt}- c^2 \\Delta u = 0$$

    We can create a system of first oder in time PDEs out of this:

    $$ \\begin{aligned}\n u_t - v&=0\\\\\n v_t -c^2\\Delta u&=0\n\\end{aligned}$$

    This allows for a quick implementation in VoronoiFVM (which may be not the optimal way, in particular with respect to time discretization).

    \n\n
    const iu=1; const iv=2;
    \n\n\n
    storage(y,u,node,data)=y.=u;
    \n\n\n
    reaction(y,u,node,data)= y[iu]=-u[iv];
    \n\n\n
    flux(y,u,edge,data)=y[iv]=data.c^2*(u[iu,1]-u[iu,2]);
    \n\n\n\n

    Implementation of transparent or mirror bc

    \n\n
    function brea(y,u,node,data)\n    if node.region==2 \n        if data.bctype==:transparent\n         \ty[iu]=data.c*u[iu]\n        elseif data.bctype==:mirror\n            boundary_dirichlet!(y,u,node,species=1,region=2,value=0)\n        end\n    end\nend;\n
    \n\n\n\n

    Wave velocity:

    \n\n
    const c=0.1
    \n
    0.1
    \n\n\n

    Domain length:

    \n\n
    L=4
    \n
    4
    \n\n
    N=151
    \n
    151
    \n\n
    dt=1.0e-2; tend=100.0;
    \n\n\n
    grid=simplexgrid(range(-L,L,length=N));
    \n\n\n
    sys=VoronoiFVM.System(grid,storage=storage,flux=flux,breaction=brea, reaction=reaction,data=(c=c,bctype=Symbol(bc2type)), species=[1,2])
    \n
    VoronoiFVM.System{Float64, Float64, Int32, Int64, Matrix{Int32}, Matrix{Float64}}(num_species=2)\n
    \n\n\n

    Perturbation in the center of the domain:

    \n\n
    begin\n   inival=unknowns(sys,inival=0)\t\n   inival[1,:].=map(x->cos(κ*π*x)*exp(-x^2/0.1) ,grid)\t\nend;
    \n\n\n
    problem = ODEProblem(sys,inival,(0.0,tend));
    \n\n\n
    tsol=solve(problem,diffeqmethods[method]();  \n                                   force_dtmin=true,\n                                   adaptive=true,\n                                   reltol=1.0e-4,\n                                   abstol=1.0e-5,\n                                   dtmin=dt,\n                                   progress=true,\n                                   progress_steps=1,\n                                   dt=dt);
    \n\n\n\n

    Boundary condition at x=L:

    \n\n\n

    Reflection (Neumann) bc $\\partial_x u|_{x=L}=0$

    \n\n\n

    Package wave number κ: method:

    t=49.95

    \n\n
    let\n    u=tsol1(t)\n    scalarplot(grid,u[1,:],flimits=(-1,1),clear=true,show=true,title=\"t=$(t)\",Plotter=CairoMakie,resolution=(600,150))\nend\n\n
    \n\n\n
    let \n    vis=GridVisualizer(Plotter=CairoMakie)\n    scalarplot!(vis,sys,tsol1,colormap=:bwr,limits=(-1,1), levels=(-0.9:0.2:0.9))\n    reveal(vis)\nend
    \n\n\n
    tsol1=reshape(tsol,sys);
    \n\n\n
    diffeqmethods=OrderedDict(\n\"QNDF2\" =>  QNDF2,\n\"FBDF\" => FBDF,\n\"Rosenbrock23 (Rosenbrock)\" => Rosenbrock23,\n\"Implicit Euler\" => ImplicitEuler,\n\"Implicit Midpoint\" => ImplicitMidpoint,\n)
    \n
    OrderedDict{String, UnionAll} with 5 entries:\n  \"QNDF2\"                     => QNDF2\n  \"FBDF\"                      => FBDF\n  \"Rosenbrock23 (Rosenbrock)\" => Rosenbrock23\n  \"Implicit Euler\"            => ImplicitEuler\n  \"Implicit Midpoint\"         => ImplicitMidpoint
    \n
    \n

    Built with Julia 1.10.2 and

    \nCairoMakie 0.11.9
    \nDataStructures 0.18.17
    \nExtendableGrids 1.3.1
    \nGridVisualize 1.5.0
    \nOrdinaryDiffEq 6.74.0
    \nPkg 1.10.0
    \nPlutoUI 0.7.58
    \nRevise 3.5.14
    \nVoronoiFVM 1.19.1\n
    \n\n","category":"page"},{"location":"plutostatichtml_examples/ode-wave1d/","page":"OrdinaryDiffEq.jl 1D wave equation","title":"OrdinaryDiffEq.jl 1D wave equation","text":"EditURL = \"https://github.com/j-fu/VoronoiFVM.jl/blob/main/nothing\"","category":"page"},{"location":"module_examples/Example206_JouleHeat/#206:-2D-Joule-heating","page":"206: 2D Joule heating","title":"206: 2D Joule heating","text":"","category":"section"},{"location":"module_examples/Example206_JouleHeat/","page":"206: 2D Joule heating","title":"206: 2D Joule heating","text":"(source code)","category":"page"},{"location":"module_examples/Example206_JouleHeat/","page":"206: 2D Joule heating","title":"206: 2D Joule heating","text":"beginaligned\n-nabla leftcdot (kappa(T) nabla phiright) = 0\npartial_t (cT) - nablacdot left(lambda nabla Tright) = kappa(T) nabla phi^2\nkappa(T)= kappa_0 exp(alpha(T-T0))\nendaligned","category":"page"},{"location":"module_examples/Example206_JouleHeat/","page":"206: 2D Joule heating","title":"206: 2D Joule heating","text":"The discretization uses the approach developed in A. Bradji, R. Herbin, DOI 10.1093/imanum/drm030.","category":"page"},{"location":"module_examples/Example206_JouleHeat/","page":"206: 2D Joule heating","title":"206: 2D Joule heating","text":"module Example206_JouleHeat\n\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing ExtendableSparse\nusing GridVisualize\nusing LinearAlgebra\nusing SimplexGridFactory\nusing Triangulate\n\nfunction main(; nref = 0, Plotter = nothing, verbose = \"and\", unknown_storage = :sparse, assembly = :edgewise,\n ythin = 0.25)\n\n # Create grid\n b = SimplexGridBuilder(; Generator = Triangulate)\n p00 = point!(b, 0, 0)\n p30 = point!(b, 3, 0)\n p32 = point!(b, 3, 1)\n p21 = point!(b, 2, ythin)\n p11 = point!(b, 1, ythin)\n p02 = point!(b, 0, 1)\n\n facetregion!(b, 4)\n facet!(b, p00, p30)\n facetregion!(b, 2)\n facet!(b, p30, p32)\n facetregion!(b, 3)\n facet!(b, p32, p21)\n facet!(b, p21, p11)\n facet!(b, p11, p02)\n facetregion!(b, 1)\n facet!(b, p02, p00)\n\n grid = simplexgrid(b; maxvolume = 0.01 * 4.0^(-nref))\n\n # Describe problem\n iϕ::Int = 1\n iT::Int = 2\n κ0::Float64 = 1\n α::Float64 = 1\n T0::Float64 = 0.5\n λ::Float64 = 1\n c::Float64 = 1\n\n function storage!(y, u, node)\n y[iT] = c * u[iT]\n end\n\n κ(T) = κ0 * exp(α * (T - T0))\n\n function flux!(y, u, edge)\n y[iϕ] = κ(y[iT]) * (u[iϕ, 1] - u[iϕ, 2])\n y[iT] = λ * (u[iT, 1] - u[iT, 2])\n end\n\n # The convention in VoronoiFVM.jl is to have all terms depending on the solution\n # on the left hand side of the equation. That is why we have the minus sign here.\n function jouleheat!(y, u, edge)\n y[iT] = -κ(y[iT]) * (u[iϕ, 1] - u[iϕ, 2]) * (u[iϕ, 1] - u[iϕ, 2])\n end\n\n function bcondition!(y, u, node)\n boundary_dirichlet!(y, u, node; species = iϕ, region = 1, value = -10)\n boundary_dirichlet!(y, u, node; species = iϕ, region = 2, value = 10)\n\n boundary_robin!(y, u, node; species = iT, region = 1, value = T0, factor = 0.5)\n boundary_robin!(y, u, node; species = iT, region = 2, value = T0, factor = 0.5)\n boundary_robin!(y, u, node; species = iT, region = 3, value = T0, factor = 0.5)\n boundary_robin!(y, u, node; species = iT, region = 4, value = T0, factor = 0.5)\n end\n\n sys = VoronoiFVM.System(grid; bcondition = bcondition!, flux = flux!,\n edgereaction = jouleheat!, storage = storage!,\n species = [iϕ, iT], assembly = assembly)\n\n sol = solve(sys; verbose)\n\n vis = GridVisualizer(; Plotter, layout = (2, 1))\n scalarplot!(vis[1, 1], grid, sol[iϕ, :]; title = \"ϕ\", colormap = :bwr)\n scalarplot!(vis[2, 1], grid, sol[iT, :]; title = \"T\", colormap = :hot)\n reveal(vis)\n norm(sol, Inf)\nend\n\nusing Test\nfunction runtests()\n testval = 24.639120035942938\n @test main(; assembly = :edgewise) ≈ testval &&\n main(; assembly = :cellwise) ≈ testval\nend\nend","category":"page"},{"location":"module_examples/Example206_JouleHeat/","page":"206: 2D Joule heating","title":"206: 2D Joule heating","text":"","category":"page"},{"location":"module_examples/Example206_JouleHeat/","page":"206: 2D Joule heating","title":"206: 2D Joule heating","text":"This page was generated using Literate.jl.","category":"page"},{"location":"plutostatichtml_examples/ode-diffusion1d/","page":"OrdinaryDiffEq.jl nonlinear diffusion","title":"OrdinaryDiffEq.jl nonlinear diffusion","text":"\n\n\n\n
    begin\n    import Pkg as _Pkg\n    haskey(ENV, \"PLUTO_PROJECT\") && _Pkg.activate(ENV[\"PLUTO_PROJECT\"])\n    using Test\n    using Revise\n    using Printf\n    using VoronoiFVM\n    using OrdinaryDiffEq\n    using LinearAlgebra\n    using PlutoUI\n    using DataStructures\n    using GridVisualize,CairoMakie\nend
    \n\n\n\n

    Solve the nonlinear diffusion equation

    $$\\partial_t u -\\Delta u^m = 0$$

    in $\\Omega=(-1,1)$ with homogeneous Neumann boundary conditons using the implicit Euler method.

    This equation is also called \"porous medium equation\". The Barenblatt solution

    $$b(x,t)=\\max\\left(0,t^{-\\alpha}\\left(1-\\frac{\\alpha(m-1)r^2}{2dmt^{\\frac{2\\alpha}{d}}}\\right)^{\\frac{1}{m-1}}\\right)$$

    is an exact solution of this problem which for m>1 has a finite support. We initialize this problem with the exact solution for $t=t_0=0.001$.

    (see Barenblatt, G. I. \"On nonsteady motions of gas and fluid in porous medium.\" Appl. Math. and Mech.(PMM) 16.1 (1952): 67-78.)

    Here, we compare the implicit Euler approach in VoronoiFVM with the ODE solvers in DifferentialEquations.jl and demonstrate the possibility to use VoronoiFVM to define differential operators compatible with its ODEFunction interface.

    \n\n
    function barenblatt(x,t,m)\n    tx=t^(-1.0/(m+1.0))\n    xx=x*tx\n    xx=xx*xx\n    xx=1- xx*(m-1)/(2.0*m*(m+1));\n    if xx<0.0\n        xx=0.0\n    end\n    return tx*xx^(1.0/(m-1.0))\nend
    \n
    barenblatt (generic function with 1 method)
    \n\n
    function create_porous_medium_problem(n,m)\n    h=1.0/convert(Float64,n/2)\n    X=collect(-1:h:1)\n    grid=VoronoiFVM.Grid(X)\n\n    function flux!(f,u,edge)\n        f[1]=u[1,1]^m-u[1,2]^m\n    end\n\n    storage!(f,u,node)= f[1]=u[1]\n\n    sys=VoronoiFVM.System(grid,flux=flux!,storage=storage!, species=1)\n    sys,X\nend
    \n
    create_porous_medium_problem (generic function with 1 method)
    \n\n
    begin\nfunction run_vfvm(;n=20,m=2,t0=0.001, tend=0.01,tstep=1.0e-6)\n    sys,X=create_porous_medium_problem(n,m)\n    inival=unknowns(sys)\n    inival[1,:].=map(x->barenblatt(x,t0,m),X)\n    sol=VoronoiFVM.solve(sys;inival,times=(t0,tend),Δt=tstep,Δu_opt=0.01,Δt_min=tstep,store_all=true,log=true, reltol=1.0e-3)\n    err=norm(sol[1,:,end]-map(x->barenblatt(x,tend,m),X))\n    sol,sys,err\nend\nrun_vfvm(m=2,n=10) # \"Precompile\"\nend;
    \n\n\n
    begin\n    function run_diffeq(;n=20,m=2, t0=0.001,tend=0.01,solver=nothing)\n    sys,X=create_porous_medium_problem(n,m)\n    inival=unknowns(sys)\n    inival[1,:].=map(x->barenblatt(x,t0,m),X)\n    problem = ODEProblem(sys,inival,(t0,tend))\n    odesol = solve(problem,solver)\n    sol=reshape(odesol,sys)\n    err=norm(sol[1,:,end]-map(x->barenblatt(x,tend,m),X))\n    sol, sys,err\n    end\nfor method in diffeqmethods\n    run_diffeq(m=2,n=10,solver=method.second()) # \"Precompile\"\nend\n    end;
    \n\n\n\n
    OrderedDict{String, UnionAll} with 4 entries:\n  \"Rosenbrock23 (Rosenbrock)\"    => Rosenbrock23\n  \"QNDF2 (Like matlab's ode15s)\" => QNDF2\n  \"FBDF\"                         => FBDF\n  \"Implicit Euler\"               => ImplicitEuler
    \n\n
    t1=@elapsed sol1,sys1,err1=run_vfvm(m=m,n=n);history_summary(sys1)
    \n
    (seconds = 1.06, tasm = 0.244, tlinsolve = 0.012, steps = 832, iters = 1662, maxabsnorm = 2.65e-6, maxrelnorm = 0.000263, maxroundoff = 2.46e-16, iters_per_step = 2.0, facts_per_step = 0.0, liniters_per_step = 0.0)
    \n\n\n

    method:

    \n\n
    m=2; n=50;
    \n\n\n
    t2=@elapsed sol2,sys2,err2=run_diffeq(m=m,n=n,solver=diffeqmethods[method]());history_summary(sys2)
    \n
    (nd = 166, njac = 82, nf = 248)
    \n\n\n\n\n\n

    Left: VoronoiFVM implicit Euler: 1124 ms e=5.17e-02

    Right: Rosenbrock23 (Rosenbrock): 24 ms, e=4.62e-02

    \n\n
    @test err2<err1
    \n
    Test Passed
    \n
    \n

    Built with Julia 1.10.2 and

    \nCairoMakie 0.11.6
    \nDataStructures 0.18.16
    \nGridVisualize 1.5.0
    \nOrdinaryDiffEq 6.58.2
    \nPkg 1.10.0
    \nPlutoUI 0.7.55
    \nRevise 3.5.13
    \nVoronoiFVM 1.17.1\n
    \n\n","category":"page"},{"location":"plutostatichtml_examples/ode-diffusion1d/","page":"OrdinaryDiffEq.jl nonlinear diffusion","title":"OrdinaryDiffEq.jl nonlinear diffusion","text":"EditURL = \"https://github.com/j-fu/VoronoiFVM.jl/blob/main/nothing\"","category":"page"},{"location":"module_examples/Example207_NonlinearPoisson2D/#207:-2D-Nonlinear-Poisson-equation","page":"207: 2D Nonlinear Poisson equation","title":"207: 2D Nonlinear Poisson equation","text":"","category":"section"},{"location":"module_examples/Example207_NonlinearPoisson2D/","page":"207: 2D Nonlinear Poisson equation","title":"207: 2D Nonlinear Poisson equation","text":"(source code)","category":"page"},{"location":"module_examples/Example207_NonlinearPoisson2D/","page":"207: 2D Nonlinear Poisson equation","title":"207: 2D Nonlinear Poisson equation","text":"module Example207_NonlinearPoisson2D\n\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing ExtendableSparse\nusing GridVisualize\nusing LinearSolve\nusing ILUZero\n\nfunction main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse,\n method_linear = nothing, assembly = :edgewise,\n precon_linear = A -> VoronoiFVM.Identity())\n h = 1.0 / convert(Float64, n)\n X = collect(0.0:h:1.0)\n Y = collect(0.0:h:1.0)\n\n grid = VoronoiFVM.Grid(X, Y)\n\n eps = 1.0e-2\n\n physics = VoronoiFVM.Physics(; reaction = function (f, u, node)\n f[1] = u[1]^2\n end, flux = function (f, u, edge)\n f[1] = eps * (u[1, 1]^2 - u[1, 2]^2)\n end, source = function (f, node)\n x1 = node[1] - 0.5\n x2 = node[2] - 0.5\n f[1] = exp(-20.0 * (x1^2 + x2^2))\n end, storage = function (f, u, node)\n f[1] = u[1]\n end)\n sys = VoronoiFVM.System(grid, physics; unknown_storage, assembly = assembly)\n enable_species!(sys, 1, [1])\n\n boundary_dirichlet!(sys, 1, 2, 0.1)\n boundary_dirichlet!(sys, 1, 4, 0.1)\n\n inival = unknowns(sys)\n inival .= 0.5\n\n control = VoronoiFVM.NewtonControl()\n control.verbose = verbose\n control.reltol_linear = 1.0e-5\n control.method_linear = method_linear\n control.precon_linear = precon_linear\n tstep = 0.01\n time = 0.0\n u15 = 0\n p = GridVisualizer(; Plotter = Plotter)\n while time < 1.0\n time = time + tstep\n U = solve(sys; inival, control, tstep)\n u15 = U[15]\n inival .= U\n\n scalarplot!(p[1, 1], grid, U[1, :]; Plotter = Plotter, clear = true, show = true)\n tstep *= 1.0\n end\n return u15\nend\n\nusing Test\nfunction runtests()","category":"page"},{"location":"module_examples/Example207_NonlinearPoisson2D/","page":"207: 2D Nonlinear Poisson equation","title":"207: 2D Nonlinear Poisson equation","text":"test at once for iterative solution here","category":"page"},{"location":"module_examples/Example207_NonlinearPoisson2D/","page":"207: 2D Nonlinear Poisson equation","title":"207: 2D Nonlinear Poisson equation","text":" testval = 0.3554284760906605\n @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :sparse, method_linear = KrylovJL_CG(), precon_linear = ILUZeroPreconditioner,\n assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :dense, method_linear = KrylovJL_CG(), precon_linear = ILUZeroPreconditioner,\n assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval &&\n main(; unknown_storage = :sparse, method_linear = KrylovJL_CG(), precon_linear = ILUZeroPreconditioner,\n assembly = :cellwise) ≈ testval &&\n main(; unknown_storage = :dense, method_linear = KrylovJL_CG(), precon_linear = ILUZeroPreconditioner,\n assembly = :cellwise) ≈ testval\nend\nend","category":"page"},{"location":"module_examples/Example207_NonlinearPoisson2D/","page":"207: 2D Nonlinear Poisson equation","title":"207: 2D Nonlinear Poisson equation","text":"","category":"page"},{"location":"module_examples/Example207_NonlinearPoisson2D/","page":"207: 2D Nonlinear Poisson equation","title":"207: 2D Nonlinear Poisson equation","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example205_StagnationPoint/#205:-Convection-in-axisymmetric-stagnation-point-flow","page":"205: Convection in axisymmetric stagnation point flow","title":"205: Convection in axisymmetric stagnation point flow","text":"","category":"section"},{"location":"module_examples/Example205_StagnationPoint/","page":"205: Convection in axisymmetric stagnation point flow","title":"205: Convection in axisymmetric stagnation point flow","text":"(source code)","category":"page"},{"location":"module_examples/Example205_StagnationPoint/","page":"205: Convection in axisymmetric stagnation point flow","title":"205: Convection in axisymmetric stagnation point flow","text":"Solve the equation","category":"page"},{"location":"module_examples/Example205_StagnationPoint/","page":"205: Convection in axisymmetric stagnation point flow","title":"205: Convection in axisymmetric stagnation point flow","text":"beginaligned\n -nabla ( D nabla u - vec v u) = 0\n u_Gamma_1 =1\n u_Gamma_0 =0\n (partial_n u)_Gamma_out = 0\nendaligned","category":"page"},{"location":"module_examples/Example205_StagnationPoint/","page":"205: Convection in axisymmetric stagnation point flow","title":"205: Convection in axisymmetric stagnation point flow","text":"in Omega=(01)times (01) with Gamma_1 = (0025)times 1, Gamma_0=(0251)times 1 and Gamma_out = 1times (01). On boundary parts not listed, no-flow boundary conditions are assumed.","category":"page"},{"location":"module_examples/Example205_StagnationPoint/","page":"205: Convection in axisymmetric stagnation point flow","title":"205: Convection in axisymmetric stagnation point flow","text":"The axisymmetric stagnation point flow vec v(rz)=(vr-2vz) is divergence free.","category":"page"},{"location":"module_examples/Example205_StagnationPoint/","page":"205: Convection in axisymmetric stagnation point flow","title":"205: Convection in axisymmetric stagnation point flow","text":"module Example205_StagnationPoint\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\n\nfunction main(; nref = 0, gridname = nothing, Plotter = nothing, D = 0.01, v = 100, cin = 1.0, assembly = :cellwise)\n H = 1.0\n L = 1.0\n\n Γ_1 = 5\n Γ_0 = 4\n Γ_out = 2\n\n if !isnothing(gridname)\n grid = simplexgrid(gridname)\n else\n grid = simplexgrid(range(0, L; length = 10 * 2^nref),\n range(0, H; length = 10 * 2^nref))\n bfacemask!(grid, [0, H], [0.25L, H], 5)\n end\n circular_symmetric!(grid)\n\n frz(r, z) = (v * r, -2v * z)\n\n evelo = edgevelocities(grid, frz)\n bfvelo = bfacevelocities(grid, frz)\n\n function flux!(f, u, edge)\n vd = evelo[edge.index] / D\n bp = fbernoulli(vd)\n bm = fbernoulli(-vd)\n f[1] = D * (bp * u[1] - bm * u[2])\n end\n\n function outflow!(f, u, node)\n if node.region == Γ_out\n f[1] = bfvelo[node.ibnode, node.ibface] * u[1]\n end\n end\n\n ispec = 1\n physics = VoronoiFVM.Physics(; flux = flux!, breaction = outflow!)\n sys = VoronoiFVM.System(grid, physics; assembly = assembly)\n enable_species!(sys, ispec, [1])\n boundary_dirichlet!(sys, ispec, Γ_1, cin)\n boundary_dirichlet!(sys, ispec, Γ_0, 0)\n\n tf = TestFunctionFactory(sys)\n tf_in = testfunction(tf, [Γ_out], [Γ_1])\n tf_out = testfunction(tf, [Γ_1], [Γ_out])\n\n sol = solve(sys)\n\n I_in = integrate(sys, tf_in, sol)\n I_out = integrate(sys, tf_out, sol)\n\n scalarplot(sys, sol; Plotter = Plotter)\n\n # Test if inflow=outflow\n test1 = isapprox(I_in, -I_out; rtol = 1.0e-5)\n\n # Test global maximum principle\n test2 = isapprox(maximum(sol), cin; rtol = 1.0e-10)\n test3 = isapprox(minimum(sol), 0; atol = 1.0e-10)\n\n # test zero divergence of fvm velocities\n div = VoronoiFVM.calc_divergences(sys, evelo, bfvelo)\n test4 = all(x -> abs(x) < 1.0e-12, div)\n\n test1 && test2 && test3 && test4\nend\n\nusing Test\nfunction runtests()\n test0 = true\n if VERSION > v\"1.6\"","category":"page"},{"location":"module_examples/Example205_StagnationPoint/","page":"205: Convection in axisymmetric stagnation point flow","title":"205: Convection in axisymmetric stagnation point flow","text":"test on unstructured grid","category":"page"},{"location":"module_examples/Example205_StagnationPoint/","page":"205: Convection in axisymmetric stagnation point flow","title":"205: Convection in axisymmetric stagnation point flow","text":" gridname = joinpath(pkgdir(VoronoiFVM), \"assets\", \"rz2d.sg\")\n test0 = test0 && main(; assembly = :edgewise, gridname) && main(; assembly = :cellwise, gridname)\n end\n @test test0 && main(; assembly = :edgewise) && main(; assembly = :cellwise)\nend\n\nend","category":"page"},{"location":"module_examples/Example205_StagnationPoint/","page":"205: Convection in axisymmetric stagnation point flow","title":"205: Convection in axisymmetric stagnation point flow","text":"","category":"page"},{"location":"module_examples/Example205_StagnationPoint/","page":"205: Convection in axisymmetric stagnation point flow","title":"205: Convection in axisymmetric stagnation point flow","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example102_StationaryConvectionDiffusion1D/#102:-1D-Stationary-convection-diffusion-equation","page":"102: 1D Stationary convection-diffusion equation","title":"102: 1D Stationary convection-diffusion equation","text":"","category":"section"},{"location":"module_examples/Example102_StationaryConvectionDiffusion1D/","page":"102: 1D Stationary convection-diffusion equation","title":"102: 1D Stationary convection-diffusion equation","text":"(source code)","category":"page"},{"location":"module_examples/Example102_StationaryConvectionDiffusion1D/","page":"102: 1D Stationary convection-diffusion equation","title":"102: 1D Stationary convection-diffusion equation","text":"Solve the equation","category":"page"},{"location":"module_examples/Example102_StationaryConvectionDiffusion1D/","page":"102: 1D Stationary convection-diffusion equation","title":"102: 1D Stationary convection-diffusion equation","text":"-nabla ( D nabla u - v u) = 0","category":"page"},{"location":"module_examples/Example102_StationaryConvectionDiffusion1D/","page":"102: 1D Stationary convection-diffusion equation","title":"102: 1D Stationary convection-diffusion equation","text":"in Omega=(01) with boundary condition u(0)=0 and u(1)=1. v could be e.g. the velocity of a moving medium or the gradient of an electric field.","category":"page"},{"location":"module_examples/Example102_StationaryConvectionDiffusion1D/","page":"102: 1D Stationary convection-diffusion equation","title":"102: 1D Stationary convection-diffusion equation","text":"This is a convection dominant second order boundary value problem which obeys a local and a global maximum principle: the solution which is bounded by the values at the boundary and has no local extrema in the interior. If v is large compared to D, a boundary layer is observed.","category":"page"},{"location":"module_examples/Example102_StationaryConvectionDiffusion1D/","page":"102: 1D Stationary convection-diffusion equation","title":"102: 1D Stationary convection-diffusion equation","text":"The maximum principle of the solution can only be guaranteed it the discretization is performed accordingly: the flux function must monotonically increase in the first argument and monotonically decrease in the second argument.","category":"page"},{"location":"module_examples/Example102_StationaryConvectionDiffusion1D/","page":"102: 1D Stationary convection-diffusion equation","title":"102: 1D Stationary convection-diffusion equation","text":"The example describes three possible ways to define the flux function and demonstrates the impact on the qualitative properties of the solution.","category":"page"},{"location":"module_examples/Example102_StationaryConvectionDiffusion1D/","page":"102: 1D Stationary convection-diffusion equation","title":"102: 1D Stationary convection-diffusion equation","text":"module Example102_StationaryConvectionDiffusion1D\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\n\n# Central difference flux. The velocity term is discretized using the\n# average of the solution in the endpoints of the grid. If the local Peclet\n# number v*h/D>1, the monotonicity property is lost. Grid refinement\n# can fix this situation by decreasing $h$.\n\nfunction central_flux!(f, u, edge, data)\n f_diff = data.D * (u[1, 1] - u[1, 2])\n vh = project(edge, data.v)\n f[1] = f_diff + vh * (u[1, 1] + u[1, 2]) / 2\nend\n\n# The simple upwind flux corrects the monotonicity properties essentially\n# via brute force and loses one order of convergence for small $h$ compared\n# to the central flux.\n\nfunction upwind_flux!(f, u, edge, data)\n fdiff = data.D * (u[1] - u[1, 2])\n vh = project(edge, data.v)\n if vh > 0\n f[1] = fdiff + vh * u[1, 1]\n else\n f[1] = fdiff + vh * u[1, 2]\n end\nend\n\n# The exponential fitting flux has the proper monotonicity properties and\n# kind of interpolates in a clever way between central\n# and upwind flux. It can be derived by solving the two-point boundary value problem\n# at the grid interval analytically.\n\n# Bernoulli function used in the exponential fitting discretization\nfunction bernoulli(x)\n if abs(x) < nextfloat(eps(typeof(x)))\n return 1\n end\n return x / (exp(x) - 1)\nend\n\nfunction exponential_flux!(f, u, edge, data)\n vh = project(edge, data.v)\n Bplus = data.D * bernoulli(vh / data.D)\n Bminus = data.D * bernoulli(-vh / data.D)\n f[1] = Bminus * u[1, 1] - Bplus * u[1, 2]\nend\n\nfunction calculate(grid, data, flux, verbose)\n sys = VoronoiFVM.System(grid, VoronoiFVM.Physics(; flux = flux, data = data))\n\n # Add species 1 to region 1\n enable_species!(sys, 1, [1])\n\n # Set boundary conditions\n boundary_dirichlet!(sys, 1, 1, 0.0)\n boundary_dirichlet!(sys, 1, 2, 1.0)\n\n # Create a solution array\n inival = unknowns(sys; inival = 0.5)\n solution = unknowns(sys)\n\n # Create solver control info\n control = VoronoiFVM.NewtonControl()\n control.verbose = verbose\n\n # Stationary solution of the problem\n solution = solve(sys; inival, verbose)\n return solution\nend\n\nfunction main(; n = 10, Plotter = nothing, verbose = false, D = 0.01, v = 1.0)\n\n # Create a one-dimensional discretization\n h = 1.0 / convert(Float64, n)\n grid = VoronoiFVM.Grid(collect(0:h:1))\n\n data = (v = [v], D = D)","category":"page"},{"location":"module_examples/Example102_StationaryConvectionDiffusion1D/","page":"102: 1D Stationary convection-diffusion equation","title":"102: 1D Stationary convection-diffusion equation","text":"Calculate three stationary solutions with different ways to calculate flux","category":"page"},{"location":"module_examples/Example102_StationaryConvectionDiffusion1D/","page":"102: 1D Stationary convection-diffusion equation","title":"102: 1D Stationary convection-diffusion equation","text":" solution_exponential = calculate(grid, data, exponential_flux!, verbose)\n solution_upwind = calculate(grid, data, upwind_flux!, verbose)\n solution_central = calculate(grid, data, central_flux!, verbose)","category":"page"},{"location":"module_examples/Example102_StationaryConvectionDiffusion1D/","page":"102: 1D Stationary convection-diffusion equation","title":"102: 1D Stationary convection-diffusion equation","text":"Visualize solutions using GridVisualize.jl","category":"page"},{"location":"module_examples/Example102_StationaryConvectionDiffusion1D/","page":"102: 1D Stationary convection-diffusion equation","title":"102: 1D Stationary convection-diffusion equation","text":" p = GridVisualizer(; Plotter = Plotter, layout = (3, 1))\n scalarplot!(p[1, 1], grid, solution_exponential[1, :]; title = \"exponential\")\n scalarplot!(p[2, 1], grid, solution_upwind[1, :]; title = \"upwind\")\n scalarplot!(p[3, 1], grid, solution_central[1, :]; title = \"centered\", show = true)\n\n # Return test value\n return sum(solution_exponential) + sum(solution_upwind) + sum(solution_central)\nend\n\nusing Test\nfunction runtests()\n testval = 2.523569744561089\n @test main() ≈ testval\nend\n\nend","category":"page"},{"location":"module_examples/Example102_StationaryConvectionDiffusion1D/","page":"102: 1D Stationary convection-diffusion equation","title":"102: 1D Stationary convection-diffusion equation","text":"","category":"page"},{"location":"module_examples/Example102_StationaryConvectionDiffusion1D/","page":"102: 1D Stationary convection-diffusion equation","title":"102: 1D Stationary convection-diffusion equation","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example301_Laplace3D/#301:-3D-Laplace-equation","page":"301: 3D Laplace equation","title":"301: 3D Laplace equation","text":"","category":"section"},{"location":"module_examples/Example301_Laplace3D/","page":"301: 3D Laplace equation","title":"301: 3D Laplace equation","text":"(source code)","category":"page"},{"location":"module_examples/Example301_Laplace3D/","page":"301: 3D Laplace equation","title":"301: 3D Laplace equation","text":"module Example301_Laplace3D\n\nusing VoronoiFVM, ExtendableGrids\nusing GridVisualize\n\n# Flux function which describes the flux\n# between neighboring control volumes\nfunction g!(f, u, edge)\n f[1] = u[1, 1] - u[1, 2]\nend\n\nfunction s(f, node)\n n = view(node.coord, :, node.index)\n f[1] = n[1] * sin(5.0 * n[2]) * exp(n[3])\nend\n\nfunction main(; Plotter = nothing, n = 5, assembly = :edgewise)\n nspecies = 1\n ispec = 1\n X = collect(0:(1 / n):1)\n grid = VoronoiFVM.Grid(X, X, X)\n physics = VoronoiFVM.Physics(; flux = g!, source = s)\n sys = VoronoiFVM.System(grid, physics; assembly = assembly)\n enable_species!(sys, ispec, [1])\n boundary_dirichlet!(sys, ispec, 5, 0.0)\n boundary_dirichlet!(sys, ispec, 6, 0.0)\n solution = solve(sys)\n scalarplot(grid, solution[1, :]; Plotter = Plotter)\n return solution[43]\nend\n\n# Called by unit test\n\nusing Test\nfunction runtests()\n testval = 0.012234524449380824\n @test main(; assembly = :edgewise) ≈ testval &&\n main(; assembly = :cellwise) ≈ testval\nend\n\nend","category":"page"},{"location":"module_examples/Example301_Laplace3D/","page":"301: 3D Laplace equation","title":"301: 3D Laplace equation","text":"","category":"page"},{"location":"module_examples/Example301_Laplace3D/","page":"301: 3D Laplace equation","title":"301: 3D Laplace equation","text":"This page was generated using Literate.jl.","category":"page"},{"location":"method/#The-Voronoi-finite-volume-method","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"","category":"section"},{"location":"method/#Construction-of-control-volumes","page":"The Voronoi finite volume method","title":"Construction of control volumes","text":"","category":"section"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"Start with a boundary conforming Delaunay triangulation of a polygonal domain (intervals in 1D, triangles in 2D, tetrahedra in 3D). Such a triangulation can be generated by e.g. by the mesh generators triangle and TetGen. These are available in Julia via Triangulate.jl and TetGen.jl. For simple geometries – tensor products of lower dimensional grids – such triangulation can be created more easily. The package ExtendableGrids.jl manages the grid data structure which is used in this package. SimplexGridFactory.jl interfaces this grid structure with Triangulate.jl and TetGen.jl and provides an API for incrementally setting up geometry descriptions.\nJoin triangle circumcenters by lines rightarrow create Voronoi cells which can serve as control volumes, akin to representative elementary volumes (REV) used to derive conservation laws. ","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"
    \n\n
    ","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"Black + green: triangle nodes\nGray: triangle edges\nBlue: triangle circumcenters\nRed: Boundaries of Voronoi cells","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"In order to make this construction possible, the triangulation must have the boundary conforming Delaunay property: ","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"The interior of any triangle circumcircle does not contain any other node of the triangulation\nAll circumcircle centers lay within the domain ","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"In 2D, an equivalent condition is:","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"The sum of triangle angles opposite to a given interior edge is less than pi\nTriangle angles opposite to boundary edges are less than fracpi2.","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"As a consequence, there is a 1:1 incidence between triangulation nodes and Voronoi cells. Moreover, the angle between the interface between two neighboring Voronoi cells and the edge between their corresponding nodes is fracpi2. Therefore the edge direction is aligned with the normal direction with respect to the boundary of the Voronoi cell. This makes it easy to use these Voronoi cells as REVs aka control volumes aka finite volume cells and to derive a space discretization for a conservation law based on very same balance rules used to derive this conservation law.","category":"page"},{"location":"method/#The-discretization-approach","page":"The Voronoi finite volume method","title":"The discretization approach","text":"","category":"section"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"
    \n\n
    ","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"Given a continuity equation nablacdot vec j=f in a domain Omega, integrate it over a control volume omega_k with associated node vec x_k and apply Gauss theorem:","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"beginaligned\n0=int_omega_k (nablacdot vec j -f ) domega\n=int_partialomega_k vec jcdot vec n ds - int_omega_k f domega\n=sum_lin N_k int_omega_kcap omega_l vec jcdot vec n ds + int_partialomega_kcap partialOmega vec jcdot vec n ds - int_omega_k f domega \napprox sum_lin N_k fracsigma_klh_klg(u_k u_l) - omega_k f_k + textboundary terms\nendaligned","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"Here, N_k is the set of neighbor control volumes, sigma_kl=omega_kcap omega_l, h_kl=vec x_k -vec x_l, where cdot denotes the measure (length resp. area) of a geometrical entity. In the approximation step, we replaced the normal flux integral over the interface between two control volumes by the measure of this interface multiplied by a function depending on the unknowns u_k u_l associated to the respective nodes divided by the distance between these nodes. The flux function g can be derived from usual finite difference formulas discretizing a particular flux law.","category":"page"},{"location":"method/#Flux-laws","page":"The Voronoi finite volume method","title":"Flux laws","text":"","category":"section"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"For instance, for the diffusion flux vec j=-Dvecnabla u, we use g(u_k u_l)=D(u_k -u_l).","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"For a convective diffusion flux vec j = -Dvec nabla u + u vec v, one can chose the upwind flux","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"beginaligned\ng(u_k u_l)=D(u_k -u_l) + \nv_klbegincases\nu_k v_kl0\nu_l v_klleq 0\nendcases\nendaligned","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"where v_kl=frach_klsigma_klint_omega_kcap omega_l vec v cdot vec n_kl ds Fluxes also can depend nonlinearily on u.","category":"page"},{"location":"method/#Boundary-conditions","page":"The Voronoi finite volume method","title":"Boundary conditions","text":"","category":"section"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"To implement a Robin boundary condition on Gamma=partialOmega ","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"- vec j cdot vec n + a u = b","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"we note that by the very construction, the discretization nodes associated to control volumes adjacent to the domain boundary are located at the domain boundary, thus we can assume that the boundary condition is valid in the corresponding collocation node u_k. We assume that partialomega_kcap partial_Omega= cup_minmathcal M_k gamma_km is the union of a finite number of line (plane) segments. For interior nodes, we set mathcal M_k = emptyset . Thus, for the boundary terms in the above equation, we have","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"beginaligned\ntextboundary terms=sum_minmathcal M_k int_gamma_km vec j cdot vec n d s\n approx sum_minmathcal M_k gamma_km vec j cdot vec n\n approxsum_minmathcal M_k gamma_km (au_k -b)\nendaligned","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"We observe that for varepsilonto 0, the Robin boundary condition ","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"- vec j cdot vec n + frac1varepsilonu = frac1varepsilong","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"tends to the Dirichlet bundary condition ","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":" u=g","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"Therefore, a Dirichlet boundary condition can be approximated by choosing a small value of varepsilon and implying the aforementioned Robin boundary conditions. This approach called penalty method is chosen for the implementation of Dirichlet boundary conditions in this package.","category":"page"},{"location":"method/#Time-dependent-problems,-reaction-terms","page":"The Voronoi finite volume method","title":"Time dependent problems, reaction terms","text":"","category":"section"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"This approach easily generalizes to time dependent nonlinear transport-reaction problems with storage terms s(u), reaction terms r(u) and source terms f:","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"partial_t s(u) + nabla cdot vec j + r(u) -f =0","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"Semidiscretization in time (for implicit Euler) leads to ","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"fracs(u)-s(u^flat)tau + nabla cdot vec j + r(u) -f =0","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"where tau is the time step size and u^flat is the solution from the old timestep. The approximation approach then for each control volume gives","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"omega_kfracs(u_k)-s(u_k^flat)tau + sum_lin N_k fracsigma_klh_klg(u_k u_l)+ sum_minmathcal M_k gamma_km (au_k -b) + omega_k (r(u_k)- f_k)=0","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"If n is the number of discretization nodes, we get a system of n equations with n unknowns which under proper conditions on rgs has a unique solution. ","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"The implicit Euler method is the default solver for time dependent problems. Alternatively, ODE and DAE solvers from DifferentialEquations.jl can be used with an upcoming glue package.","category":"page"},{"location":"method/#Generalizations-to-systems","page":"The Voronoi finite volume method","title":"Generalizations to systems","text":"","category":"section"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"This approach generalizes to systems of partial differential equations, which formally can be written in the same way, but assuming that u is a vector function of vec xt, and rgs are vector functions of their arguments. The package allows to handle different sets of species in different subdomains of Omega.","category":"page"},{"location":"method/#Boundary-reactions,-boundary-species","page":"The Voronoi finite volume method","title":"Boundary reactions, boundary species","text":"","category":"section"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"In addition to rgs, the package allows to specify additional boundary species, boundary reaction, boundary flux and boundary storage terms.","category":"page"},{"location":"method/#Why-this-method-?","page":"The Voronoi finite volume method","title":"Why this method ?","text":"","category":"section"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"Independent of space dimension, the method (with properly chosen flux functions) is able to preserve a number of physical quantities if they are present on the continuous level:","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"local and global mass conservation\npositivity of solutions\nmaximum principle: in the absence of source and reaction terms, local extrema of the stationary solution are located at the domain boundaries, never in the interior. For transient problems, local extrema in the interior can only come from the initial value. \nConsistency to thermodynamics: entropy production etc.","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"Many of these properties are hard to prove for finite element methods, in particular for the convection-diffusion case.","category":"page"},{"location":"method/#Where-is-this-method-not-appropriate-?","page":"The Voronoi finite volume method","title":"Where is this method not appropriate ?","text":"","category":"section"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"There are a number of cases where this method needs to be replaced by something else or at least to be applied with great care:","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"Anisotropic diffusion only works with proper mesh alignment \nStrongly varying capacity (in the function s) at domain interfaces lead to inexact breakthrough curves\nSharp moving convection fronts are smeared out too strongly","category":"page"},{"location":"method/#History-and-literature","page":"The Voronoi finite volume method","title":"History and literature","text":"","category":"section"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"The following list is work in progress and incomplete, but it references some sources behind the ideas in this package.","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"Macneal, R. H. (1953). An asymmetrical finite difference network. Quarterly of Applied Mathematics, 11(3), 295-310. (pdf via JSTOR). Perhaps this is the earliest mentioning of the method. Note that it was used on an electrical analog computer. \nGärtner, K., & Kamenski, L. (2019). Why do we need Voronoi cells and Delaunay meshes? arXiv preprint arXiv:1905.01738. A recent overview on the merits of the method. One of the authors belongs to the pioneers of its application in 3D.\nFuhrmann, J., & Langmach, H. (2001). Stability and existence of solutions of time-implicit finite volume schemes for viscous nonlinear conservation laws. Applied Numerical Mathematics, 37(1-2), 201-230. A discussion of the method applied to rather general nonlinear scalar problems.\nSi, H., Gärtner, K., & Fuhrmann, J. (2010). Boundary conforming Delaunay mesh generation. Computational Mathematics and Mathematical Physics, 50(1), 38-53. Definition of the boundary conforming Delaunay property. \nEymard, R., Fuhrmann, J., & Gärtner, K. (2006). A finite volume scheme for nonlinear parabolic equations derived from one-dimensional local Dirichlet problems. Numerische Mathematik, 102(3), 463-495. General concept of the derivation of upwind fluxes for nonlinear problems.\nFarrell, P., Rotundo, N., Doan, D. H., Kantner, M., Fuhrmann, J., & Koprucki, T. (2017). Drift-diffusion models. In Handbook of Optoelectronic Device Modeling and Simulation (pp. 733-772). CRC Press. Overview and introduction to the method applied to semiconductor device simulation. This problem class profits most from the desirable properties of the method.","category":"page"},{"location":"method/#Software-API-and-implementation","page":"The Voronoi finite volume method","title":"Software API and implementation","text":"","category":"section"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"The entities describing the discrete system can be subdivided into two categories:","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"Geometrical data: omega_k gamma_k sigma_kl h_kl together with the connectivity information simplex grid. These data are calculated from the discretization grid.\nPhysics data: the number of species and the functions sgrf etc. describing the particular problem.","category":"page"},{"location":"method/","page":"The Voronoi finite volume method","title":"The Voronoi finite volume method","text":"The solution of the nonlinear systems of equations is performed by Newton's method combined with various direct and iterative linear solvers. The Jacobi matrices used in Newton's method are assembled from the constitutive functions with the help of forward mode automatic differentiation implemented in ForwardDiff.jl.","category":"page"},{"location":"grid/#Grid","page":"Grid","title":"Grid","text":"","category":"section"},{"location":"grid/#Types-and-Constants","page":"Grid","title":"Types and Constants","text":"","category":"section"},{"location":"grid/","page":"Grid","title":"Grid","text":"Modules = [VoronoiFVM]\nPages = [ \n \"grid/file.jl\",\n \"grid/grid_interface.jl\",\n \"grid/grid.jl\",\n \"grid/regionedit.jl\",\n \"grid/subgrid.jl\",\n \"grid/tensor.jl\",\n \"grid/generate.jl\",\n \"grid/tokenstream.jl\",\n]\nOrder = [:type]","category":"page"},{"location":"grid/","page":"Grid","title":"Grid","text":"Modules = [VoronoiFVM]\nPages = [ \n \"grid/file.jl\",\n \"grid/grid_interface.jl\",\n \"grid/grid.jl\",\n \"grid/regionedit.jl\",\n \"grid/subgrid.jl\",\n \"grid/tensor.jl\",\n \"grid/generate.jl\",\n \"grid/tokenstream.jl\",\n]\nOrder = [:constant]","category":"page"},{"location":"grid/#Methods","page":"Grid","title":"Methods","text":"","category":"section"},{"location":"grid/","page":"Grid","title":"Grid","text":"Modules = [VoronoiFVM]\nPages = [ \n \"grid/file.jl\",\n \"grid/grid_interface.jl\",\n \"grid/grid.jl\",\n \"grid/regionedit.jl\",\n \"grid/subgrid.jl\",\n \"grid/tensor.jl\",\n \"grid/generate.jl\",\n \"grid/tokenstream.jl\",\n]\nOrder = [:function]","category":"page"},{"location":"module_examples/Example110_ReactionDiffusion1D_TwoSpecies/#110:-1D-Reaction-Diffusion-equation-with-two-species","page":"110: 1D Reaction Diffusion equation with two species","title":"110: 1D Reaction Diffusion equation with two species","text":"","category":"section"},{"location":"module_examples/Example110_ReactionDiffusion1D_TwoSpecies/","page":"110: 1D Reaction Diffusion equation with two species","title":"110: 1D Reaction Diffusion equation with two species","text":"(source code)","category":"page"},{"location":"module_examples/Example110_ReactionDiffusion1D_TwoSpecies/","page":"110: 1D Reaction Diffusion equation with two species","title":"110: 1D Reaction Diffusion equation with two species","text":"Solve the nonlinear coupled reaction diffusion problem","category":"page"},{"location":"module_examples/Example110_ReactionDiffusion1D_TwoSpecies/","page":"110: 1D Reaction Diffusion equation with two species","title":"110: 1D Reaction Diffusion equation with two species","text":"-nabla (001+2u_2)nabla u_1 + u_1u_2= 00001(001+x)","category":"page"},{"location":"module_examples/Example110_ReactionDiffusion1D_TwoSpecies/","page":"110: 1D Reaction Diffusion equation with two species","title":"110: 1D Reaction Diffusion equation with two species","text":"-nabla (001+2u_1)nabla u_2 - u_1u_2 = 00001(101-x)","category":"page"},{"location":"module_examples/Example110_ReactionDiffusion1D_TwoSpecies/","page":"110: 1D Reaction Diffusion equation with two species","title":"110: 1D Reaction Diffusion equation with two species","text":"in Omega=(01) with boundary condition u_1(0)=1, u_2(0)=0 and u_1(1)=1, u_2(1)=1.","category":"page"},{"location":"module_examples/Example110_ReactionDiffusion1D_TwoSpecies/","page":"110: 1D Reaction Diffusion equation with two species","title":"110: 1D Reaction Diffusion equation with two species","text":"module Example110_ReactionDiffusion1D_TwoSpecies\n\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\n\nfunction main(; n = 100, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise)\n h = 1 / n\n grid = VoronoiFVM.Grid(collect(0:h:1))\n\n eps::Vector{Float64} = [1.0, 1.0]\n\n physics = VoronoiFVM.Physics(\n ; reaction = function (f, u, node)\n f[1] = u[1] * u[2]\n f[2] = -u[1] * u[2]\n end,\n flux = function (f, u, edge)\n nspecies = 2\n f[1] = eps[1] * (u[1, 1] - u[1, 2]) *\n (0.01 + u[2, 1] + u[2, 2])\n f[2] = eps[2] * (u[2, 1] - u[2, 2]) *\n (0.01 + u[1, 1] + u[1, 2])\n end, source = function (f, node)\n f[1] = 1.0e-4 * (0.01 + node[1])\n f[2] = 1.0e-4 * (0.01 + 1.0 - node[1])\n end, storage = function (f, u, node)\n f[1] = u[1]\n f[2] = u[2]\n end)\n\n sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage)\n\n enable_species!(sys, 1, [1])\n enable_species!(sys, 2, [1])\n\n boundary_dirichlet!(sys, 1, 1, 1.0)\n boundary_dirichlet!(sys, 1, 2, 0.0)\n\n boundary_dirichlet!(sys, 2, 1, 1.0)\n boundary_dirichlet!(sys, 2, 2, 0.0)\n\n U = unknowns(sys)\n U .= 0\n\n control = VoronoiFVM.NewtonControl()\n control.verbose = verbose\n control.damp_initial = 0.1\n u5 = 0\n p = GridVisualizer(; Plotter = Plotter, layout = (2, 1))\n for xeps in [1.0, 0.5, 0.25, 0.1, 0.05, 0.025, 0.01]\n eps = [xeps, xeps]\n U = solve(sys; inival = U, control)\n scalarplot!(p[1, 1], grid, U[1, :]; clear = true, title = \"U1, eps=$(xeps)\")\n scalarplot!(p[2, 1], grid, U[2, :]; clear = true, title = \"U2, eps=$(xeps)\",\n reveal = true)\n sleep(0.2)\n u5 = U[5]\n end\n return u5\nend\n\nusing Test\nfunction runtests()\n testval = 0.7117546972922056\n\n @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval\nend\nend","category":"page"},{"location":"module_examples/Example110_ReactionDiffusion1D_TwoSpecies/","page":"110: 1D Reaction Diffusion equation with two species","title":"110: 1D Reaction Diffusion equation with two species","text":"","category":"page"},{"location":"module_examples/Example110_ReactionDiffusion1D_TwoSpecies/","page":"110: 1D Reaction Diffusion equation with two species","title":"110: 1D Reaction Diffusion equation with two species","text":"This page was generated using Literate.jl.","category":"page"},{"location":"plutostatichtml_examples/problemcase/","page":"A case for caution","title":"A case for caution","text":"\n\n\n\n\n

    Some problems with Voronoi FVM

    Source

    Draft. J. Fuhrmann, Oct. 29. 2021. Updated Dec 19, 2021.

    We discuss one of the critical cases for application the Voronoi finite volume method. We provide some practical fix and opine that the finite element method probably has the same problems.

    \n\n\n\n\n
    begin\n    import Pkg as _Pkg\n    haskey(ENV, \"PLUTO_PROJECT\") && _Pkg.activate(ENV[\"PLUTO_PROJECT\"])\n    using Revise\n    using Test\n    using VoronoiFVM\n    using ExtendableGrids\n    using PlutoUI\n    using GridVisualize\n    using CairoMakie\n    CairoMakie.activate!(; type = \"svg\", visible = false)\n    GridVisualize.default_plotter!(CairoMakie)\nend;
    \n\n\n","category":"page"},{"location":"plutostatichtml_examples/problemcase/#Transient-problem","page":"A case for caution","title":"Transient problem","text":"","category":"section"},{"location":"plutostatichtml_examples/problemcase/","page":"A case for caution","title":"A case for caution","text":"
    \n

    This problem was suggested by R. Eymard.

    \n\n\n

    Regard the following problem coupling Darcy's equation with Fick's law and transport:

    \n\n\n

    $$ \\begin{aligned}\n \\vec v &= k \\nabla p \\\\\n \\nabla \\cdot \\vec v &= 0\\\\\n \\partial_t (\\phi c) - \\nabla \\cdot (D\\nabla c + c \\vec v) &= 0\n \\end{aligned}$$

    \n\n\n

    The domain is described by the following discretization grid:

    \n\n\n\n\n\n

    In the center of the domain, we assume a layer with high permeability.

    As boundary conditions for the pressure $p$ we choose fixed pressure values at the left and right boundaries of the domain, triggering a constant pressure gradient throughout the domain.

    At the inlet of the high permeability layer, we set $c=1$, and at the outlet, we set $c=0$.

    The high permeability layer has length L=10 and width W= 0.5.

    We solve the time dependent problem on three types of rectangular grids with the same resolution in $x$ direction and different variants to to handle the high permeability layer.

    • grid_n - a \"naive\" grid which just resolves the permeability layer and the surrounding material with equally spaced (in y direction) grids

    • grid_1 - a 1D grid of the high permeability layer. With high permeability contrast, the solution of the 2D case at y=0 should coincide with the 1D solution

    • grid_f - a \"fixed\" 2D grid which resolves the permeability layer and the surrounding material with equally spaced (in y direction) grids and \"protection layers\" of width ε_fix=0.0001 correcting the size of high permeability control volumes

    \n\n\n\n\n\n

    Results

    In the calculations, we ramp up the inlet concentration and measure the amount of solute flowing through the outlet - the breaktrough curve.

    \n\n
    nref = 1
    \n
    1
    \n\n
    tend = 100
    \n
    100
    \n\n
    ε_fix = 1.0e-4
    \n
    0.0001
    \n\n
    grid_n, sol_n, bt_n = trsolve(grid_2d(; nref = nref); tend = tend);
    \n\n\n
    sum(bt_n)
    \n
    18.143158169851787
    \n\n
    @test sum(bt_n) ≈ 18.143158169851787
    \n
    Test Passed
    \n\n
    grid_1, sol_1, bt_1 = trsolve(grid_1d(; nref = nref); tend = tend);
    \n\n\n
    @test sum(bt_1) ≈ 20.66209910195916
    \n
    Test Passed
    \n\n
    grid_f, sol_f, bt_f = trsolve(grid_2d(; nref = nref, ε_fix = ε_fix); tend = tend);
    \n\n\n
    @test sum(bt_f) ≈ 20.661131375044135
    \n
    Test Passed
    \n\n
    grid_ϕ, sol_ϕ, bt_ϕ = trsolve(grid_2d(; nref = nref); ϕ = [1.0e-3, 1], tend = tend);
    \n\n\n
    @test sum(bt_ϕ) ≈ 20.412256299447236
    \n
    Test Passed
    \n\n
    begin\n    p1 = GridVisualizer(; resolution = (500, 200),\n                        xlabel = \"t\",\n                        ylabel = \"outflow\",\n                        legend = :rb,\n                        title = \"Breakthrough Curves\")\n    scalarplot!(p1, sol_n.t, bt_n; label = \"naive grid\", color = :red)\n    scalarplot!(p1,\n                sol_1.t,\n                bt_1;\n                label = \"1D grid\",\n                markershape = :x,\n                markersize = 10,\n                clear = false,\n                color = :green)\n    scalarplot!(p1,\n                sol_f.t,\n                bt_f;\n                label = \"grid with fix\",\n                markershape = :circle,\n                color = :green,\n                clear = false)\n    scalarplot!(p1,\n                sol_ϕ.t,\n                bt_ϕ;\n                label = \"modified ϕ\",\n                markershape = :cross,\n                color = :blue,\n                clear = false)\n    reveal(p1)\nend
    \n\n\n\n

    Here, we plot the solutions for the grid_n case and the grid_f case.

    \n\n\n\n\n\n

    Time: 10.0

    \n\n
    scalarplot(grid_n, sol_n(t)[ic, :]; resolution = (500, 200), show = true)
    \n\n\n
    scalarplot(grid_f, sol_f(t)[ic, :]; resolution = (500, 200), show = true)
    \n\n\n","category":"page"},{"location":"plutostatichtml_examples/problemcase/#Reaction-Diffusion-problem","page":"A case for caution","title":"Reaction-Diffusion problem","text":"","category":"section"},{"location":"plutostatichtml_examples/problemcase/","page":"A case for caution","title":"A case for caution","text":"
    \n

    Here we solve the following problem:

    $$ -\\nabla D \\nabla u + R u = 0$$

    where D is large in the high permeability region and small otherwise. R is a constant.

    \n\n\n

    Results

    \n\n
    rdgrid_1, rdsol_1, of_1 = rdsolve(grid_1d(; nref = nref));
    \n\n\n
    @test of_1 ≈ -0.013495959676585267
    \n
    Test Passed
    \n\n
    rdgrid_n, rdsol_n, of_n = rdsolve(grid_2d(; nref = nref));
    \n\n\n
    @test of_n ≈ -0.00023622450350365264
    \n
    Test Passed
    \n\n
    rdgrid_f, rdsol_f, of_f = rdsolve(grid_2d(; nref = nref, ε_fix = ε_fix));
    \n\n\n
    @test of_f ≈ -0.013466874615165499
    \n
    Test Passed
    \n\n
    rdgrid_r, rdsol_r, of_r = rdsolve(grid_2d(; nref = nref); R = [0, 0.1]);
    \n\n\n
    @test of_r ≈ -0.013495959676764535
    \n
    Test Passed
    \n\n\n

    We measure the outflow at the outlet. As a result, we obtain:

    • 1D case: -0.013495959676585255

    • 2D case, naive grid: -0.00023622450350365272

    • 2D case, grid with \"protective layer\": -0.013466874615165514

    • 2D case, naive grid, \"modified\" R: -0.013495959676764539

    \n\n
    scalarplot(rdgrid_1, rdsol_1; resolution = (300, 200))
    \n\n\n
    scalarplot(rdgrid_n, rdsol_n; resolution = (500, 200))
    \n\n\n
    scalarplot(rdgrid_f, rdsol_f; resolution = (500, 200))
    \n\n\n","category":"page"},{"location":"plutostatichtml_examples/problemcase/#Discussion","page":"A case for caution","title":"Discussion","text":"","category":"section"},{"location":"plutostatichtml_examples/problemcase/","page":"A case for caution","title":"A case for caution","text":"
    \n

    Transient case

    As there will be nearly no flow in y-direction, we should get the very same results in all four cases for small permeability values in the low permeability region.

    In the grid_n case, the heterogeneous control volumina ovrestimate the storage capacity which shows itself in a underestimation of the transferred solute.

    With the high permeability contrast, the results for heterogeneous domain should be essentially equal to those for 1D domain. However, with a coarse resolution in y-direction, we see large differences in the transient behaviour of the breaktrough curve compared to the 1D case. The introduction of a thin protection layer leads to reasonable results.

    Alternatively, the porosity of the low permeability region can be modified. Arguably, this is the case in practice, see e.g. Ackerer et al, Transport in Porous Media35:345–373, 1999 (eq. 7).

    Reaction diffusion case

    In this case, we look at a homogeneous reaction in a domain divided into a high and low diffusion region. With high contrast in the diffusion coefficients, the reasonable assumption is that the reaction takes place only in the high diffusion region, and the un-consumed share of species leaves at the outlet.

    In this case we observe a similar related problem which can be fixed by adding a thin layer of control volumes at the boundary. No problem occurs if the reaction rate at in the low diffusion region is zero.

    Conclusion

    Here, we indeed observe problem with the Voronoi approach: care must be taken to handle the case of hetero interfaces in connection with transient processes and/or homogeneous reactions. In these cases it should be analyzed if the problem occurs, and why, and it appears, that the discussion should not be had without reference to the correct physical models. A remedy based on meshing is available at least for straight interfaces.

    Opinion

    With standard ways of using finite elements, the issue described here will occur in a similar way, so the discussion is indeed with the alternative \"cell centered\" finite volume approach which places interfaces at the boundaries of the control volumes rather than along the edges of a underlying triangulation.

    Drawbacks of two point flux Voronoi methods based on simplicial meshes (as tested here):

    • Anisotropic diffusion is only correct with aligned meshes

    • Reliance on boundary conforming Delaunay property of the underlying mesh, thus narrowing the available meshing strategies

    • The issue described in the present notebook. However, in both cases discussed here, IMHO it might \"go away\" depending on the correct physics. There should be more discussions with relevant application problems at hand.

    Advantages (compared to the cell centered approach placing collocation points away from interfaces)

    • Availability of P1 interpolant on simplices for visualization, interpolation, coupling etc.

    • Mesh generators tend to place interfaces at triangle edges.

    • Dirichlet BC can be applied exactly

    • There is a straightforward way to link interface processes with bulk processes, e.g. an adsorption reaction is easily described by a reaction term at the boundary which involves interface and bulk value available at the same mesh node.

    \n\n","category":"page"},{"location":"plutostatichtml_examples/problemcase/#Appendix","page":"A case for caution","title":"Appendix","text":"","category":"section"},{"location":"plutostatichtml_examples/problemcase/","page":"A case for caution","title":"A case for caution","text":"
    \n
    \n\n\n

    Domain data

    \n\n\n

    Sizes:

    \n\n
    begin\n    L = 10   # length of the high perm layer\n    W = 0.5  # width of high perm layer\n    Wlow = 2 # width of adjacent low perm layers\nend;
    \n\n\n\n

    Boundary conditions:

    \n\n
    begin\n    const Γ_top = 3\n    const Γ_bot = 1\n    const Γ_left = 4\n    const Γ_right = 2\n    const Γ_in = 5\n    const Γ_out = 2\nend;
    \n\n\n
    begin\n    Ω_low = 1\n    Ω_high = 2\nend;
    \n\n\n
    function grid_2d(; nref = 0, ε_fix = 0.0)\n    nx = 10 * 2^nref\n    ny = 1 * 2^nref\n    nylow = 3 * 2^nref\n    xc = linspace(0, L, nx + 1)\n    y0 = linspace(-W / 2, W / 2, ny + 1)\n    if ε_fix > 0.0\n        yfix = [W / 2, W / 2 + ε_fix]\n        ytop = glue(yfix, linspace(yfix[end], Wlow, nylow + 1))\n    else\n        ytop = linspace(W / 2, Wlow, nylow + 1)\n    end\n    yc = glue(-reverse(ytop), glue(y0, ytop))\n    grid = simplexgrid(xc, yc)\n    cellmask!(grid, [0, -W / 2], [L, W / 2], Ω_high)\n    bfacemask!(grid, [0, -W / 2], [0, W / 2], Γ_in)\n    bfacemask!(grid, [L, -W / 2], [L, W / 2], Γ_out)\nend
    \n
    grid_2d (generic function with 1 method)
    \n\n
    function grid_1d(; nref = 0)\n    nx = 10 * 2^nref\n    xc = linspace(0, L, nx + 1)\n    grid = simplexgrid(xc)\n    cellmask!(grid, [0], [L], Ω_high)\n    bfacemask!(grid, [0], [0], Γ_in)\n    bfacemask!(grid, [L], [L], Γ_out)\n    grid\nend
    \n
    grid_1d (generic function with 1 method)
    \n\n\n

    Transient solver

    \n\n\n

    Pressure index in solution

    \n\n
    const ip = 1;
    \n\n\n\n

    Concentration index in solution

    \n\n
    const ic = 2;
    \n\n\n\n

    Generate breaktrough courve from transient solution

    \n\n
    function breakthrough(sys, tf, sol)\n    of = similar(sol.t)\n    t = sol.t\n    of[1] = 0\n    for i = 2:length(sol.t)\n        of[i] = -integrate(sys, tf, sol[i], sol[i - 1], t[i] - t[i - 1])[ic]\n    end\n    of\nend
    \n
    breakthrough (generic function with 1 method)
    \n\n\n

    Transient solver:

    \n\n
    function trsolve(grid;\n                 κ = [1.0e-3, 5],\n                 D = [1.0e-12, 1.0e-12],\n                 Δp = 1.0,\n                 ϕ = [1, 1],\n                 tend = 100,)\n    function flux(y, u, edge)\n        y[ip] = κ[edge.region] * (u[ip, 1] - u[ip, 2])\n        bp, bm = fbernoulli_pm(y[ip] / D[edge.region])\n        y[ic] = D[edge.region] * (bm * u[ic, 1] - bp * u[ic, 2])\n    end\n\n    function stor(y, u, node)\n        y[ip] = 0\n        y[ic] = ϕ[node.region] * u[ic]\n    end\n\n    dim = dim_space(grid)\n    function bc(y, u, bnode)\n        c0 = ramp(bnode.time; dt = (0, 0.001), du = (0, 1))\n        boundary_dirichlet!(y, u, bnode, ic, Γ_in, c0)\n        boundary_dirichlet!(y, u, bnode, ic, Γ_out, 0)\n\n        boundary_dirichlet!(y, u, bnode, ip, Γ_in, Δp)\n        boundary_dirichlet!(y, u, bnode, ip, Γ_out, 0)\n        if dim > 1\n            boundary_dirichlet!(y, u, bnode, ip, Γ_left, Δp)\n            boundary_dirichlet!(y, u, bnode, ip, Γ_right, 0)\n        end\n    end\n\n    sys = VoronoiFVM.System(grid;\n                            check_allocs = true,\n                            flux = flux,\n                            storage = stor,\n                            bcondition = bc,\n                            species = [ip, ic],)\n\n    inival = solve(sys; inival = 0, time = 0.0)\n    factory = TestFunctionFactory(sys)\n    tfc = testfunction(factory, [Γ_in, Γ_left, Γ_top, Γ_bot], [Γ_out])\n\n    sol = VoronoiFVM.solve(sys;\n                           inival = inival,\n                           times = [0, tend],\n                           Δt = 1.0e-4,\n                           Δt_min = 1.0e-6,)\n\n    bt = breakthrough(sys, tfc, sol)\n    if dim == 1\n        bt = bt * W\n    end\n\n    grid, sol, bt\nend
    \n
    trsolve (generic function with 1 method)
    \n\n\n

    Reaction-Diffusion solver

    \n\n
    function rdsolve(grid; D = [1.0e-12, 1.0], R = [1, 0.1])\n    function flux(y, u, edge)\n        y[1] = D[edge.region] * (u[1, 1] - u[1, 2])\n    end\n\n    function rea(y, u, node)\n        y[1] = R[node.region] * u[1]\n    end\n    function bc(args...)\n        boundary_dirichlet!(args..., 1, Γ_in, 1)\n        boundary_dirichlet!(args..., 1, Γ_out, 0)\n    end\n    sys = VoronoiFVM.System(grid;\n                            flux = flux,\n                            reaction = rea,\n                            species = 1,\n                            bcondition = bc,\n                            check_allocs = true)\n    dim = dim_space(grid)\n\n    sol = VoronoiFVM.solve(sys)\n    factory = TestFunctionFactory(sys)\n    tf = testfunction(factory, [Γ_in, Γ_left, Γ_top, Γ_bot], [Γ_out])\n    of = integrate(sys, tf, sol)\n    fac = 1.0\n    if dim == 1\n        fac = W\n    end\n    grid, sol[1, :], of[1] * fac\nend
    \n
    rdsolve (generic function with 1 method)
    \n\n\n
    \n\n\n
    \n
    \n

    Built with Julia 1.10.2 and

    \nCairoMakie 0.11.5
    \nExtendableGrids 1.2.3
    \nGridVisualize 1.5.0
    \nPkg 1.10.0
    \nPlutoUI 0.7.55
    \nRevise 3.5.13
    \nVoronoiFVM 1.16.0\n
    \n\n","category":"page"},{"location":"plutostatichtml_examples/problemcase/","page":"A case for caution","title":"A case for caution","text":"EditURL = \"https://github.com/j-fu/VoronoiFVM.jl/blob/main/nothing\"","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Markdown\nMarkdown.parse(\"\"\"\n$(read(\"../../README.md\",String))\n\"\"\")","category":"page"},{"location":"#Papers-and-preprints-using-this-package","page":"Home","title":"Papers and preprints using this package","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Please consider a pull request if you have published work which could be added to this list.","category":"page"},{"location":"","page":"Home","title":"Home","text":"S. Matera, C. Merdon and D. Runge. Reduced Basis Approach for Convection-Diffusion Equations with Non-linear Boundary Reaction Conditions. In: International Conference on Finite Volumes for Complex Applications (Springer, 2023); pp. 335–343.\n\n\n\nB. Spetzler, D. Abdel, F. Schwierz, M. Ziegler and P. Farrell. The Role of Vacancy Dynamics in Two-Dimensional Memristive Devices. Advanced Electronic Materials, 2300635 (2023).\n\n\n\nS. Scholz and L. Berger. Hestia.jl: A Julia Library for Heat Conduction Modeling with Boundary Actuation. Simul. Notes Eur. 33, 27–30 (2023).\n\n\n\nR. P. Schärer and J. Schumacher. A Transient Non-isothermal Cell Performance Model for Organic Redox Flow Batteries. In: 19th Symposium on Modeling and Experimental Validation of Electrochemical Energy Technologies (ModVal), Duisburg, Germany, 21-23 March 2023 (2023).\n\n\n\nJ. Fuhrmann, B. Gaudeul and C. Keller. Two Entropic Finite Volume Schemes for a Nernst–Planck–Poisson System with Ion Volume Constraints. In: International Conference on Finite Volumes for Complex Applications (Springer, 2023); pp. 285–294.\n\n\n\nP. Vágner, M. Pavelka, J. Fuhrmann and V. Klika. A multiscale thermodynamic generalization of Maxwell-Stefan diffusion equations and of the dusty gas model. International Journal of Heat and Mass Transfer 199, 123405 (2022).\n\n\n\nV. Miloš, P. Vágner, D. Budáč, M. Carda, M. Paidar, J. Fuhrmann and K. Bouzek. Generalized Poisson-Nernst-Planck-based physical model of an O2 | LSM | YSZ electrode. Journal of the Electrochemical Society, 044505 (2022).\n\n\n\nL. Xiao, G. Mei, N. Xi and F. Piccialli. Julia language in computational mechanics: A new competitor. Archives of Computational Methods in Engineering 29, 1713–1726 (2022).\n\n\n\nB. Gaudeul and J. Fuhrmann. Entropy and convergence analysis for two finite volume schemes for a Nernst–Planck–Poisson system with ion volume constraints. Numerische Mathematik 151, 99–149 (2022).\n\n\n\nJ. R. Martins, F. Alves and P. M. Ferreira. From Semiconductor to Transistor-Level: Modeling, Simulation, and Layout Rendering Tools. In: Colloque du GdR SOC2 (2022), hal-03690082.\n\n\n\nJ. Jambrich. Consistent non-equilibrium thermodynamic modeling of hydrogen fuel cells. Master's thesis, Univerzita Karlova, Matematicko-fyzikálnı́ fakulta (2022).\n\n\n\nS. B. Chinnery. TCAD-Informed Surrogate Models of Semiconductor Devices. Master's thesis, Massachusetts Institute of Technology (2022).\n\n\n\nD. Abdel, P. Vágner, J. Fuhrmann and P. Farrell. Modelling charge transport in perovskite solar cells: Potential-based and limiting ion depletion. Electrochimica Acta 390, 138696 (2021).\n\n\n\nD. Abdel, P. Farrell and J. Fuhrmann. Assessing the quality of the excess chemical potential flux scheme for degenerate semiconductor device simulation. Optical and Quantum Electronics 53, 1–10 (2021).\n\n\n\nC. Cancès, C. Chainais-Hillairet, J. Fuhrmann and B. Gaudeul. A numerical-analysis-focused comparison of several finite volume schemes for a unipolar degenerate drift-diffusion model. IMA Journal of Numerical Analysis 41, 271–314 (2021).\n\n\n\nJ. Park, J. H. Cho and R. D. Braatz. Mathematical modeling and analysis of microwave-assisted freeze-drying in biopharmaceutical applications. Computers & Chemical Engineering 153, 107412 (2021).\n\n\n\nC. Cancès, C. C. Hillairet, J. Fuhrmann and B. Gaudeul. On four numerical schemes for a unipolar degenerate drift-diffusion model. In: Finite Volumes for Complex Applications IX-Methods, Theoretical Aspects, Examples: FVCA 9, Bergen, Norway, June 2020 IX (Springer, 2020); pp. 163–171.\n\n\n\n","category":"page"},{"location":"module_examples/Example204_HagenPoiseuille/#204:-2D-Convection-in-Hagen-Poiseuille-flow","page":"204: 2D Convection in Hagen-Poiseuille flow","title":"204: 2D Convection in Hagen-Poiseuille flow","text":"","category":"section"},{"location":"module_examples/Example204_HagenPoiseuille/","page":"204: 2D Convection in Hagen-Poiseuille flow","title":"204: 2D Convection in Hagen-Poiseuille flow","text":"(source code)","category":"page"},{"location":"module_examples/Example204_HagenPoiseuille/","page":"204: 2D Convection in Hagen-Poiseuille flow","title":"204: 2D Convection in Hagen-Poiseuille flow","text":"Solve the equation","category":"page"},{"location":"module_examples/Example204_HagenPoiseuille/","page":"204: 2D Convection in Hagen-Poiseuille flow","title":"204: 2D Convection in Hagen-Poiseuille flow","text":"partial_t u -nabla ( D nabla u - v u) = 0","category":"page"},{"location":"module_examples/Example204_HagenPoiseuille/","page":"204: 2D Convection in Hagen-Poiseuille flow","title":"204: 2D Convection in Hagen-Poiseuille flow","text":"in Omega=(0L)times (0H) with dirichlet boundary conditions at x=0 and outflow boundary condition at x=L.","category":"page"},{"location":"module_examples/Example204_HagenPoiseuille/","page":"204: 2D Convection in Hagen-Poiseuille flow","title":"204: 2D Convection in Hagen-Poiseuille flow","text":"module Example204_HagenPoiseuille\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\n\nfunction main(; nref = 0, Plotter = nothing, D = 0.01, v = 1.0, tend = 100, cin = 1.0, assembly = :edgewise)\n H = 1.0\n L = 5.0\n grid = simplexgrid(range(0, L; length = 20 * 2^nref),\n range(0, H; length = 5 * 2^nref))\n\n function fhp(x, y)\n yh = y / H\n return v * 4 * yh * (1.0 - yh), 0\n end\n\n evelo = edgevelocities(grid, fhp)\n bfvelo = bfacevelocities(grid, fhp)\n\n function flux!(f, u, edge)\n vd = evelo[edge.index] / D\n bp = fbernoulli(vd)\n bm = fbernoulli(-vd)\n f[1] = D * (bp * u[1] - bm * u[2])\n end\n\n function outflow!(f, u, node)\n if node.region == 2\n f[1] = bfvelo[node.ibnode, node.ibface] * u[1]\n end\n end\n\n ispec = 1\n physics = VoronoiFVM.Physics(; flux = flux!, breaction = outflow!)\n sys = VoronoiFVM.System(grid, physics; assembly = assembly)\n enable_species!(sys, ispec, [1])\n\n boundary_dirichlet!(sys, ispec, 4, cin)\n\n # Transient solution of the problem\n control = VoronoiFVM.NewtonControl()\n control.Δt = 0.01 * 2.0^(-nref)\n control.Δt_min = 0.01 * 2.0^(-nref)\n control.Δt_max = 0.1 * tend\n control.force_first_step = true\n tsol = solve(sys; inival = 0, times = [0, tend], control = control)\n\n vis = GridVisualizer(; Plotter = Plotter)\n for i = 1:length(tsol.t)\n scalarplot!(vis[1, 1], grid, tsol[1, :, i]; flimits = (0, cin + 1.0e-5),\n title = @sprintf(\"time=%3f\", tsol.t[i]), show = true)\n end\n tsol\nend\n\nusing Test\nfunction runtests()\n tsol1 = main(; assembly = :edgewise)\n tsol2 = main(; assembly = :cellwise)\n @test all(tsol1[end] .≈ 1) &&\n all(tsol1[end] .≈ 1)\nend\n\nend","category":"page"},{"location":"module_examples/Example204_HagenPoiseuille/","page":"204: 2D Convection in Hagen-Poiseuille flow","title":"204: 2D Convection in Hagen-Poiseuille flow","text":"","category":"page"},{"location":"module_examples/Example204_HagenPoiseuille/","page":"204: 2D Convection in Hagen-Poiseuille flow","title":"204: 2D Convection in Hagen-Poiseuille flow","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example108_OrdinaryDiffEq1D/#108:-1D-Nonlinear-Diffusion-equation-with-ODE","page":"108: 1D Nonlinear Diffusion equation with ODE","title":"108: 1D Nonlinear Diffusion equation with ODE","text":"","category":"section"},{"location":"module_examples/Example108_OrdinaryDiffEq1D/","page":"108: 1D Nonlinear Diffusion equation with ODE","title":"108: 1D Nonlinear Diffusion equation with ODE","text":"(source code)","category":"page"},{"location":"module_examples/Example108_OrdinaryDiffEq1D/","page":"108: 1D Nonlinear Diffusion equation with ODE","title":"108: 1D Nonlinear Diffusion equation with ODE","text":"Solve the nonlinear diffusion equation","category":"page"},{"location":"module_examples/Example108_OrdinaryDiffEq1D/","page":"108: 1D Nonlinear Diffusion equation with ODE","title":"108: 1D Nonlinear Diffusion equation with ODE","text":"partial_t u -Delta u^m = 0","category":"page"},{"location":"module_examples/Example108_OrdinaryDiffEq1D/","page":"108: 1D Nonlinear Diffusion equation with ODE","title":"108: 1D Nonlinear Diffusion equation with ODE","text":"in Omega=(-11) with homogeneous Neumann boundary conditions using the implicit Euler method.","category":"page"},{"location":"module_examples/Example108_OrdinaryDiffEq1D/","page":"108: 1D Nonlinear Diffusion equation with ODE","title":"108: 1D Nonlinear Diffusion equation with ODE","text":"This equation is also called \"porous medium equation\". The Barenblatt solution is an exact solution of this problem which for m>1 has a finite support. We initialize this problem with the exact solution for t=t_0=0001.","category":"page"},{"location":"module_examples/Example108_OrdinaryDiffEq1D/","page":"108: 1D Nonlinear Diffusion equation with ODE","title":"108: 1D Nonlinear Diffusion equation with ODE","text":"(see Barenblatt, G. I. \"On nonsteady motions of gas and fluid in porous medium.\" Appl. Math. and Mech.(PMM) 16.1 (1952): 67-78.)","category":"page"},{"location":"module_examples/Example108_OrdinaryDiffEq1D/","page":"108: 1D Nonlinear Diffusion equation with ODE","title":"108: 1D Nonlinear Diffusion equation with ODE","text":"module Example108_OrdinaryDiffEq1D\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\nusing OrdinaryDiffEq\n\nfunction barenblatt(x, t, m)\n tx = t^(-1.0 / (m + 1.0))\n xx = x * tx\n xx = xx * xx\n xx = 1 - xx * (m - 1) / (2.0 * m * (m + 1))\n if xx < 0.0\n xx = 0.0\n end\n return tx * xx^(1.0 / (m - 1.0))\nend\n\nfunction main(; n = 20, m = 2, Plotter = nothing, verbose = false,\n unknown_storage = :sparse, tend = 0.01, assembly = :edgewise, solver=Rosenbrock23())\n\n # Create a one-dimensional discretization\n h = 1.0 / convert(Float64, n / 2)\n X = collect(-1:h:1)\n grid = VoronoiFVM.Grid(X)\n\n # Flux function which describes the flux\n # between neighboring control volumes\n function flux!(f, u, edge)\n f[1] = u[1, 1]^m - u[1, 2]^m\n end\n\n # Storage term\n function storage!(f, u, node)\n f[1] = u[1]\n end\n\n # Create a physics structure\n physics = VoronoiFVM.Physics(; flux = flux!,\n storage = storage!)\n\n # Create a finite volume system - either\n # in the dense or the sparse version.\n # The difference is in the way the solution object\n # is stored - as dense or as sparse matrix\n sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly)\n\n # Add species 1 to region 1\n enable_species!(sys, 1, [1])\n\n # Create a solution array\n inival = unknowns(sys)\n t0 = 0.001\n\n # Broadcast the initial value\n inival[1, :] .= map(x -> barenblatt(x, t0, m), X)\n\n problem = ODEProblem(sys,inival,(t0,tend))\n odesol = solve(problem,solver)\n tsol=reshape(odesol,sys)\n\n\n p = GridVisualizer(; Plotter = Plotter, layout = (1, 1), fast = true)\n for i = 1:length(tsol)\n time = tsol.t[i]\n scalarplot!(p[1, 1], grid, tsol[1, :, i]; title = @sprintf(\"t=%.3g\", time),\n color = :red, label = \"numerical\",\n markershape = :circle, markevery = 1)\n scalarplot!(p[1, 1], grid, map(x -> barenblatt(x, time, m), grid); clear = false,\n color = :green,\n label = \"exact\", markershape = :none)\n reveal(p)\n sleep(1.0e-2)\n end\n return sum(tsol[end])\nend\n\nusing Test\nfunction runtests()\n testval = 46.66666666671521\n @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval\n @test main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval\n @test main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval\n @test main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval\nend\n\nend","category":"page"},{"location":"module_examples/Example108_OrdinaryDiffEq1D/","page":"108: 1D Nonlinear Diffusion equation with ODE","title":"108: 1D Nonlinear Diffusion equation with ODE","text":"","category":"page"},{"location":"module_examples/Example108_OrdinaryDiffEq1D/","page":"108: 1D Nonlinear Diffusion equation with ODE","title":"108: 1D Nonlinear Diffusion equation with ODE","text":"This page was generated using Literate.jl.","category":"page"},{"location":"plutostatichtml_examples/api-update/","page":"API Updates","title":"API Updates","text":"\n\n\n\n\n

    API updates

    Source

    \n\n\n

    Here we describe some updates for the API of VoronoiFVM.jl. These have been implemented mostly on top of the existing API, whose functionality is not affected.

    \n\n
    TableOfContents(; aside = false, depth = 5)
    \n\n\n
    begin\n    import Pkg as _Pkg\n    haskey(ENV, \"PLUTO_PROJECT\") && _Pkg.activate(ENV[\"PLUTO_PROJECT\"])\n    using Revise\n    using VoronoiFVM\n    using ExtendableGrids\n    using ExtendableSparse\n    using Test\n    using PlutoUI\n    using GridVisualize\n    using LinearSolve\n    using ILUZero\n    using LinearAlgebra\n    using CairoMakie\n    CairoMakie.activate!(; type = \"svg\", visible = false)\n    GridVisualize.default_plotter!(CairoMakie)\nend;
    \n\n\n","category":"page"},{"location":"plutostatichtml_examples/api-update/#v0.19","page":"API Updates","title":"v0.19","text":"","category":"section"},{"location":"plutostatichtml_examples/api-update/","page":"API Updates","title":"API Updates","text":"
    \n
    \n\n\n

    This is a breaking release. Implementations using default solver settings should continue to work (albeit possibly with deprecation and allocation warnings). Really breaking is control of iterative linear solvers and allocation checks.

    \n\n\n

    Solve now a method of CommonSolve.solve

    \n\n\n

    As a consequence, all VoronoiFVM.solve methods with signatures others than solve(system; kwargs...) are now deprecated

    \n\n
    n = 100
    \n
    100
    \n\n
    begin\n    h = 1.0 / convert(Float64, n)\n    const eps = 1.0e-2\n    function reaction(f, u, node)\n        f[1] = u[1]^2\n    end\n\n    function flux(f, u, edge)\n        f[1] = eps * (u[1, 1]^2 - u[1, 2]^2)\n    end\n\n    function source(f, node)\n        x1 = node[1] - 0.5\n        x2 = node[2] - 0.5\n        f[1] = exp(-20.0 * (x1^2 + x2^2))\n    end\n\n    function storage(f, u, node)\n        f[1] = u[1]\n    end\n\n    function bcondition(f, u, node)\n        boundary_dirichlet!(f,\n                            u,\n                            node;\n                            species = 1,\n                            region = 2,\n                            value = ramp(node.time; dt = (0, 0.1), du = (0, 1)))\n        boundary_dirichlet!(f,\n                            u,\n                            node;\n                            species = 1,\n                            region = 4,\n                            value = ramp(node.time; dt = (0, 0.1), du = (0, 1)))\n    end\n\n    sys0 = VoronoiFVM.System(0.0:h:1.0,\n                             0.0:h:1.0;\n                             reaction,\n                             flux,\n                             source,\n                             storage,\n                             bcondition,\n                             species = [1],)\nend
    \n
    VoronoiFVM.System{Float64, Float64, Int32, Int64, Matrix{Int32}, Matrix{Float64}}(num_species=1)\n
    \n\n\n

    Deprecated call:

    \n\n
    begin\n    inival = unknowns(sys0; inival = 0.1)\n    sol00 = unknowns(sys0)\n    solve!(sol00, inival, sys0)\nend
    \n
    1×10201 Matrix{Float64}:\n 1.0  0.951791  0.906016  0.862563  0.821331  …  0.862563  0.906016  0.951791  1.0
    \n\n\n

    Replace this by:

    \n\n
    sol0 = solve(sys0; inival = 0.1)
    \n
    1×10201 Matrix{Float64}:\n 1.02241e-33  0.0319717  0.045243  0.0554731  …  0.045243  0.0319717  1.02241e-33
    \n\n\n

    Docstring of solve

    \n\n\n
    solve(system; kwargs...)

    Built-in solution method for VoronoiFVM.System.

    Keyword arguments:

    • General for all solvers

      • inival (default: 0) : Array created via unknowns or number giving the initial value.

      • control (default: nothing): Pass instance of SolverControl

      • All elements of SolverControl can be used as kwargs. Eventually overwrites values given via control

      • params: Parameters (Parameter handling is experimental and may change)

    • Stationary solver: Invoked if neither times nor embed, nor tstep are given as keyword argument.

      • time (default: 0.0): Set time value.

      Returns a DenseSolutionArray or SparseSolutionArray

    • Embedding (homotopy) solver: Invoked if embed kwarg is given. Use homotopy embedding + damped Newton's method to solve stationary problem or to solve series of parameter dependent problems. Parameter step control is performed according to solver control data. kwargs and default values are:

      • embed (default: nothing ): vector of parameter values to be reached exactly

      In addition, all kwargs of the implicit Euler solver (besides times) are handled. Returns a transient solution object sol containing the stored solution(s), see TransientSolution.

    • Implicit Euler transient solver: Invoked if times kwarg is given. Use implicit Euler method + damped Newton's method to solve time dependent problem. Time step control is performed according to solver control data. kwargs and default values are:

      • times (default: nothing ): vector of time values to be reached exactly

      • pre (default: (sol,t)->nothing ): callback invoked before each time step

      • post (default: (sol,oldsol, t, Δt)->nothing ): callback invoked after each time step

      • sample (default: (sol,t)->nothing ): callback invoked after timestep for all times in times[2:end].

      • delta (default: (system, u,v,t, Δt)->norm(sys,u-v,Inf) ): Value used to control the time step size Δu

      If control.handle_error is true, if time step solution throws an error, stepsize is lowered, and step solution is called again with a smaller time value. If control.Δt<control.Δt_min, solution is aborted with error. Returns a transient solution object sol containing the stored solution, see TransientSolution.

    • Implicit Euler timestep solver. Invoked if tstep kwarg is given. Solve one time step of the implicit Euler method.

      • time (default: 0): Set time value.

      • tstep: time step

      Returns a DenseSolutionArray or SparseSolutionArray

    \n\n\n

    Docstring of SolverControl

    \n\n\n
    SolverControl\nSolverControl(;kwargs...)\nSolverControl(linear_solver_strategy, sys; kwargs...)

    Solver control parameter for time stepping, embedding, Newton method and linear solver control. All field names can be used as keyword arguments for solve(system::VoronoiFVM.AbstractSystem; kwargs...)

    Newton's method solves $F(u)=0$ by the iterative procedure $u_{i+1}=u_{i} - d_i F'(u_i)^{-1}F(u_i)$ starting with some initial value $u_0$, where $d_i$ is a damping parameter.

    For linear solver strategies, see VoronoiFVM.LinearSolverStrategy.

    • verbose::Union{Bool, String}: Verbosity control. A collection of output categories is given in a string composed of the following letters:

      • a: allocation warnings

      • d: deprecation warnings

      • e: time/parameter evolution log

      • n: newton solver log

      • l: linear solver log

      Alternatively, a Bool value can be given, resulting in

      • true: \"neda\"

      • false: \"da\"

      Switch off all output including deprecation warnings via verbose=\"\". In the output, corresponding messages are marked e.g. via '[n]', [a] etc. (besides of '[l]')

    • abstol::Float64: Tolerance (in terms of norm of Newton update): terminate if $\\Delta u_i=||u_{i+1}-u_i||_\\infty <$abstol.

    • reltol::Float64: Tolerance (relative to the size of the first update): terminate if $\\Delta u_i/\\Delta u_1<$reltol.

    • maxiters::Int64: Maximum number of newton iterations.

    • tol_round::Float64: Tolerance for roundoff error detection: terminate if $|\\;||u_{i+1}||_1 - ||u_{i}||_1\\;|/ ||u_{i}||_1<$tol_round occurred max_round times in a row.

    • tol_mono::Float64: Tolerance for monotonicity test: terminate with error if $\\Delta u_i/\\Delta u_{i-1}>$1/tol_mono.

    • damp_initial::Float64: Initial damping parameter $d_0$. To handle convergence problems, set this to a value less than 1.

    • damp_growth::Float64: Damping parameter growth factor: $d_{i+1}=\\min(d_i\\cdot$max_growth$,1)$. It should be larger than 1.

    • max_round::Int64: Maximum number of consecutive iterations within roundoff error tolerance The default effectively disables this criterion.

    • unorm::Function: Calculation of Newton update norm

    • rnorm::Function: Functional for roundoff error calculation

    • method_linear::Union{Nothing, LinearSolve.SciMLLinearSolveAlgorithm}: Solver method for linear systems (see LinearSolve.jl). If given nothing, as default are chosen (for Float64 calculations):

      • 1D: KLUFactorization()

      • 2D: SparspakFactorization()

      • 3D: UMFPACKFactorization()

      SparspakFactorization() is the default choice for general number types. Users should experiment with what works best for their problem.

      For easy access to this functionality, see see also VoronoiFVM.LinearSolverStrategy.

    • reltol_linear::Float64: Relative tolerance of iterative linear solver.

    • abstol_linear::Float64: Absolute tolerance of iterative linear solver.

    • maxiters_linear::Int64: Maximum number of iterations of linear solver

    • precon_linear::Union{Nothing, Function, ExtendableSparse.AbstractFactorization, LinearSolve.SciMLLinearSolveAlgorithm, Type}: Constructor for preconditioner for linear systems. This should work as a function precon_linear(A) which takes an AbstractSparseMatrixCSC (e.g. an ExtendableSparseMatrix) and returns a preconditioner object in the sense of LinearSolve.jl, i.e. which has an ldiv!(u,A,v) method. Useful examples:

      • ExtendableSparse.ILUZero

      • ExtendableSparse.Jacobi

      For easy access to this functionality, see see also VoronoiFVM.LinearSolverStrategy.

    • keepcurrent_linear::Bool: Update preconditioner in each Newton step ?

    • Δp::Float64: Initial parameter step for embedding.

    • Δp_max::Float64: Maximal parameter step size.

    • Δp_min::Float64: Minimal parameter step size.

    • Δp_grow::Float64: Maximal parameter step size growth.

    • Δp_decrease::Float64: Parameter step decrease factor upon rejection

    • Δt::Float64: Initial time step size.

    • Δt_max::Float64: Maximal time step size.

    • Δt_min::Float64: Minimal time step size.

    • Δt_grow::Float64: Maximal time step size growth.

    • Δt_decrease::Float64: Time step decrease factor upon rejection

    • Δu_opt::Float64: Optimal size of update for time stepping and embedding. The algorithm tries to keep the difference in norm between \"old\" and \"new\" solutions approximately at this value.

    • Δu_max_factor::Float64: Control maximum sice of update Δu for time stepping and embeding relative to Δu_opt. Time steps with Δu > Δu_max_factor*Δu_opt will be rejected.

    • force_first_step::Bool: Force first timestep.

    • num_final_steps::Int64: Number of final steps to adjust at end of time interval in order to prevent breakdown of step size.

    • handle_exceptions::Bool: Handle exceptions during transient solver and parameter embedding. If true, exceptions in Newton solves are caught, the embedding resp. time step is lowered, and solution is retried. Moreover, if embedding or time stepping fails (e.g. due to reaching minimal step size), a warning is issued, and a solution is returned with all steps calculated so far.

      Otherwise (by default) errors are thrown.

    • store_all::Bool: Store all steps of transient/embedding problem:

    • in_memory::Bool: Store transient/embedding solution in memory

    • log::Any: Record history

    • edge_cutoff::Float64: Edge parameter cutoff for rectangular triangles.

    • pre::Function: Function pre(sol,t) called before time/embedding step

    • post::Function: Function post(sol,oldsol,t,Δt) called after successful time/embedding step

    • sample::Function: Function sample(sol,t) to be called for each t in times[2:end]

    • delta::Function: Time step error estimator. A function Δu=delta(system,u,uold,t,Δt) to calculate Δu.

    • tol_absolute::Union{Nothing, Float64}

    • tol_relative::Union{Nothing, Float64}

    • damp::Union{Nothing, Float64}

    • damp_grow::Union{Nothing, Float64}

    • max_iterations::Union{Nothing, Int64}

    • tol_linear::Union{Nothing, Float64}

    • max_lureuse::Union{Nothing, Int64}

    • mynorm::Union{Nothing, Function}

    • myrnorm::Union{Nothing, Function}

    \n\n\n

    Rely on LinearSolve.jl for linear system solution

    \n\n\n

    This provides easy access to a large variety of linear solvers:

    \n\n\n

    LU factorization from UMFPACK

    \n\n
    umf_sol = solve(sys0; inival = 0.1, method_linear = UMFPACKFactorization(), verbose = true)
    \n
    1×10201 Matrix{Float64}:\n 1.02241e-33  0.0319717  0.045243  0.0554731  …  0.045243  0.0319717  1.02241e-33
    \n\n
    @test isapprox(umf_sol, sol0, atol = 1.0e-7)
    \n
    Test Passed
    \n\n\n

    LU factorization from Sparspak.jl

    \n\n
    sppk_sol = solve(sys0; inival = 0.1, method_linear = SparspakFactorization(), verbose = true)
    \n
    1×10201 Matrix{Float64}:\n 1.02241e-33  0.0319717  0.045243  0.0554731  …  0.045243  0.0319717  1.02241e-33
    \n\n
    @test isapprox(sppk_sol, sol0, atol = 1.0e-7)
    \n
    Test Passed
    \n\n\n

    Iterative solvers

    \n\n\n
    BICGstab from Krylov.jl with diagonal (Jacobi) preconditioner

    The Jacobi preconditioner is defined in ExtendableSparse.jl.

    \n\n
    krydiag_sol = solve(sys0;\n                    inival = 0.1,\n                    method_linear = KrylovJL_BICGSTAB(),\n                    precon_linear = JacobiPreconditioner,\n                    verbose = true,)
    \n
    1×10201 Matrix{Float64}:\n 1.02241e-33  0.0319717  0.045243  0.0554731  …  0.045243  0.0319717  1.02241e-33
    \n\n
    @test isapprox(krydiag_sol, sol0, atol = 1.0e-5)
    \n
    Test Passed
    \n\n\n
    BICGstab from Krylov.jl with delayed factorization preconditioner
    \n\n
    krydel_sol = solve(sys0;\n                   inival = 0.1,\n                   method_linear = KrylovJL_BICGSTAB(),\n                   precon_linear = SparspakFactorization(),\n                   verbose = \"nlad\",)
    \n
    1×10201 Matrix{Float64}:\n 1.02241e-33  0.0319717  0.045243  0.0554731  …  0.045243  0.0319717  1.02241e-33
    \n\n
    @test isapprox(krydel_sol, sol0, atol = 1.0e-5)
    \n
    Test Passed
    \n\n\n
    BICGstab from Krylov.jl with ilu0 preconditioner

    ILUZeroPreconditioner is exported from ExtendableSparse and wraps the predonditioner defined in ILUZero.jl .

    \n\n
    kryilu0_sol = solve(sys0;\n                    inival = 0.5,\n                    method_linear = KrylovJL_BICGSTAB(),\n                    precon_linear = ILUZeroPreconditioner,\n                    verbose = true,)
    \n
    1×10201 Matrix{Float64}:\n 1.05391e-33  0.0319717  0.045243  0.0554731  …  0.045243  0.0319717  1.05573e-33
    \n\n
    @test isapprox(kryilu0_sol, sol0, atol = 1.0e-5)
    \n
    Test Passed
    \n\n\n

    New verbosity handling

    \n\n\n
    • verbose can now be a Bool or a String of flag characters, allowing for control of different output categories. I would love to do this via logging, but there is still a long way to go IMHO

    • Allocation check is active by default with warnings which can be muted by passing a verbose string without 'a'. This is now the only control in this respect. All check_allocs methods/kwargs, control via environment variables have been removed.

    • Deprecation warnings can be switched off by passing a verbose string without 'd'.

    • Improve iteration logging etc., allow for logging of linear iterations ('l' flag character)

    \n\n\n

    The following example gives some information in this respect:

    \n\n
    D = 0.1
    \n
    0.1
    \n\n
    function xflux(f, u, edge)\n    f[1] = D * (u[1, 1]^2 - u[1, 2]^2)\nend
    \n
    xflux (generic function with 1 method)
    \n\n
    xsys = VoronoiFVM.System(0:0.001:1; flux = xflux, species = [1])
    \n
    VoronoiFVM.System{Float64, Float64, Int32, Int64, Matrix{Int32}, Matrix{Float64}}(num_species=1)\n
    \n\n
    solve(xsys; inival = 0.1, times = [0, 1]);
    \n\n\n\n

    If we find these warnings annoying, we can switch them off:

    \n\n
    solve(xsys; inival = 0.1, times = [0, 1], verbose = \"\");
    \n\n\n\n

    Or we get some more logging:

    \n\n
    solve(xsys; inival = 0.1, times = [0, 1], verbose = \"en\");
    \n\n\n\n

    But we can also look for the reasons of the allocations. Here, global values should be declared as constants.

    \n\n
    const D1 = 0.1
    \n
    0.1
    \n\n
    function xflux1(f, u, edge)\n    f[1] = D1 * (u[1, 1]^2 - u[1, 2]^2)\nend
    \n
    xflux1 (generic function with 1 method)
    \n\n
    xsys1 = VoronoiFVM.System(0:0.001:1; flux = xflux1, species = [1])
    \n
    VoronoiFVM.System{Float64, Float64, Int32, Int64, Matrix{Int32}, Matrix{Float64}}(num_species=1)\n
    \n\n
    solve(xsys1; inival = 0.1, times = [0, 1]);
    \n\n\n","category":"page"},{"location":"plutostatichtml_examples/api-update/#v0.14","page":"API Updates","title":"v0.14","text":"","category":"section"},{"location":"plutostatichtml_examples/api-update/","page":"API Updates","title":"API Updates","text":"
    \n
    \n\n\n

    VoronoiFVM.System constructor

    \n\n\n

    Implicit creation of physics

    The VoronoiFVM.Physics struct almost never was used outside of the constructor of VoronoiFVM.System. Now it is possible to specify the flux functions directly in the system constructor. By default, it is also possible to set a list of species which are attached to all interior and boundary regions of the grid.

    \n\n
    grid1 = simplexgrid(0:0.1:1);
    \n\n\n
    function multispecies_flux(y, u, edge)\n    for i = 1:(edge.nspec)\n        y[i] = u[i, 1] - u[i, 2]\n    end\nend
    \n
    multispecies_flux (generic function with 1 method)
    \n\n
    function test_reaction(y, u, node)\n    y[1] = u[1]\n    y[2] = -u[1]\nend
    \n
    test_reaction (generic function with 1 method)
    \n\n
    begin\n    system1 = VoronoiFVM.System(grid1;\n                                flux = multispecies_flux,\n                                reaction = test_reaction,\n                                species = [1, 2])\n    boundary_dirichlet!(system1; species = 1, region = 1, value = 1)\n    boundary_dirichlet!(system1; species = 2, region = 2, value = 0)\nend;
    \n\n\n
    sol1 = solve(system1);
    \n\n\n\n\n\n
    @test isapprox(sum(sol1), 11.323894375033476, rtol = 1.0e-14)
    \n
    Test Passed
    \n\n\n

    Boundary conditions as part of physics

    This makes the API more consistent and opens an easy possibility to have space and time dependent boundary conditions. One can specify them either in breaction or the synonymous bcondition.

    \n\n
    function bcond2(y, u, bnode)\n    boundary_neumann!(y, u, bnode; species = 1, region = 1, value = sin(bnode.time))\n    boundary_dirichlet!(y, u, bnode; species = 2, region = 2, value = 0)\nend;
    \n\n\n
    system2 = VoronoiFVM.System(grid1;\n                            flux = multispecies_flux,\n                            reaction = test_reaction,\n                            species = [1, 2],\n                            bcondition = bcond2,\n                            check_allocs = false);
    \n\n\n
    sol2 = solve(system2; times = (0, 10), Δt_max = 0.01);
    \n\n\n\nGridVisualizer(Plotter=CairoMakie)\n\n\n

    time: 4.99

    \n\n\n\n\n
    @test isapprox(sum(sol2) / length(sol2), 2.4921650158811794, rtol = 1.0e-14)
    \n
    Test Passed
    \n\n\n

    Implicit creation of grid

    \n\n\n

    By passing data for grid creation (one to three abstract vectors) instead a grid, a tensor product grid is implicitly created. This example also demonstrates position dependent boundary values.

    \n\n
    function bcond3(y, u, bnode)\n    boundary_dirichlet!(y, u, bnode; region = 4, value = bnode[2])\n    boundary_dirichlet!(y, u, bnode; region = 2, value = -bnode[2])\nend;
    \n\n\n
    system3 = VoronoiFVM.System(-1:0.1:1,\n                            -1:0.1:1;\n                            flux = multispecies_flux,\n                            bcondition = bcond3,\n                            species = 1);
    \n\n\n
    sol3 = solve(system3);
    \n\n\n
    @test isapprox(sum(sol3), 0.0, atol = 1.0e-14)
    \n
    Test Passed
    \n\n\n

    GridVisualize API extended to System

    Instead of a grid, a system can be passed to gridplot and scalarplot.

    \n\n
    scalarplot(system3, sol3; resolution = (300, 300), levels = 10, colormap = :hot)
    \n\n\n\n

    Parameters of solve

    \n\n\n

    The solve API has been simplified and made more Julian. All entries of VoronoiFVM.NewtonControl can be now passed as keyword arguments to solve.

    Another new keyword argument is inival which allows to pass an initial value which by default is initialized to zero. Therefore we now can write solve(system) as we already have seen above.

    \n\n
    reaction4(y, u, bnode) = y[1] = -bnode[1]^2 + u[1]^4;
    \n\n\n
    bc4(args...) = boundary_dirichlet!(args...; value = 0);
    \n\n\n
    system4 = VoronoiFVM.System(-10:0.1:10;\n                            species = [1],\n                            reaction = reaction4,\n                            flux = multispecies_flux,\n                            bcondition = bc4);
    \n\n\n
    sol4 = solve(system4; log = true, damp_initial = 0.001, damp_growth = 3);
    \n\n\n\n\n\n
    @test isapprox(sum(sol4), 418.58515700568535, rtol = 1.0e-14)
    \n
    Test Passed
    \n\n\n
    \n\n\n
    \n
    \n

    Built with Julia 1.10.2 and

    \nCairoMakie 0.11.5
    \nExtendableGrids 1.2.3
    \nExtendableSparse 1.3.1
    \nGridVisualize 1.5.0
    \nILUZero 0.2.0
    \nLinearSolve 2.22.1
    \nPkg 1.10.0
    \nPlutoUI 0.7.55
    \nRevise 3.5.13
    \nVoronoiFVM 1.16.0\n
    \n\n","category":"page"},{"location":"plutostatichtml_examples/api-update/","page":"API Updates","title":"API Updates","text":"EditURL = \"https://github.com/j-fu/VoronoiFVM.jl/blob/main/nothing\"","category":"page"},{"location":"solutions/#Solution-objects","page":"Solution objects","title":"Solution objects","text":"","category":"section"},{"location":"solutions/#Dense-solution-arrays","page":"Solution objects","title":"Dense solution arrays","text":"","category":"section"},{"location":"solutions/","page":"Solution objects","title":"Solution objects","text":"Modules = [VoronoiFVM]\nPages=[\"vfvm_densesolution.jl\"]","category":"page"},{"location":"solutions/#VoronoiFVM.DenseSolutionArray","page":"Solution objects","title":"VoronoiFVM.DenseSolutionArray","text":"const DenseSolutionArray=Matrix\n\nDense storage of solution\n\n\n\n\n\n","category":"type"},{"location":"solutions/#VoronoiFVM._add-Union{Tuple{Tv}, Tuple{Matrix{Tv}, Any, Any}} where Tv","page":"Solution objects","title":"VoronoiFVM._add","text":"_add(U::Array{Tv, 2}, idof, val) -> Any\n\n\nAdd residual value into global degree of freedom\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.dof-Union{Tuple{Tv}, Tuple{Matrix{Tv}, Integer, Integer}} where Tv","page":"Solution objects","title":"VoronoiFVM.dof","text":"dof(a, ispec, K)\n\n\nGet degree of freedom number\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.values-Union{Tuple{Matrix{Tv}}, Tuple{Tv}} where Tv","page":"Solution objects","title":"VoronoiFVM.values","text":"values(a)\n\n\nArray of values in solution array.\n\n\n\n\n\n","category":"method"},{"location":"solutions/#Sparse-solution-arrays","page":"Solution objects","title":"Sparse solution arrays","text":"","category":"section"},{"location":"solutions/","page":"Solution objects","title":"Solution objects","text":"Modules = [VoronoiFVM]\nPages=[\"vfvm_sparsesolution.jl\"]","category":"page"},{"location":"solutions/#VoronoiFVM.SparseSolutionArray","page":"Solution objects","title":"VoronoiFVM.SparseSolutionArray","text":"struct SparseSolutionArray{Tv, Ti} <: AbstractArray{Tv, 2}\n\nStruct holding solution information for SparseSystem. Solution is stored in a sparse matrix structure.\n\nThis class plays well with the abstract array interface.\n\nFields:\n\nnode_dof::SparseArrays.SparseMatrixCSC{Tv, Ti} where {Tv, Ti}: Sparse matrix holding actual data.\n\n\n\n\n\n","category":"type"},{"location":"solutions/#Base.copy-Union{Tuple{VoronoiFVM.SparseSolutionArray{Tv, Ti}}, Tuple{Ti}, Tuple{Tv}} where {Tv, Ti}","page":"Solution objects","title":"Base.copy","text":"copy(this)\n\n\nCreate a copy of sparse solution array\n\n\n\n\n\n","category":"method"},{"location":"solutions/#Base.getindex-Tuple{VoronoiFVM.SparseSolutionArray, Integer, Integer}","page":"Solution objects","title":"Base.getindex","text":"getindex(a, ispec, inode)\n\n\nAccessor for sparse solution array.\n\n\n\n\n\n","category":"method"},{"location":"solutions/#Base.setindex!-Tuple{VoronoiFVM.SparseSolutionArray, Any, Integer, Integer}","page":"Solution objects","title":"Base.setindex!","text":"setindex!(a, v, ispec, inode)\n\n\nAccessor for sparse solution array.\n\n\n\n\n\n","category":"method"},{"location":"solutions/#Base.similar-Union{Tuple{VoronoiFVM.SparseSolutionArray{Tv, Ti}}, Tuple{Ti}, Tuple{Tv}} where {Tv, Ti}","page":"Solution objects","title":"Base.similar","text":"similar(this)\n\n\nCreate a similar uninitialized sparse solution array\n\n\n\n\n\n","category":"method"},{"location":"solutions/#Base.size-Tuple{VoronoiFVM.SparseSolutionArray}","page":"Solution objects","title":"Base.size","text":"size(a)\n\n\nReturn size of sparse solution array.\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM._add-Tuple{VoronoiFVM.SparseSolutionArray, Any, Any}","page":"Solution objects","title":"VoronoiFVM._add","text":"_add(U::VoronoiFVM.SparseSolutionArray, idof, val) -> Any\n\n\nAdd residual value into global degree of freedom\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.dof-Union{Tuple{Ti}, Tuple{Tv}, Tuple{VoronoiFVM.SparseSolutionArray{Tv, Ti}, Integer, Integer}} where {Tv, Ti}","page":"Solution objects","title":"VoronoiFVM.dof","text":"dof(a, i, j)\n\n\nGet number of degree of freedom. Return 0 if species is not defined in node.\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.getdof-Tuple{VoronoiFVM.SparseSolutionArray, Integer}","page":"Solution objects","title":"VoronoiFVM.getdof","text":"getdof(a, i)\n\n\nReturn value for degree of freedom.\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.setdof!-Tuple{VoronoiFVM.SparseSolutionArray, Any, Integer}","page":"Solution objects","title":"VoronoiFVM.setdof!","text":"setdof!(a, v, i)\n\n\nSet value for degree of freedom.\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.values-Tuple{VoronoiFVM.SparseSolutionArray}","page":"Solution objects","title":"VoronoiFVM.values","text":"values(a)\n\n\nArray of values in sparse solution array.\n\n\n\n\n\n","category":"method"},{"location":"solutions/#Transient-solution","page":"Solution objects","title":"Transient solution","text":"","category":"section"},{"location":"solutions/","page":"Solution objects","title":"Solution objects","text":"Modules = [VoronoiFVM]\nPages=[\"vfvm_transientsolution.jl\"]","category":"page"},{"location":"solutions/#VoronoiFVM.AbstractTransientSolution","page":"Solution objects","title":"VoronoiFVM.AbstractTransientSolution","text":"abstract type AbstractTransientSolution{T, N, A, B} <: AbstractDiffEqArray{T, N, A}\n\nAbstract type for transient solution\n\n\n\n\n\n","category":"type"},{"location":"solutions/#VoronoiFVM.TransientSolution","page":"Solution objects","title":"VoronoiFVM.TransientSolution","text":"mutable struct TransientSolution{T, N, A, B} <: VoronoiFVM.AbstractTransientSolution{T, N, A, B}\n\nTransient solution structure\n\nFields\n\nu::Any: Vector of solutions\n\nt::Any: Vector of times\n\nhistory::TransientSolverHistory: History\n\nInterface\n\nObject of this type adhere to the AbstractDiffEqArray interface. For indexing and interpolation, see https://diffeq.sciml.ai/stable/basics/solution/.\n\nIn particular, a TransientSolution sol can be accessed as follows:\n\nsol[i] contains the solution for timestep i\nsol[ispec,:,i] contains the solution for component ispec at timestep i\nsol(t) returns a (linearly) interpolated solution value for t.\nsol.t[i] is the corresponding time for timestep i\nsol[ispec,ix,i] refers to solution of component ispec at node ix at moment i\n\n\n\n\n\n","category":"type"},{"location":"solutions/#VoronoiFVM.TransientSolution-Union{Tuple{T}, Tuple{Number, AbstractArray{T}}} where T","page":"Solution objects","title":"VoronoiFVM.TransientSolution","text":"TransientSolution(t0,inival;\n in_memory=true,\n keep_open=true,\n fname=tempname(pwd())*\".jld2\"\n\nConstructor of transient solution with initial value and initial time.\n\nin_memory: if true (default), data are kept in main memory, otherwise on disk (via JLD2)\nkeep_open: if true, disk file is not closed during the existence of the object\nfname: file name for the disk file\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.VectorOfDiskArrays-Union{Tuple{AbstractArray{T}}, Tuple{T}} where T","page":"Solution objects","title":"VoronoiFVM.VectorOfDiskArrays","text":"VectorOfDiskArrays(firstobj:AbstractArray;\n keep_open=true,\n fname= fname=tempname(pwd())*\".jld2\")\n\nConstructor of vector of arrays stored on disk (via JLD2).\n\nkeep_open: if true, disk file is not closed during the existence of the object\nfname: file name for the disk file\n\nThe disk file is automatically removed if the object is garbage collected.\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.history-Tuple{VoronoiFVM.AbstractTransientSolution}","page":"Solution objects","title":"VoronoiFVM.history","text":"history(tsol)\n\nReturn solver history if log was set to true. See see NewtonSolverHistory, TransientSolverHistory.\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.history_details-Tuple{VoronoiFVM.AbstractTransientSolution}","page":"Solution objects","title":"VoronoiFVM.history_details","text":"history_details(tsol)\n\nReturn details of solver history from last solve call, if log was set to true. See details.\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.history_summary-Tuple{VoronoiFVM.AbstractTransientSolution}","page":"Solution objects","title":"VoronoiFVM.history_summary","text":"history_summary(tsol)\n\nReturn summary of solver history from last solve call, if log was set to true.\n\n\n\n\n\n","category":"method"},{"location":"module_examples/Example220_NonlinearPoisson2D_BoundarySpecies/#220:-2D-Nonlinear-Poisson-with-boundary-reaction-and-boundary-species","page":"220: 2D Nonlinear Poisson with boundary reaction and boundary species","title":"220: 2D Nonlinear Poisson with boundary reaction and boundary species","text":"","category":"section"},{"location":"module_examples/Example220_NonlinearPoisson2D_BoundarySpecies/","page":"220: 2D Nonlinear Poisson with boundary reaction and boundary species","title":"220: 2D Nonlinear Poisson with boundary reaction and boundary species","text":"(source code)","category":"page"},{"location":"module_examples/Example220_NonlinearPoisson2D_BoundarySpecies/","page":"220: 2D Nonlinear Poisson with boundary reaction and boundary species","title":"220: 2D Nonlinear Poisson with boundary reaction and boundary species","text":"module Example220_NonlinearPoisson2D_BoundarySpecies\n\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\n\nfunction main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse)\n h = 1.0 / convert(Float64, n)\n X = collect(0.0:h:1.0)\n Y = collect(0.0:h:1.0)\n\n grid = VoronoiFVM.Grid(X, Y)\n\n k = 1.0\n eps::Float64 = 1.0\n physics = VoronoiFVM.Physics(;\n breaction = function (f, u, node)\n if node.region == 2\n f[1] = k * (u[1] - u[3])\n f[3] = k * (u[3] - u[1]) + k * (u[3] - u[2])\n f[2] = k * (u[2] - u[3])\n end\n end, bstorage = function (f, u, node)\n if node.region == 2\n f[3] = u[3]\n end\n end, flux = function (f, u, edge)\n f[1] = eps * (u[1, 1] - u[1, 2])\n f[2] = eps * (u[2, 1] - u[2, 2])\n end, source = function (f, node)\n x1 = node[1] - 0.5\n x2 = node[2] - 0.5\n f[1] = exp(-20.0 * (x1^2 + x2^2))\n end, storage = function (f, u, node)\n f[1] = u[1]\n f[2] = u[2]\n end)\n\n sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage)\n\n enable_species!(sys, 1, [1])\n enable_species!(sys, 2, [1])\n enable_boundary_species!(sys, 3, [2])\n\n function tran32!(a, b)\n a[1] = b[2]\n end\n\n bgrid2 = subgrid(grid, [2]; boundary = true, transform = tran32!)\n\n inival = unknowns(sys)\n inival .= 0.0\n\n eps = 1.0e-2\n\n control = VoronoiFVM.NewtonControl()\n control.verbose = verbose\n control.reltol_linear = 1.0e-5\n control.reltol = 1.0e-5\n control.max_lureuse = 0\n tstep = 0.01\n time = 0.0\n istep = 0\n u5 = 0\n p = GridVisualizer(; Plotter = Plotter, layout = (3, 1))\n while time < 1\n time = time + tstep\n U = solve(sys; inival, control, tstep)\n inival .= U\n if verbose\n @printf(\"time=%g\\n\", time)\n end\n tstep *= 1.0\n istep = istep + 1\n U_bound = view(U[3, :], bgrid2)\n u5 = U_bound[5]\n scalarplot!(p[1, 1], grid, U[1, :]; clear = true)\n scalarplot!(p[2, 1], grid, U[2, :])\n scalarplot!(p[3, 1], bgrid2, U_bound; show = true, flimits = (0, 0.0025))\n end\n return u5\nend\n\nusing Test\nfunction runtests()\n @test main(; unknown_storage = :sparse) ≈ 0.0020781361856598\n main(; unknown_storage = :dense) ≈ 0.0020781361856598\nend\nend","category":"page"},{"location":"module_examples/Example220_NonlinearPoisson2D_BoundarySpecies/","page":"220: 2D Nonlinear Poisson with boundary reaction and boundary species","title":"220: 2D Nonlinear Poisson with boundary reaction and boundary species","text":"","category":"page"},{"location":"module_examples/Example220_NonlinearPoisson2D_BoundarySpecies/","page":"220: 2D Nonlinear Poisson with boundary reaction and boundary species","title":"220: 2D Nonlinear Poisson with boundary reaction and boundary species","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example510_Mixture/#510:-Mixture","page":"510: Mixture","title":"510: Mixture","text":"","category":"section"},{"location":"module_examples/Example510_Mixture/","page":"510: Mixture","title":"510: Mixture","text":"(source code)","category":"page"},{"location":"module_examples/Example510_Mixture/","page":"510: Mixture","title":"510: Mixture","text":"Test mixture diffusion flux. The problem is here that in the flux function we need to solve a linear system of equations which calculates the fluxes from the gradients.# To do so without (heap) allocations can be achieved using StrideArrays, together with the possibility to have static (compile time) information about the size of the local arrays to be allocated.","category":"page"},{"location":"module_examples/Example510_Mixture/","page":"510: Mixture","title":"510: Mixture","text":"u_i are the species partial pressures, vec N_i are the species fluxes. D_i^K are the Knudsen diffusion coefficients, and D^B_ij are the binary diffusion coefficients.","category":"page"},{"location":"module_examples/Example510_Mixture/","page":"510: Mixture","title":"510: Mixture","text":" -nabla cdot vec N_i =0 quad (i=1dots n)\n fracvec N_iD^K_i + sum_jneq ifracu_j vec N_i - u_i vec N_jD^B_ij = -vec nabla u_i quad (i=1dots n)","category":"page"},{"location":"module_examples/Example510_Mixture/","page":"510: Mixture","title":"510: Mixture","text":"From this representation, we can derive the matrix M=(m_ij) with","category":"page"},{"location":"module_examples/Example510_Mixture/","page":"510: Mixture","title":"510: Mixture","text":"m_ii= frac1D^K_i + sum_jneq i fracu_jD_ij\nm_ij= -sum_jneq i fracu_iD_ij","category":"page"},{"location":"module_examples/Example510_Mixture/","page":"510: Mixture","title":"510: Mixture","text":"such that","category":"page"},{"location":"module_examples/Example510_Mixture/","page":"510: Mixture","title":"510: Mixture","text":"\tMbeginpmatrix\nvec N_1\nvdots\nvec N_n\nendpmatrix\n=\nbeginpmatrix\nvec nabla u_1\nvdots\nvec nabla u_n\nendpmatrix","category":"page"},{"location":"module_examples/Example510_Mixture/","page":"510: Mixture","title":"510: Mixture","text":"In the two point flux finite volume discretization, this results into a corresponding linear system which calculates the discrete edge fluxes from the discrete gradients. Here we demonstrate how to implement this in a fast, (heap) allocation free way.","category":"page"},{"location":"module_examples/Example510_Mixture/","page":"510: Mixture","title":"510: Mixture","text":"For this purpose, intermediate arrays need to be allocated on the stack with via StrideArrays.jl or MArray from StaticArrays.jl They need to have the same element type as the unknowns passed to the flux function (which could be Float64 or some dual number). Size information must be static, e.g. a global constant, or, as demonstrated here, a type parameter. Broadcasting probably should be avoided.","category":"page"},{"location":"module_examples/Example510_Mixture/","page":"510: Mixture","title":"510: Mixture","text":"As documented in StrideArrays.jl, use @gc_preserve when passing a StrideArray as a function parameter.","category":"page"},{"location":"module_examples/Example510_Mixture/","page":"510: Mixture","title":"510: Mixture","text":"Alternatively, we can try to avoid StrideArrays.jl and use MArrays together with inlined code. In this case, one should be aware of this issue, which requires @inbounds e.g. with reverse order loops.","category":"page"},{"location":"module_examples/Example510_Mixture/","page":"510: Mixture","title":"510: Mixture","text":"See also this Discourse thread.","category":"page"},{"location":"module_examples/Example510_Mixture/","page":"510: Mixture","title":"510: Mixture","text":"module Example510_Mixture\n\nusing Printf\nusing VoronoiFVM\nusing VoronoiFVM.SolverStrategies\n\nusing ExtendableGrids\nusing GridVisualize\nusing LinearAlgebra\nusing AMGCLWrap\nusing Random\nusing StrideArraysCore: @gc_preserve, StrideArray, StaticInt, PtrArray\nusing LinearSolve, ExtendableSparse\nusing StaticArrays\nusing ExtendableSparse\n\nstruct MyData{NSPec}\n DBinary::Symmetric{Float64, Matrix{Float64}}\n DKnudsen::Vector{Float64}\n diribc::Vector{Int}\nend\n\nnspec(::MyData{NSpec}) where {NSpec} = NSpec\n\nfunction flux_strided(f, u, edge, data)\n T = eltype(u)\n M = StrideArray{T}(undef, StaticInt(nspec(data)), StaticInt(nspec(data)))\n au = StrideArray{T}(undef, StaticInt(nspec(data)))\n du = StrideArray{T}(undef, StaticInt(nspec(data)))\n ipiv = StrideArray{Int}(undef, StaticInt(nspec(data)))\n\n for ispec = 1:nspec(data)\n M[ispec, ispec] = 1.0 / data.DKnudsen[ispec]\n du[ispec] = u[ispec, 1] - u[ispec, 2]\n au[ispec] = 0.5 * (u[ispec, 1] + u[ispec, 2])\n end\n\n for ispec = 1:nspec(data)\n for jspec = 1:nspec(data)\n if ispec != jspec\n M[ispec, ispec] += au[jspec] / data.DBinary[ispec, jspec]\n M[ispec, jspec] = -au[ispec] / data.DBinary[ispec, jspec]\n end\n end\n end\n\n if VERSION >= v\"1.9-rc0\"\n # Pivoting linear system solution via RecursiveFactorizations.jl\n @gc_preserve inplace_linsolve!(M, du, ipiv)\n else\n # Non-pivoting implementation currently implemented in vfvm_functions.jl\n @gc_preserve inplace_linsolve!(M, du)\n end\n\n for ispec = 1:nspec(data)\n f[ispec] = du[ispec]\n end\nend\n\nfunction flux_marray(f, u, edge, data)\n T = eltype(u)\n n = nspec(data)\n\n M = MMatrix{nspec(data), nspec(data), T}(undef)\n au = MVector{nspec(data), T}(undef)\n du = MVector{nspec(data), T}(undef)\n ipiv = MVector{nspec(data), Int}(undef)\n\n for ispec = 1:nspec(data)\n M[ispec, ispec] = 1.0 / data.DKnudsen[ispec]\n du[ispec] = u[ispec, 1] - u[ispec, 2]\n au[ispec] = 0.5 * (u[ispec, 1] + u[ispec, 2])\n end\n\n for ispec = 1:nspec(data)\n for jspec = 1:nspec(data)\n if ispec != jspec\n M[ispec, ispec] += au[jspec] / data.DBinary[ispec, jspec]\n M[ispec, jspec] = -au[ispec] / data.DBinary[ispec, jspec]\n end\n end\n end\n\n # Here, we also could use @gc_preserve.\n # As this function is inlined one can avoid StrideArrays.jl\n # Starting with Julia 1.8 one also can use callsite @inline.\n inplace_linsolve!(M, du)\n\n for ispec = 1:nspec(data)\n f[ispec] = du[ispec]\n end\nend\n\nfunction bcondition(f, u, node, data)\n for species = 1:nspec(data)\n boundary_dirichlet!(f, u, node; species, region = data.diribc[1],\n value = species % 2)\n boundary_dirichlet!(f, u, node; species, region = data.diribc[2],\n value = 1 - species % 2)\n end\nend\n\nfunction main(; n = 11, nspec = 5,\n dim = 2,\n Plotter = nothing,\n verbose = false,\n unknown_storage = :dense,\n flux = :flux_strided,\n strategy = nothing,\n assembly = :cellwise)\n h = 1.0 / convert(Float64, n - 1)\n X = collect(0.0:h:1.0)\n DBinary = Symmetric(fill(0.1, nspec, nspec))\n for ispec = 1:nspec\n DBinary[ispec, ispec] = 0\n end\n\n DKnudsen = fill(1.0, nspec)\n\n if dim == 1\n grid = VoronoiFVM.Grid(X)\n diribc = [1, 2]\n elseif dim == 2\n grid = VoronoiFVM.Grid(X, X)\n diribc = [4, 2]\n else\n grid = VoronoiFVM.Grid(X, X, X)\n diribc = [4, 2]\n end\n\n function storage(f, u, node, data)\n f .= u\n end\n\n _flux = flux == :flux_strided ? flux_strided : flux_marray\n\n data = MyData{nspec}(DBinary, DKnudsen, diribc)\n sys = VoronoiFVM.System(grid; flux = _flux, storage, bcondition, species = 1:nspec, data, assembly,unknown_storage)\n\n @info \"Strategy: $(strategy)\"\n control = SolverControl(strategy, sys)\n control.maxiters = 500\n @info control.method_linear\n u = solve(sys; verbose, control, log = true)\n @show norm(u)\n norm(u)\nend\n\nusing Test\nfunction runtests()","category":"page"},{"location":"module_examples/Example510_Mixture/","page":"510: Mixture","title":"510: Mixture","text":"Legacy strategy list (only in 1.5)","category":"page"},{"location":"module_examples/Example510_Mixture/","page":"510: Mixture","title":"510: Mixture","text":" strat1 = [direct_umfpack(), gmres_umfpack(), gmres_eqnblock_umfpack(),\n gmres_iluzero(),","category":"page"},{"location":"module_examples/Example510_Mixture/","page":"510: Mixture","title":"510: Mixture","text":" gmres_eqnblock_iluzero(),\n gmres_pointblock_iluzero()","category":"page"},{"location":"module_examples/Example510_Mixture/","page":"510: Mixture","title":"510: Mixture","text":" ]","category":"page"},{"location":"module_examples/Example510_Mixture/","page":"510: Mixture","title":"510: Mixture","text":"Equivalent up-to-date list","category":"page"},{"location":"module_examples/Example510_Mixture/","page":"510: Mixture","title":"510: Mixture","text":" strat2 = [DirectSolver(UMFPACKFactorization()),\n GMRESIteration(UMFPACKFactorization()),\n GMRESIteration(UMFPACKFactorization(), EquationBlock()),\n GMRESIteration(AMGCL_AMGPreconditioner(),EquationBlock()),\n BICGstabIteration(AMGCL_AMGPreconditioner(),EquationBlock()),\n GMRESIteration(ILUZeroPreconditioner()),","category":"page"},{"location":"module_examples/Example510_Mixture/","page":"510: Mixture","title":"510: Mixture","text":" GMRESIteration(ILUZeroPreconditioner(), EquationBlock()),\n GMRESIteration(ILUZeroPreconditioner(), PointBlock())","category":"page"},{"location":"module_examples/Example510_Mixture/","page":"510: Mixture","title":"510: Mixture","text":" ]\n\n val1D = 4.788926530387466\n val2D = 15.883072449873742\n val3D = 52.67819183426213\n\n res1 = main(; dim = 1, assembly = :edgewise) ≈ val1D &&\n main(; dim = 2, assembly = :edgewise) ≈ val2D &&\n main(; dim = 3, assembly = :edgewise) ≈ val3D &&\n main(; dim = 1, flux = :flux_marray, assembly = :edgewise) ≈ val1D &&\n main(; dim = 2, flux = :flux_marray, assembly = :edgewise) ≈ val2D &&\n main(; dim = 3, flux = :flux_marray, assembly = :edgewise) ≈ val3D &&\n all(map(strategy -> main(; dim = 2, flux = :flux_marray, strategy) ≈ val2D, strat1))\n\n res2 = main(; dim = 1, assembly = :cellwise) ≈ val1D &&\n main(; dim = 2, assembly = :cellwise) ≈ val2D &&\n main(; dim = 3, assembly = :cellwise) ≈ val3D &&\n main(; dim = 1, flux = :flux_marray, assembly = :cellwise) ≈ val1D &&\n main(; dim = 2, flux = :flux_marray, assembly = :cellwise) ≈ val2D &&\n main(; dim = 3, flux = :flux_marray, assembly = :cellwise) ≈ val3D &&\n all(map(strategy -> main(; dim = 2, flux = :flux_marray, strategy) ≈ val2D, strat2))\n @show res1, res2\n\n @test res1 && res2\nend\nend","category":"page"},{"location":"module_examples/Example510_Mixture/","page":"510: Mixture","title":"510: Mixture","text":"","category":"page"},{"location":"module_examples/Example510_Mixture/","page":"510: Mixture","title":"510: Mixture","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example420_DiscontinuousQuantities/#420:-Discontinuous-Quantities","page":"420: Discontinuous Quantities","title":"420: Discontinuous Quantities","text":"","category":"section"},{"location":"module_examples/Example420_DiscontinuousQuantities/","page":"420: Discontinuous Quantities","title":"420: Discontinuous Quantities","text":"(source code)","category":"page"},{"location":"module_examples/Example420_DiscontinuousQuantities/","page":"420: Discontinuous Quantities","title":"420: Discontinuous Quantities","text":"Test jumping species and quantity handling","category":"page"},{"location":"module_examples/Example420_DiscontinuousQuantities/","page":"420: Discontinuous Quantities","title":"420: Discontinuous Quantities","text":"module Example420_DiscontinuousQuantities\nusing Printf\nusing VoronoiFVM\nusing SparseArrays\nusing ExtendableGrids\nusing GridVisualize\nusing LinearAlgebra\n\nfunction main(; N = 5, Plotter = nothing, unknown_storage = :sparse, assembly = :edgewise)\n XX = collect(0:0.1:1)\n xcoord = XX\n for i = 1:(N - 1)\n xcoord = glue(xcoord, XX .+ i)\n end\n grid2 = simplexgrid(xcoord)\n for i = 1:N\n cellmask!(grid2, [i - 1], [i], i)\n end\n for i = 1:(N - 1)\n bfacemask!(grid2, [i], [i], i + 2)\n end\n\n params = zeros(2, num_cellregions(grid2))\n for i = 1:num_cellregions(grid2)\n params[1, i] = i\n params[2, i] = 10 * i\n end\n\n system = VoronoiFVM.System(grid2; unknown_storage = unknown_storage, assembly = assembly)\n\n # First, we introduce a continuous quantity which we name \"cspec\". Note that the \"species number\" can be assigned automatically if not given explicitly.\n cspec = ContinuousQuantity(system, 1:N; ispec = 1, id = 1)\n\n # A discontinuous quantity can be introduced as well. by default, each reagion gets a new species number. This can be overwritten by the user.\n dspec = DiscontinuousQuantity(system, 1:N; regionspec = [2 + i % 2 for i = 1:N], id = 2)","category":"page"},{"location":"module_examples/Example420_DiscontinuousQuantities/","page":"420: Discontinuous Quantities","title":"420: Discontinuous Quantities","text":"check 1D array access with quantities","category":"page"},{"location":"module_examples/Example420_DiscontinuousQuantities/","page":"420: Discontinuous Quantities","title":"420: Discontinuous Quantities","text":" carrierList = [cspec dspec]\n numberCarriers = length(carrierList)\n\n params2 = zeros(1, numberCarriers)\n\n for icc ∈ carrierList\n params2[icc] = 2\n end\n\n for i = 1:numberCarriers\n @assert params2[i] == 2\n end","category":"page"},{"location":"module_examples/Example420_DiscontinuousQuantities/","page":"420: Discontinuous Quantities","title":"420: Discontinuous Quantities","text":"check 2D array access with quantities","category":"page"},{"location":"module_examples/Example420_DiscontinuousQuantities/","page":"420: Discontinuous Quantities","title":"420: Discontinuous Quantities","text":" for i = 1:num_cellregions(grid2)\n @assert params[cspec, i] == i\n @assert params[dspec, i] == 10 * i\n end\n\n for i = 1:num_cellregions(grid2)\n params[cspec, i] = -i\n params[dspec, i] = -10 * i\n end\n\n for i = 1:num_cellregions(grid2)\n @assert params[1, i] == -i\n @assert params[2, i] == -10 * i\n end\n\n ##For both quantities, we define simple diffusion fluxes:\n\n function flux(f, u, edge)\n f[dspec] = u[dspec, 1] - u[dspec, 2]\n f[cspec] = u[cspec, 1] - u[cspec, 2]\n end\n\n d1 = 1\n q1 = 0.2\n\n function breaction(f, u, bnode)","category":"page"},{"location":"module_examples/Example420_DiscontinuousQuantities/","page":"420: Discontinuous Quantities","title":"420: Discontinuous Quantities","text":"left outer boundary value for dspec","category":"page"},{"location":"module_examples/Example420_DiscontinuousQuantities/","page":"420: Discontinuous Quantities","title":"420: Discontinuous Quantities","text":" if bnode.region == 1\n f[dspec] = u[dspec] + 0.5\n end\n\n # Define a thin layer interface condition for `dspec` and an interface source for `cspec`.\n if bnode.region > 2\n react = (u[dspec, 1] - u[dspec, 2]) / d1\n f[dspec, 1] = react\n f[dspec, 2] = -react\n f[cspec] = -q1 * u[cspec]\n end\n end\n\n physics!(system, VoronoiFVM.Physics(; flux = flux,\n breaction = breaction))\n\n # Set boundary conditions\n boundary_dirichlet!(system, dspec, 2, 0.1)\n boundary_dirichlet!(system, cspec, 1, 0.1)\n boundary_dirichlet!(system, cspec, 2, 1.0)\n subgrids = VoronoiFVM.subgrids(dspec, system)\n\n U = solve(system)\n\n dvws = views(U, dspec, subgrids, system)\n cvws = views(U, cspec, subgrids, system)\n vis = GridVisualizer(; resolution = (600, 300), Plotter = Plotter)\n for i in eachindex(dvws)\n scalarplot!(vis, subgrids[i], dvws[i]; flimits = (-0.5, 1.5), clear = false,\n color = :red)\n scalarplot!(vis, subgrids[i], cvws[i]; flimits = (-0.5, 1.5), clear = false,\n color = :green)\n end\n reveal(vis)\n I = integrate(system, system.physics.storage, U)\n sum(I[dspec, :]) + sum(I[cspec, :])\nend\n\nusing Test\nfunction runtests()\n testval = 4.2\n @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval\nend\n\nend","category":"page"},{"location":"module_examples/Example420_DiscontinuousQuantities/","page":"420: Discontinuous Quantities","title":"420: Discontinuous Quantities","text":"","category":"page"},{"location":"module_examples/Example420_DiscontinuousQuantities/","page":"420: Discontinuous Quantities","title":"420: Discontinuous Quantities","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example406_WeirdReaction/#406:-1D-Weird-Surface-Reaction","page":"406: 1D Weird Surface Reaction","title":"406: 1D Weird Surface Reaction","text":"","category":"section"},{"location":"module_examples/Example406_WeirdReaction/","page":"406: 1D Weird Surface Reaction","title":"406: 1D Weird Surface Reaction","text":"(source code)","category":"page"},{"location":"module_examples/Example406_WeirdReaction/","page":"406: 1D Weird Surface Reaction","title":"406: 1D Weird Surface Reaction","text":"Species A and B exist in the interior of the domain, species C lives a the boundary Gamma_1. We assume a heterogeneous reaction scheme where A reacts to B with a rate depending on nabla A near the surface","category":"page"},{"location":"module_examples/Example406_WeirdReaction/","page":"406: 1D Weird Surface Reaction","title":"406: 1D Weird Surface Reaction","text":"beginaligned\n A leftrightarrow B\nendaligned","category":"page"},{"location":"module_examples/Example406_WeirdReaction/","page":"406: 1D Weird Surface Reaction","title":"406: 1D Weird Surface Reaction","text":"In Omega, both A and B are transported through diffusion:","category":"page"},{"location":"module_examples/Example406_WeirdReaction/","page":"406: 1D Weird Surface Reaction","title":"406: 1D Weird Surface Reaction","text":"beginaligned\npartial_t u_B - nablacdot D_A nabla u_A = f_A\npartial_t u_B - nablacdot D_B nabla u_B = 0\nendaligned","category":"page"},{"location":"module_examples/Example406_WeirdReaction/","page":"406: 1D Weird Surface Reaction","title":"406: 1D Weird Surface Reaction","text":"Here, f(x) is a source term creating A. On Gamma_2, we set boundary conditions","category":"page"},{"location":"module_examples/Example406_WeirdReaction/","page":"406: 1D Weird Surface Reaction","title":"406: 1D Weird Surface Reaction","text":"beginaligned\nD_A nabla u_A = 0\nu_B=0\nendaligned","category":"page"},{"location":"module_examples/Example406_WeirdReaction/","page":"406: 1D Weird Surface Reaction","title":"406: 1D Weird Surface Reaction","text":"describing no normal flux for A and zero concentration of B. On Gamma_1, we use the mass action law to describe the boundary reaction and the evolution of the boundary concentration C. We assume that there is a limited amount of surface sites S for species C, so in fact A has to react with a free surface site in order to become C which reflected by the factor 1-u_C. The same is true for B.","category":"page"},{"location":"module_examples/Example406_WeirdReaction/","page":"406: 1D Weird Surface Reaction","title":"406: 1D Weird Surface Reaction","text":"beginaligned\nR_AB(u_A u_B)=k_AB^+exp(u_A(0))u_A - k_AB^-exp(-u_A(0))u_B\n- D_A nabla u_A + R_AB(u_A u_B) =0 \n- D_B nabla u_B - R_AB(u_A u_B) =0 \nendaligned","category":"page"},{"location":"module_examples/Example406_WeirdReaction/","page":"406: 1D Weird Surface Reaction","title":"406: 1D Weird Surface Reaction","text":"module Example406_WeirdReaction\nusing Printf\nusing VoronoiFVM\nusing SparseArrays\nusing ExtendableGrids\nusing GridVisualize\n\nfunction main(; n = 10,\n Plotter = nothing,\n verbose = false,\n tend = 1,\n unknown_storage = :sparse,\n autodetect_sparsity = true)\n h = 1.0 / convert(Float64, n)\n X = collect(0.0:h:1.0)\n N = length(X)\n\n grid = VoronoiFVM.Grid(X)\n # By default, \\Gamma_1 at X[1] and \\Gamma_2 is at X[end]\n\n # Species numbers\n iA = 1\n iB = 2\n iC = 3\n\n # Diffusion flux for species A and B\n D_A = 1.0\n D_B = 1.0e-2\n function flux!(f, u, edge)\n f[iA] = D_A * (u[iA, 1] - u[iA, 2])\n f[iB] = D_B * (u[iB, 1] - u[iB, 2])\n end\n\n # Storage term of species A and B\n function storage!(f, u, node)\n f[iA] = u[iA]\n f[iB] = u[iB]\n end\n\n # Source term for species a around 0.5\n function source!(f, node)\n x1 = node[1] - 0.5\n f[iA] = exp(-100 * x1^2)\n end\n\n # Reaction constants (p = + , m = -)\n # Chosen to prefer path A-> B\n kp_AB = 1.0\n km_AB = 0.1\n\n function breaction!(f, u, node)\n if node.region == 1\n R = kp_AB * exp(u[iC]) * u[iA] - exp(-u[iC]) * km_AB * u[iB]\n f[iA] += R\n f[iB] -= R\n end\n end\n\n # This generic operator works on the full solution seen as linear vector, and indexing\n # into it needs to be performed with the help of idx (defined below for a solution vector)\n # Its sparsity is detected automatically using SparsityDetection.jl\n # Here, we calculate the gradient of u_A at the boundary and store the value in u_C which\n # is then used as a parameter in the boundary reaction\n function generic_operator!(f, u, sys)\n f .= 0\n f[idx[iC, 1]] = u[idx[iC, 1]] +\n 0.1 * (u[idx[iA, 1]] - u[idx[iA, 2]]) / (X[2] - X[1])\n end","category":"page"},{"location":"module_examples/Example406_WeirdReaction/","page":"406: 1D Weird Surface Reaction","title":"406: 1D Weird Surface Reaction","text":"If we know the sparsity pattern, we can here create a sparse matrix with values set to 1 in the nonzero slots. This allows to circumvent the autodetection which may takes some time.","category":"page"},{"location":"module_examples/Example406_WeirdReaction/","page":"406: 1D Weird Surface Reaction","title":"406: 1D Weird Surface Reaction","text":" function generic_operator_sparsity(sys)\n idx = unknown_indices(unknowns(sys))\n sparsity = spzeros(num_dof(sys), num_dof(sys))\n sparsity[idx[iC, 1], idx[iC, 1]] = 1\n sparsity[idx[iC, 1], idx[iA, 1]] = 1\n sparsity[idx[iC, 1], idx[iA, 2]] = 1\n sparsity\n end\n\n if autodetect_sparsity\n physics = VoronoiFVM.Physics(; breaction = breaction!,\n generic = generic_operator!,\n flux = flux!,\n storage = storage!,\n source = source!)\n else\n physics = VoronoiFVM.Physics(; breaction = breaction!,\n generic = generic_operator!,\n generic_sparsity = generic_operator_sparsity,\n flux = flux!,\n storage = storage!,\n source = source!)\n end\n\n sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage)\n\n # Enable species in bulk resp\n enable_species!(sys, iA, [1])\n enable_species!(sys, iB, [1])\n\n # Enable surface species\n enable_boundary_species!(sys, iC, [1])\n\n # Set Dirichlet bc for species B on \\Gamma_2\n boundary_dirichlet!(sys, iB, 2, 0.0)\n\n # Initial values\n U = unknowns(sys)\n U .= 0.0\n idx = unknown_indices(U)\n\n tstep = 0.01\n time = 0.0\n T = Float64[]\n u_C = Float64[]\n\n control = VoronoiFVM.NewtonControl()\n control.verbose = verbose\n p = GridVisualizer(; Plotter = Plotter, layout = (2, 1))\n while time < tend\n time = time + tstep\n U = solve(sys; inival = U, time, tstep, control)\n if verbose\n @printf(\"time=%g\\n\", time)\n end\n # Record boundary pecies\n push!(T, time)\n push!(u_C, U[iC, 1])\n\n scalarplot!(p[1, 1], grid, U[iA, :]; label = \"[A]\",\n title = @sprintf(\"max_A=%.5f max_B=%.5f u_C=%.5f\", maximum(U[iA, :]),\n maximum(U[iB, :]), u_C[end]), color = :red)\n scalarplot!(p[1, 1], grid, U[iB, :]; label = \"[B]\", clear = false, color = :blue)\n scalarplot!(p[2, 1], copy(T), copy(u_C); label = \"[C]\", clear = true, show = true)\n end\n return U[iC, 1]\nend\n\nusing Test\nfunction runtests()\n testval = 0.007027597470502758\n @test main(; unknown_storage = :sparse) ≈ testval &&\n main(; unknown_storage = :dense) ≈ testval &&\n main(; unknown_storage = :sparse, autodetect_sparsity = false) ≈ testval &&\n main(; unknown_storage = :dense, autodetect_sparsity = false) ≈ testval\nend\n\nend","category":"page"},{"location":"module_examples/Example406_WeirdReaction/","page":"406: 1D Weird Surface Reaction","title":"406: 1D Weird Surface Reaction","text":"","category":"page"},{"location":"module_examples/Example406_WeirdReaction/","page":"406: 1D Weird Surface Reaction","title":"406: 1D Weird Surface Reaction","text":"This page was generated using Literate.jl.","category":"page"},{"location":"plutostatichtml_examples/nonlinear-solvers/","page":"Nonlinear solver control","title":"Nonlinear solver control","text":"\n\n\n\n\n

    Nonlinear solver control

    Source

    \n\n\n

    Generally, nonlinear systems in this package are solved using Newton's method. In many cases, the default settings provided by this package work well. However, the convergence of Newton's method is only guaranteed with initial values s7ufficiently close to the exact solution. This notebook describes how change the default settings for the solution of nonlinear problems with VoronoiFVM.jl.

    \n\n\n\n\n
    begin\n    import Pkg as _Pkg\n    haskey(ENV, \"PLUTO_PROJECT\") && _Pkg.activate(ENV[\"PLUTO_PROJECT\"])\n    using Revise\n    using VoronoiFVM\n    using ExtendableGrids\n    using Test\n    using PlutoUI\n    using LinearAlgebra\n    using GridVisualize\n    using CairoMakie\n    CairoMakie.activate!(; type = \"svg\", visible = false)\n    GridVisualize.default_plotter!(CairoMakie)\nend;
    \n\n\n\n

    Define a nonlinear Poisson equation to have an example. Let $Ω=(0,10)$ and define

    $$\\begin{aligned}\n-Δ u + e^u-e^{-u} & = 0 & \\text{in}\\; Ω \\\\\n\tu(0)&=100\\\\\n u(10)&=0\n\\end{aligned}$$

    \n\n
    X = 0:0.001:1
    \n
    0.0:0.001:1.0
    \n\n
    flux(y, u, edge) = y[1] = u[1, 1] - u[1, 2];
    \n\n\n
    function reaction(y, u, node)\n    eplus = exp(u[1])\n    eminus = 1 / eplus\n    y[1] = eplus - eminus\nend
    \n
    reaction (generic function with 1 method)
    \n\n
    function bc(y, u, node)\n    boundary_dirichlet!(y, u, node; region = 1, value = 100)\n    boundary_dirichlet!(y, u, node; region = 2, value = 0.0)\nend;
    \n\n\n
    system = VoronoiFVM.System(X; flux = flux, reaction = reaction, bcondition = bc, species = 1);
    \n\n\n","category":"page"},{"location":"plutostatichtml_examples/nonlinear-solvers/#Solution-using-default-settings","page":"Nonlinear solver control","title":"Solution using default settings","text":"","category":"section"},{"location":"plutostatichtml_examples/nonlinear-solvers/","page":"Nonlinear solver control","title":"Nonlinear solver control","text":"
    \n
    \n\n
    begin\n    sol = solve(system; log = true)\n    hist = history(system)\nend;
    \n\n\n
    scalarplot(system,\n           sol;\n           resolution = (500, 200),\n           xlabel = \"x\",\n           ylable = \"y\",\n           title = \"solution\")
    \n\n\n\n

    With log=true, the solve method in addition to the solution records the solution history which after finished solution can be obtatined as history(system).

    \n\n\n

    The history can be plotted:

    \n\n
    function plothistory(h)\n    scalarplot(1:length(h),\n               h;\n               resolution = (500, 200),\n               yscale = :log,\n               xlabel = \"step\",\n               ylabel = \"||δu||_∞\",\n               title = \"Maximum norm of Newton update\")\nend;
    \n\n\n
    plothistory(hist)
    \n\n\n\n

    History can be summarized:

    \n\n
    summary(hist)
    \n
    (seconds = 6.22, tasm = 4.95, tlinsolve = 0.501, iters = 93, absnorm = 1.6e-12, relnorm = 1.62e-14, roundoff = 1.69e-13, factorizations = 1, liniters = 0)
    \n\n\n

    History can be explored in detail:

    \n\n\n
    93-element Vector{Any}:\n (update = 98.8, contraction = 1.0, round = 426.0)\n (update = 1.0, contraction = 0.0101, round = 0.0222)\n (update = 1.0, contraction = 1.0, round = 0.0223)\n (update = 1.0, contraction = 1.0, round = 0.0225)\n (update = 1.0, contraction = 1.0, round = 0.0227)\n (update = 1.0, contraction = 1.0, round = 0.0229)\n (update = 1.0, contraction = 1.0, round = 0.0231)\n ⋮\n (update = 0.87, contraction = 0.88, round = 0.0247)\n (update = 0.477, contraction = 0.548, round = 0.0167)\n (update = 0.0915, contraction = 0.192, round = 0.00446)\n (update = 0.00266, contraction = 0.029, round = 0.000177)\n (update = 2.22e-6, contraction = 0.000837, round = 1.9e-7)\n (update = 1.6e-12, contraction = 7.21e-7, round = 1.69e-13)
    \n\n\n

    With default solver settings, for this particular problem, Newton's method needs 93 iteration steps.

    \n\n
    check(sol) = isapprox(sum(sol), 2554.7106586964906; rtol = 1.0e-12)
    \n
    check (generic function with 1 method)
    \n\n
    @test check(sol)
    \n
    Test Passed
    \n\n","category":"page"},{"location":"plutostatichtml_examples/nonlinear-solvers/#Damping","page":"Nonlinear solver control","title":"Damping","text":"","category":"section"},{"location":"plutostatichtml_examples/nonlinear-solvers/","page":"Nonlinear solver control","title":"Nonlinear solver control","text":"
    \n
    \n\n\n

    Try to use a damped version of Newton method. The damping scheme is rather simple: an initial damping value damp_initial is increased by a growth factor damp_growth in each iteration until it reaches 1.

    \n\n
    begin\n    sol1 = solve(system; log = true, inival = 1, damp = 0.15, damp_grow = 1.5)\n    hist1 = history(system)\nend
    \n
    28-element NewtonSolverHistory:\n 97.81584868964866\n  9.16276036973695\n  0.9999999998511513\n  0.9999999997401952\n  0.999999999441547\n  0.9999999983981808\n  0.9999999941837444\n  ⋮\n  0.7769510871365354\n  0.50248357049323\n  0.17071846878363076\n  0.015586640226738904\n  0.00011698226343053463\n  6.5218450955795574e-9
    \n\n\n\n\n
    VoronoiFVM.details(hist1)
    \n
    28-element Vector{Any}:\n (update = 97.8, contraction = 1.0, round = 5.29)\n (update = 9.16, contraction = 0.0937, round = 0.0298)\n (update = 1.0, contraction = 0.109, round = 0.0446)\n (update = 1.0, contraction = 1.0, round = 0.0655)\n (update = 1.0, contraction = 1.0, round = 0.0959)\n (update = 1.0, contraction = 1.0, round = 0.123)\n (update = 1.0, contraction = 1.0, round = 0.119)\n ⋮\n (update = 0.777, contraction = 0.85, round = 0.00032)\n (update = 0.502, contraction = 0.647, round = 0.000207)\n (update = 0.171, contraction = 0.34, round = 7.04e-5)\n (update = 0.0156, contraction = 0.0913, round = 6.43e-6)\n (update = 0.000117, contraction = 0.00751, round = 4.83e-8)\n (update = 6.52e-9, contraction = 5.58e-5, round = 2.69e-12)
    \n\n
    summary(hist1)
    \n
    (seconds = 0.117, tasm = 0.071, tlinsolve = 0.00208, iters = 28, absnorm = 6.52e-9, relnorm = 6.67e-11, roundoff = 2.69e-12, factorizations = 1, liniters = 0)
    \n\n\n

    We see that the number of iterations decreased significantly.

    \n\n
    @test check(sol1)
    \n
    Test Passed
    \n\n","category":"page"},{"location":"plutostatichtml_examples/nonlinear-solvers/#Embedding","page":"Nonlinear solver control","title":"Embedding","text":"","category":"section"},{"location":"plutostatichtml_examples/nonlinear-solvers/","page":"Nonlinear solver control","title":"Nonlinear solver control","text":"
    \n
    \n\n\n

    Another possibility is the embedding (or homotopy) via a parameter: start with solving a simple problem and increase the level of complexity by increasing the parameter until the full problem is solved. This process is controlled by the parameters

    • Δp: initial parameter step size

    • Δp_min: minimal parameter step size

    • Δp_max: maximum parameter step size

    • Δp_grow: maximum growth factor

    • Δu_opt: optimal difference of solutions between two embedding steps

    After successful solution of a parameter, the new parameter step size is calculated as Δp_new=min(Δp_max, Δp_grow, Δp*Δu_opt/(|u-u_old|+1.0e-14)) and adjusted to the end of the parameter interval.

    If the solution is unsuccessful, the parameter stepsize is halved and solution is retried, until the minimum step size is reached.

    \n\n
    function pbc(y, u, node)\n    boundary_dirichlet!(y, u, node; region = 1, value = 100 * embedparam(node))\n    boundary_dirichlet!(y, u, node; region = 2, value = 0)\nend;
    \n\n\n
    system2 = VoronoiFVM.System(X;\n                            flux = flux,\n                            reaction = function (y, u, node)\n                                reaction(y, u, node)\n\n                                y[1] = y[1] * embedparam(node)\n                            end,\n                            bcondition = pbc,\n                            species = 1,);
    \n\n\n
    begin\n    sol2 = solve(system2;\n                 inival = 0,\n                 log = true,\n                 embed = (0, 1),\n                 Δp = 0.1,\n                 max_lureuse = 0,\n                 Δp_grow = 1.2,\n                 Δu_opt = 15)\n    history2 = history(system2)\nend
    \n
    9-element TransientSolverHistory:\n [0.0]\n [9.989343055885081, 0.9814815933242419, 0.8053267053699209, 0.34118506593318715, 0.03588566608739014, 0.00030501510006572047, 2.0656145571680413e-8, 3.757192460108166e-15]\n [11.341463937623553, 0.9997315206431724, 0.9990808258671514, 0.9966384496135858, 0.9877041414138585, 0.9368309534589447, 0.7284028335581745, 0.3000679588018341, 0.03462415975336775, 0.0003922413719470201, 5.021311921938722e-8, 1.3746465935025473e-15]\n [1.5085724152572164, 0.4205506561491683, 0.108269451712538, 0.005760884131907183, 1.5240635820497453e-5, 1.0633964457339243e-10]\n [0.32730498752976783, 0.04842614744100337, 0.0010781622274304223, 4.822924090663502e-7, 8.955521318268822e-14]\n [0.18959174175720017, 0.019475257499845283, 0.00017208751333109998, 1.2283470332866745e-8, 2.6864715379034067e-15]\n [0.22151709368816527, 0.01350594149309332, 8.295254108208108e-5, 2.874746673723172e-9, 1.5200200846066591e-15]\n [0.9999839855864412, 0.9999129411082036, 0.9996450908924625, 0.9987144967036693, 0.9956410853148298, 0.9858738195909444, 0.956086313069598, 0.8714057437368012, 0.6663892012877948, 0.32544591325884925, 0.05964842227578665, 0.001663844974785831, 1.2451922222765833e-6, 6.958364377298142e-13]\n [0.9999999999255985, 0.9999999995955114, 0.9999999983507291, 0.9999999940224221, 0.9999999796890736, 0.9999999337470156, 0.9999997898900223, 0.9999993472708903, 0.9999980039123315, 0.9999939712056518  …  0.988838205675924, 0.9682851805189654, 0.9122371945634715, 0.772531338122871, 0.4950582642141573, 0.16481352791232465, 0.014468420882723347, 0.00010072395527063067, 4.834945474226937e-9, 1.0894463251845288e-15]
    \n\n
    summary(history2)
    \n
    (seconds = 0.258, tasm = 0.186, tlinsolve = 0.00497, steps = 9, iters = 81, maxabsnorm = 1.06e-10, maxrelnorm = 7.05e-11, maxroundoff = 2.26e-13, iters_per_step = 10.1, facts_per_step = 0.0, liniters_per_step = 0.0)
    \n\n
    plothistory(vcat(history2[2:end]...))
    \n\n\n
    sol2[end]
    \n
    1×1001 Matrix{Float64}:\n 79.6896  17.8835  14.5192  13.1759  12.3601  …  0.00695716  0.00347857  3.47857e-30
    \n\n
    @test check(sol2[end])
    \n
    Test Passed
    \n\n\n

    For this particular problem, embedding uses less overall Newton steps than the default settings, but the damped method is faster.

    \n\n","category":"page"},{"location":"plutostatichtml_examples/nonlinear-solvers/#Solver-control","page":"Nonlinear solver control","title":"Solver control","text":"","category":"section"},{"location":"plutostatichtml_examples/nonlinear-solvers/","page":"Nonlinear solver control","title":"Nonlinear solver control","text":"
    \n
    \n\n\n

    Here we show the docsctring of SolverControl (formerly NewtonControl). This is a struct which can be passed to the solve method. Alternatively, as shown in this notebook, keyword arguments named like its entries can be passed directly to the solve method.

    \n\n
    @doc VoronoiFVM.SolverControl
    \n
    SolverControl\nSolverControl(;kwargs...)\nSolverControl(linear_solver_strategy, sys; kwargs...)

    Solver control parameter for time stepping, embedding, Newton method and linear solver control. All field names can be used as keyword arguments for solve(system::VoronoiFVM.AbstractSystem; kwargs...)

    Newton's method solves $F(u)=0$ by the iterative procedure $u_{i+1}=u_{i} - d_i F'(u_i)^{-1}F(u_i)$ starting with some initial value $u_0$, where $d_i$ is a damping parameter.

    For linear solver strategies, see VoronoiFVM.LinearSolverStrategy.

    • verbose::Union{Bool, String}: Verbosity control. A collection of output categories is given in a string composed of the following letters:

      • a: allocation warnings

      • d: deprecation warnings

      • e: time/parameter evolution log

      • n: newton solver log

      • l: linear solver log

      Alternatively, a Bool value can be given, resulting in

      • true: \"neda\"

      • false: \"da\"

      Switch off all output including deprecation warnings via verbose=\"\". In the output, corresponding messages are marked e.g. via '[n]', [a] etc. (besides of '[l]')

    • abstol::Float64: Tolerance (in terms of norm of Newton update): terminate if $\\Delta u_i=||u_{i+1}-u_i||_\\infty <$abstol.

    • reltol::Float64: Tolerance (relative to the size of the first update): terminate if $\\Delta u_i/\\Delta u_1<$reltol.

    • maxiters::Int64: Maximum number of newton iterations.

    • tol_round::Float64: Tolerance for roundoff error detection: terminate if $|\\;||u_{i+1}||_1 - ||u_{i}||_1\\;|/ ||u_{i}||_1<$tol_round occurred max_round times in a row.

    • tol_mono::Float64: Tolerance for monotonicity test: terminate with error if $\\Delta u_i/\\Delta u_{i-1}>$1/tol_mono.

    • damp_initial::Float64: Initial damping parameter $d_0$. To handle convergence problems, set this to a value less than 1.

    • damp_growth::Float64: Damping parameter growth factor: $d_{i+1}=\\min(d_i\\cdot$max_growth$,1)$. It should be larger than 1.

    • max_round::Int64: Maximum number of consecutive iterations within roundoff error tolerance The default effectively disables this criterion.

    • unorm::Function: Calculation of Newton update norm

    • rnorm::Function: Functional for roundoff error calculation

    • method_linear::Union{Nothing, LinearSolve.SciMLLinearSolveAlgorithm}: Solver method for linear systems (see LinearSolve.jl). If given nothing, as default are chosen (for Float64 calculations):

      • 1D: KLUFactorization()

      • 2D: SparspakFactorization()

      • 3D: UMFPACKFactorization()

      SparspakFactorization() is the default choice for general number types. Users should experiment with what works best for their problem.

      For easy access to this functionality, see see also VoronoiFVM.LinearSolverStrategy.

    • reltol_linear::Float64: Relative tolerance of iterative linear solver.

    • abstol_linear::Float64: Absolute tolerance of iterative linear solver.

    • maxiters_linear::Int64: Maximum number of iterations of linear solver

    • precon_linear::Union{Nothing, Function, ExtendableSparse.AbstractFactorization, LinearSolve.SciMLLinearSolveAlgorithm, Type}: Constructor for preconditioner for linear systems. This should work as a function precon_linear(A) which takes an AbstractSparseMatrixCSC (e.g. an ExtendableSparseMatrix) and returns a preconditioner object in the sense of LinearSolve.jl, i.e. which has an ldiv!(u,A,v) method. Useful examples:

      • ExtendableSparse.ILUZero

      • ExtendableSparse.Jacobi

      For easy access to this functionality, see see also VoronoiFVM.LinearSolverStrategy.

    • keepcurrent_linear::Bool: Update preconditioner in each Newton step ?

    • Δp::Float64: Initial parameter step for embedding.

    • Δp_max::Float64: Maximal parameter step size.

    • Δp_min::Float64: Minimal parameter step size.

    • Δp_grow::Float64: Maximal parameter step size growth.

    • Δp_decrease::Float64: Parameter step decrease factor upon rejection

    • Δt::Float64: Initial time step size.

    • Δt_max::Float64: Maximal time step size.

    • Δt_min::Float64: Minimal time step size.

    • Δt_grow::Float64: Maximal time step size growth.

    • Δt_decrease::Float64: Time step decrease factor upon rejection

    • Δu_opt::Float64: Optimal size of update for time stepping and embedding. The algorithm tries to keep the difference in norm between \"old\" and \"new\" solutions approximately at this value.

    • Δu_max_factor::Float64: Control maximum sice of update Δu for time stepping and embeding relative to Δu_opt. Time steps with Δu > Δu_max_factor*Δu_opt will be rejected.

    • force_first_step::Bool: Force first timestep.

    • num_final_steps::Int64: Number of final steps to adjust at end of time interval in order to prevent breakdown of step size.

    • handle_exceptions::Bool: Handle exceptions during transient solver and parameter embedding. If true, exceptions in Newton solves are caught, the embedding resp. time step is lowered, and solution is retried. Moreover, if embedding or time stepping fails (e.g. due to reaching minimal step size), a warning is issued, and a solution is returned with all steps calculated so far.

      Otherwise (by default) errors are thrown.

    • store_all::Bool: Store all steps of transient/embedding problem:

    • in_memory::Bool: Store transient/embedding solution in memory

    • log::Any: Record history

    • edge_cutoff::Float64: Edge parameter cutoff for rectangular triangles.

    • pre::Function: Function pre(sol,t) called before time/embedding step

    • post::Function: Function post(sol,oldsol,t,Δt) called after successful time/embedding step

    • sample::Function: Function sample(sol,t) to be called for each t in times[2:end]

    • delta::Function: Time step error estimator. A function Δu=delta(system,u,uold,t,Δt) to calculate Δu.

    • tol_absolute::Union{Nothing, Float64}

    • tol_relative::Union{Nothing, Float64}

    • damp::Union{Nothing, Float64}

    • damp_grow::Union{Nothing, Float64}

    • max_iterations::Union{Nothing, Int64}

    • tol_linear::Union{Nothing, Float64}

    • max_lureuse::Union{Nothing, Int64}

    • mynorm::Union{Nothing, Function}

    • myrnorm::Union{Nothing, Function}

    \n\n\n
    \n\n\n
    \n
    \n

    Built with Julia 1.10.2 and

    \nCairoMakie 0.11.5
    \nExtendableGrids 1.2.3
    \nGridVisualize 1.5.0
    \nPkg 1.10.0
    \nPlutoUI 0.7.55
    \nRevise 3.5.13
    \nVoronoiFVM 1.16.0\n
    \n\n","category":"page"},{"location":"plutostatichtml_examples/nonlinear-solvers/","page":"Nonlinear solver control","title":"Nonlinear solver control","text":"EditURL = \"https://github.com/j-fu/VoronoiFVM.jl/blob/main/nothing\"","category":"page"},{"location":"physics/#Physics-and-special-functions","page":"Physics & special functions","title":"Physics & special functions","text":"","category":"section"},{"location":"physics/#Physics","page":"Physics & special functions","title":"Physics","text":"","category":"section"},{"location":"physics/","page":"Physics & special functions","title":"Physics & special functions","text":"VoronoiFVM.AbstractPhysics\nVoronoiFVM.Physics\nVoronoiFVM.Physics(;kwargs...)\nBase.show(io::IO,physics::VoronoiFVM.AbstractPhysics)","category":"page"},{"location":"physics/#VoronoiFVM.AbstractPhysics","page":"Physics & special functions","title":"VoronoiFVM.AbstractPhysics","text":"abstract type AbstractPhysics\n\nAbstract type for physics.\n\n\n\n\n\n","category":"type"},{"location":"physics/#VoronoiFVM.Physics","page":"Physics & special functions","title":"VoronoiFVM.Physics","text":"struct Physics\n\nPhysics data record with the following fields:\n\nflux::Function: Flux between neighboring control volumes: flux(f,u,edge) or flux(f,u,edge,data) should return in f[i] the flux of species i along the edge joining circumcenters of neighboring control volumes. u is a 2D array such that for species i, u[i,1] and u[i,2] contain the unknown values at the corresponding ends of the edge.\n\nstorage::Function: Storage term (term under time derivative): storage(f,u,node) or storage(f,u,node,data)\nIt should return in f[i] the storage term for the i-th equation. u[i] contains the value of the i-th unknown.\n\nreaction::Function: Reaction term: reaction(f,u,node) or reaction(f,u,node,data)\nIt should return in f[i] the reaction term for the i-th equation. u[i] contains the value of the i-th unknown.\n\nedgereaction::Function: Edge reaction term: edgereaction(f,u,edge) or edgereaction(f,u,edge,data)\nIt should return in f[i] the reaction term for the i-th equation. u[i] contains the value of the i-th unknown. u is a 2D array such that for species i, u[i,1] and u[i,2] contain the unknown values at the corresponding ends of the edge.\n\nsource::Function: Source term: source(f,node) or source(f,node,data).\nIt should return the in f[i] the value of the source term for the i-th equation.\n\nbflux::Function: Flux between neighboring control volumes on the boundary. Called on edges fully adjacent to the boundary: bflux(f,u,bedge) or `bflux(f,u,bedge,data)\n\nbreaction::Function: Boundary reaction term: breaction(f,u,node) or breaction(f,u,node,data) Similar to reaction, but restricted to the inner or outer boundaries.\n\nbsource::Function: Boundary source term: bsource(f,node) or bsource(f,node,data).\nIt should return in f[i] the value of the source term for the i-th equation.\n\nbstorage::Function: Boundary storage term: bstorage(f,u,node) or bstorage(f,u,node,data) Similar to storage, but restricted to the inner or outer boundaries.\n\nboutflow::Function: Outflow boundary term boutflow(f,u,edge) or boutflow(f,u,edge,data) This function is called for edges (including interior ones) which have at least one ode on one of the outflow boundaries. Within this function, outflownode and isoutflownode can be used to identify that node. There is some ambiguity in the case that both nodes are outflow nodes, in that case it is assumed that the contribution is zero.\n\noutflowboundaries::Vector{Int64}: List (Vector) of boundary regions which carry outflow bondary conditions. Influences when boutflow is called.\n\ngeneric_operator::Function: Generic operator generic_operator(f,u,sys). This operator acts on the full solution u of a system. Sparsity is detected automatically unless generic_operator_sparsity is given.\n\ngeneric_operator_sparsity::Function: Function defining the sparsity structure of the generic operator. This should return the sparsity pattern of the generic_operator.\n\ndata::Any: User data (parameters). This allows to pass various parameters to the callback functions.\n\nnum_species::Int8: Number of species including boundary species.\n\n\n\n\n\n","category":"type"},{"location":"physics/#VoronoiFVM.Physics-Tuple{}","page":"Physics & special functions","title":"VoronoiFVM.Physics","text":"Physics(;num_species=0,\n data=nothing,\n flux,\n reaction,\n edgereaction,\n storage,\n source,\n breaction,\n bstorage,\n boutflow,\n outflowboundaries,\n generic,\n generic_sparsity\n )\n\nConstructor for physics data. For the meaning of the optional keyword arguments, see VoronoiFVM.System(grid::ExtendableGrid; kwargs...).\n\n\n\n\n\n","category":"method"},{"location":"physics/#Base.show-Tuple{IO, VoronoiFVM.AbstractPhysics}","page":"Physics & special functions","title":"Base.show","text":"show(io, physics)\n\n\nShow physics object\n\n\n\n\n\n","category":"method"},{"location":"physics/#Edge-and-node-data","page":"Physics & special functions","title":"Edge and node data","text":"","category":"section"},{"location":"physics/","page":"Physics & special functions","title":"Physics & special functions","text":"VoronoiFVM.Node\nVoronoiFVM.BNode\nVoronoiFVM.Edge\nVoronoiFVM.BEdge","category":"page"},{"location":"physics/#VoronoiFVM.Node","page":"Physics & special functions","title":"VoronoiFVM.Node","text":"mutable struct Node{Tc, Tp, Ti} <: VoronoiFVM.AbstractNode{Tc, Tp, Ti}\n\nStructure holding local node information.\n\nindex::Any: Index in grid\n\nregion::Any: Inner region number\n\nnspec::Any: Number of species defined in node\n\nicell::Any: Number of discretization cell the node is invoked from\n\ncoord::Matrix: Grid coordinates\n\ncellnodes::Matrix: Grid cell nodes\n\ncellregions::Vector: Grid cell regions\n\ntime::Float64: System time\n\nembedparam::Float64: Current value of embedding parameter\n\nparams::Vector: parameters\n\nfac::Float64: Form factor\n\n_idx::Any: Local loop index\n\n\n\n\n\n","category":"type"},{"location":"physics/#VoronoiFVM.BNode","page":"Physics & special functions","title":"VoronoiFVM.BNode","text":"mutable struct BNode{Tv, Tc, Tp, Ti} <: VoronoiFVM.AbstractNode{Tc, Tp, Ti}\n\nStructure holding local boundary node information.\n\nindex::Any: Index in grid\n\nibface::Any: BFace number it is called from\n\nibnode::Any: local node number\n\nregion::Any: Boundary region number\n\ncellregions::Vector\nnspec::Any: Number of species defined in node\n\ncoord::Matrix: Grid coordinates\n\nbfacenodes::Matrix\nbfaceregions::Vector\nallcellregions::Vector\nbfacecells::Adjacency\nDirichlet::Any\ntime::Float64: System time\n\nembedparam::Float64: Current value of embedding parameter\n\nparams::Vector\ndirichlet_value::Vector\nfac::Float64\n\n\n\n\n\n","category":"type"},{"location":"physics/#VoronoiFVM.Edge","page":"Physics & special functions","title":"VoronoiFVM.Edge","text":"mutable struct Edge{Tc, Tp, Ti} <: VoronoiFVM.AbstractEdge{Tc, Tp, Ti}\n\nStructure holding local edge information.\n\nindex::Any: Index in grid\n\nnode::Vector: Index\n\nregion::Any: Inner region number corresponding to edge\n\nnspec::Any: Number of species defined in edge\n\nicell::Any: Number of discretization cell the edge is invoked from\n\ncoord::Matrix: Grid coordinates\n\ncellx::Matrix\nedgenodes::Matrix\ncellregions::Vector\nhas_celledges::Bool\ntime::Float64: System time\n\nembedparam::Float64: Current value of embedding parameter\n\nparams::Vector\nfac::Float64: Form factor\n\n_idx::Any: Local loop index\n\noutflownoderegions::Union{Nothing, SparseArrays.SparseMatrixCSC{Bool, Int64}}\noutflownode::Int64: Outflow node\n\n\n\n\n\n","category":"type"},{"location":"physics/#VoronoiFVM.BEdge","page":"Physics & special functions","title":"VoronoiFVM.BEdge","text":"mutable struct BEdge{Tc, Tp, Ti} <: VoronoiFVM.AbstractEdge{Tc, Tp, Ti}\n\nStructure holding local edge information.\n\nindex::Any: Index in grid\n\nnode::Vector: Index\n\nregion::Any: Inner region number corresponding to edge\n\nnspec::Any: Number of species defined in edge\n\nicell::Any: Number of discretization cell the edge is invoked from\n\ncoord::Matrix: Grid coordinates\n\nbedgenodes::Matrix\nbfaceedges::Matrix\nbfaceregions::Vector\ntime::Float64: System time\n\nembedparam::Float64: Current value of embedding parameter\n\nparams::Vector\nfac::Float64\n\n\n\n\n\n","category":"type"},{"location":"physics/#Special-functions","page":"Physics & special functions","title":"Special functions","text":"","category":"section"},{"location":"physics/","page":"Physics & special functions","title":"Physics & special functions","text":"fbernoulli\nfbernoulli_pm\ninplace_linsolve!(A,b)\ninplace_linsolve!(A,b,ipiv)","category":"page"},{"location":"physics/#VoronoiFVM.fbernoulli","page":"Physics & special functions","title":"VoronoiFVM.fbernoulli","text":"fbernoulli(x)\n\n\nBernoulli function B(x)=fracxe^x-1 for exponentially fitted upwinding.\n\nThe name fbernoulli has been chosen to avoid confusion with Bernoulli from JuliaStats/Distributions.jl\n\nReturns a real number containing the result.\n\n\n\n\n\n","category":"function"},{"location":"physics/#VoronoiFVM.fbernoulli_pm","page":"Physics & special functions","title":"VoronoiFVM.fbernoulli_pm","text":"fbernoulli_pm(x)\n\n\nBernoulli function B(x)=fracxe^x-1 for exponentially fitted upwind, joint evaluation for positive and negative argument\n\nUsually, we need B(x) B(-x) together, and it is cheaper to calculate them together.\n\nReturns two real numbers containing the result for argument x and argument -x.\n\nThe error in comparison with the evaluation of the original expression with BigFloat is less than 1.0e-15\n\n\n\n\n\n","category":"function"},{"location":"physics/#VoronoiFVM.inplace_linsolve!-Tuple{Any, Any}","page":"Physics & special functions","title":"VoronoiFVM.inplace_linsolve!","text":"inplace_linsolve!(A, b)\n\n\nNon-allocating, non-pivoting inplace solution of square linear system of equations A*x=b using Doolittle's method.\n\nAfter solution, A will contain the LU factorization, and b the result.\n\nA pivoting version is available with Julia v1.9.\n\n\n\n\n\n","category":"method"},{"location":"physics/#VoronoiFVM.inplace_linsolve!-Tuple{Any, Any, Any}","page":"Physics & special functions","title":"VoronoiFVM.inplace_linsolve!","text":"inplace_linsolve!(A, b, ipiv)\n\n\nNon-allocating, pivoting, inplace solution of square linear system of equations A*x=b using LU factorization from RecursiveFactorizations.jl. \n\nAfter solution, A will contain the LU factorization, and b the result. ipiv must be an Int64 vector of the same length as b.\n\ninfo: Info\nNeeds Julia v1.9 or later\n\n\n\n\n\n","category":"method"},{"location":"module_examples/Example115_HeterogeneousCatalysis1D/#115:-1D-heterogeneous-catalysis","page":"115: 1D heterogeneous catalysis","title":"115: 1D heterogeneous catalysis","text":"","category":"section"},{"location":"module_examples/Example115_HeterogeneousCatalysis1D/","page":"115: 1D heterogeneous catalysis","title":"115: 1D heterogeneous catalysis","text":"(source code)","category":"page"},{"location":"module_examples/Example115_HeterogeneousCatalysis1D/","page":"115: 1D heterogeneous catalysis","title":"115: 1D heterogeneous catalysis","text":"Let Omega=(01), Gamma_1=0, Gamma_2=1 Regard a system of three species: ABC and let u_A=A, u_B=B and u_C=C be their corresponding concentrations.","category":"page"},{"location":"module_examples/Example115_HeterogeneousCatalysis1D/","page":"115: 1D heterogeneous catalysis","title":"115: 1D heterogeneous catalysis","text":"Species A and B exist in the interior of the domain, species C lives a the boundary Gamma_1. We assume a heterogeneous reaction scheme where A reacts to C and C reacts to B:","category":"page"},{"location":"module_examples/Example115_HeterogeneousCatalysis1D/","page":"115: 1D heterogeneous catalysis","title":"115: 1D heterogeneous catalysis","text":"beginaligned\n A leftrightarrow C\n C leftrightarrow B\nendaligned","category":"page"},{"location":"module_examples/Example115_HeterogeneousCatalysis1D/","page":"115: 1D heterogeneous catalysis","title":"115: 1D heterogeneous catalysis","text":"with reaction constants k_AC^pm and k_{BC}^\\pm$.","category":"page"},{"location":"module_examples/Example115_HeterogeneousCatalysis1D/","page":"115: 1D heterogeneous catalysis","title":"115: 1D heterogeneous catalysis","text":"In Omega, both A and B are transported through diffusion:","category":"page"},{"location":"module_examples/Example115_HeterogeneousCatalysis1D/","page":"115: 1D heterogeneous catalysis","title":"115: 1D heterogeneous catalysis","text":"beginaligned\npartial_t u_B - nablacdot D_A nabla u_A = f_A\npartial_t u_B - nablacdot D_B nabla u_B = 0\nendaligned","category":"page"},{"location":"module_examples/Example115_HeterogeneousCatalysis1D/","page":"115: 1D heterogeneous catalysis","title":"115: 1D heterogeneous catalysis","text":"Here, f(x) is a source term creating A. On Gamma_2, we set boundary conditions","category":"page"},{"location":"module_examples/Example115_HeterogeneousCatalysis1D/","page":"115: 1D heterogeneous catalysis","title":"115: 1D heterogeneous catalysis","text":"beginaligned\nD_A nabla u_A = 0\nu_B=0\nendaligned","category":"page"},{"location":"module_examples/Example115_HeterogeneousCatalysis1D/","page":"115: 1D heterogeneous catalysis","title":"115: 1D heterogeneous catalysis","text":"describing no normal flux for A and zero concentration of B. On Gamma_1, we use the mass action law to describe the boundary reaction and the evolution of the boundary concentration C. We assume that there is a limited amount of surface sites S for species C, so in fact A has to react with a free surface site in order to become C which reflected by the factor 1-u_C. The same is true for B.","category":"page"},{"location":"module_examples/Example115_HeterogeneousCatalysis1D/","page":"115: 1D heterogeneous catalysis","title":"115: 1D heterogeneous catalysis","text":"beginaligned\nR_AC(u_A u_C)=k_AC^+ u_A(1-u_C) - k_AC^-u_C\nR_BC(u_C u_B)=k_BC^+ u_B(1-u_C) - k_BC^-u_C\n- D_A nabla u_A + S R_AC(u_A u_C) =0 \n- D_B nabla u_B + S R_BC(u_B u_C) =0 \npartial_t C - R_AC(u_A u_C) - R_BC(u_B u_C) =0\nendaligned","category":"page"},{"location":"module_examples/Example115_HeterogeneousCatalysis1D/","page":"115: 1D heterogeneous catalysis","title":"115: 1D heterogeneous catalysis","text":"module Example115_HeterogeneousCatalysis1D\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\nusing LinearAlgebra\n\nfunction main(; n = 10, Plotter = nothing, verbose = false, tend = 1,\n unknown_storage = :sparse, assembly = :edgewise)\n h = 1.0 / convert(Float64, n)\n X = collect(0.0:h:1.0)\n N = length(X)\n\n grid = VoronoiFVM.Grid(X)\n # By default, \\Gamma_1 at X[1] and \\Gamma_2 is at X[end]\n\n # Species numbers\n iA = 1\n iB = 2\n iC = 3\n\n # Diffusion flux for species A and B\n D_A = 1.0\n D_B = 1.0e-2\n function flux!(f, u, edge)\n f[iA] = D_A * (u[iA, 1] - u[iA, 2])\n f[iB] = D_B * (u[iB, 1] - u[iB, 2])\n end\n\n # Storage term of species A and B\n function storage!(f, u, node)\n f[iA] = u[iA]\n f[iB] = u[iB]\n end\n\n # Source term for species a around 0.5\n function source!(f, node)\n x1 = node[1] - 0.5\n f[iA] = exp(-100 * x1^2)\n end\n\n # Reaction constants (p = + , m = -)\n # Chosen to prefer path A-> C -> B\n # More over, A reacts faster than to C than C to B\n # leading to \"catalyst poisoning\", i.e. C taking up most of the\n # available catalyst sites\n kp_AC = 100.0\n km_AC = 1.0\n\n kp_BC = 0.1\n km_BC = 1.0\n\n S = 0.01\n\n R_AC(u_A, u_C) = kp_AC * u_A * (1 - u_C) - km_AC * u_C\n R_BC(u_B, u_C) = kp_BC * u_B * (1 - u_C) - km_BC * u_C\n\n function breaction!(f, u, node)\n if node.region == 1\n f[iA] = S * R_AC(u[iA], u[iC])\n f[iB] = S * R_BC(u[iB], u[iC])\n f[iC] = -R_BC(u[iB], u[iC]) - R_AC(u[iA], u[iC])\n end\n end\n\n # This is for the term \\partial_t u_C at the boundary\n function bstorage!(f, u, node)\n if node.region == 1\n f[iC] = u[iC]\n end\n end\n\n physics = VoronoiFVM.Physics(; breaction = breaction!,\n bstorage = bstorage!,\n flux = flux!,\n storage = storage!,\n source = source!)\n\n sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage)\n\n # Enable species in bulk resp\n enable_species!(sys, iA, [1])\n enable_species!(sys, iB, [1])\n\n # Enable surface species\n enable_boundary_species!(sys, iC, [1])\n\n # Set Dirichlet bc for species B on \\Gamma_2\n boundary_dirichlet!(sys, iB, 2, 0.0)\n\n # Initial values\n inival = unknowns(sys)\n inival .= 0.0\n U = unknowns(sys)\n\n tstep = 0.01\n time = 0.0\n\n # Data to store surface concentration vs time\n\n p = GridVisualizer(; Plotter = Plotter, layout = (3, 1))\n\n control = fixed_timesteps!(VoronoiFVM.NewtonControl(), tstep)\n tsol = solve(sys; inival, times = [0, tend], control, verbose = verbose)\n\n p = GridVisualizer(; Plotter = Plotter, layout = (3, 1), fast = true)\n for it = 1:length(tsol)\n time = tsol.t[it]\n scalarplot!(p[1, 1], grid, tsol[iA, :, it]; clear = true,\n title = @sprintf(\"[A]: (%.3f,%.3f)\", extrema(tsol[iA, :, it])...))\n scalarplot!(p[2, 1], grid, tsol[iB, :, it]; clear = true,\n title = @sprintf(\"[B]: (%.3f,%.3f)\", extrema(tsol[iB, :, it])...))\n scalarplot!(p[3, 1], tsol.t[1:it], tsol[iC, 1, 1:it]; title = @sprintf(\"[C]\"),\n clear = true, show = true)\n end\n\n return tsol[iC, 1, end]\nend\n\nusing Test\nfunction runtests()\n testval = 0.87544440641274\n @test isapprox(main(; unknown_storage = :sparse, assembly = :edgewise), testval; rtol = 1.0e-12) &&\n isapprox(main(; unknown_storage = :dense, assembly = :edgewise), testval; rtol = 1.0e-12) &&\n isapprox(main(; unknown_storage = :sparse, assembly = :cellwise), testval; rtol = 1.0e-12) &&\n isapprox(main(; unknown_storage = :dense, assembly = :cellwise), testval; rtol = 1.0e-12)\nend\nend","category":"page"},{"location":"module_examples/Example115_HeterogeneousCatalysis1D/","page":"115: 1D heterogeneous catalysis","title":"115: 1D heterogeneous catalysis","text":"","category":"page"},{"location":"module_examples/Example115_HeterogeneousCatalysis1D/","page":"115: 1D heterogeneous catalysis","title":"115: 1D heterogeneous catalysis","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example421_AbstractQuantities_TestFunctions/#421:-Current-Calculation-for-AbstractQuantities","page":"421: Current Calculation for AbstractQuantities","title":"421: Current Calculation for AbstractQuantities","text":"","category":"section"},{"location":"module_examples/Example421_AbstractQuantities_TestFunctions/","page":"421: Current Calculation for AbstractQuantities","title":"421: Current Calculation for AbstractQuantities","text":"(source code)","category":"page"},{"location":"module_examples/Example421_AbstractQuantities_TestFunctions/","page":"421: Current Calculation for AbstractQuantities","title":"421: Current Calculation for AbstractQuantities","text":"Test current calculation for jumping species. Here, we have three cases: a. Problem initialized as usual b. Problem initialized with Continuousquantity c. Problem initialized with Discontinuousquantity with adjusted reaction rate We see that the resulting current coincides for all three cases when adjusting the reaction rate.","category":"page"},{"location":"module_examples/Example421_AbstractQuantities_TestFunctions/","page":"421: Current Calculation for AbstractQuantities","title":"421: Current Calculation for AbstractQuantities","text":"module Example421_AbstractQuantities_TestFunctions\n\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\nusing LinearAlgebra\n\nmutable struct Data\n rate::Float64 # rate which is within DiscontinuousQuantities\n Data() = new()\nend\n\nfunction main(; N = 3, Plotter = nothing, unknown_storage = :sparse, assembly = :edgewise)\n XX = collect(0:0.1:1)\n xcoord = XX\n for i = 1:(N - 1)\n xcoord = glue(xcoord, XX .+ i)\n end\n grid = simplexgrid(xcoord)\n for i = 1:N\n cellmask!(grid, [i - 1], [i], i)\n end\n for i = 1:(N - 1)\n bfacemask!(grid, [i], [i], i + 2)\n end\n\n sysQ = VoronoiFVM.System(grid; unknown_storage = unknown_storage)\n cspec = ContinuousQuantity(sysQ, 1:N; id = 1) # continuous quantity\n dspec = DiscontinuousQuantity(sysQ, 1:N; id = 2) # discontinuous quantity\n\n data = Data()\n rate = 0.0\n data.rate = rate\n\n function fluxQ(f, u, edge, data) # For both quantities, we define simple diffusion fluxes\n f[dspec] = u[dspec, 1] - u[dspec, 2]\n f[cspec] = u[cspec, 1] - u[cspec, 2]\n end\n\n function breactionQ(f, u, bnode, data)\n # Define a thin layer interface condition for `dspec`.\n if bnode.region > 2\n react = (u[dspec, 1] - u[dspec, 2]) / data.rate\n f[dspec, 1] = react\n f[dspec, 2] = -react\n end\n end\n\n physics!(sysQ, VoronoiFVM.Physics(; data = data,\n flux = fluxQ,\n breaction = breactionQ))\n\n ##########################################################\n icc = 1 # for system without AbstractQuantities\n\n function flux!(f, u, edge) # analogous as for other system\n f[icc] = u[icc, 1] - u[icc, 2]\n end","category":"page"},{"location":"module_examples/Example421_AbstractQuantities_TestFunctions/","page":"421: Current Calculation for AbstractQuantities","title":"421: Current Calculation for AbstractQuantities","text":"other system to which we compare current calculation","category":"page"},{"location":"module_examples/Example421_AbstractQuantities_TestFunctions/","page":"421: Current Calculation for AbstractQuantities","title":"421: Current Calculation for AbstractQuantities","text":" sys = VoronoiFVM.System(grid; flux = flux!, species = icc,\n unknown_storage = unknown_storage)\n\n # Set left boundary conditions\n boundary_dirichlet!(sysQ, dspec, 1, 0.0)\n boundary_dirichlet!(sysQ, cspec, 1, 0.0)\n boundary_dirichlet!(sys, icc, 1, 0.0)\n\n subgrids = VoronoiFVM.subgrids(dspec, sysQ)","category":"page"},{"location":"module_examples/Example421_AbstractQuantities_TestFunctions/","page":"421: Current Calculation for AbstractQuantities","title":"421: Current Calculation for AbstractQuantities","text":"solve","category":"page"},{"location":"module_examples/Example421_AbstractQuantities_TestFunctions/","page":"421: Current Calculation for AbstractQuantities","title":"421: Current Calculation for AbstractQuantities","text":" UQ = unknowns(sysQ)\n U = unknowns(sys)\n UQ .= 0.0\n U .= 0.0\n biasval = range(0; stop = 2.0, length = 5)\n\n Icspec = zeros(length(biasval))\n Idspec = zeros(length(biasval))\n Iicc = zeros(length(biasval))\n\n for data.rate in [1.0e2, 1.0e0, 1.0e-2, 1.0e-4, 1.0e-6]\n count = 1\n for Δu in biasval","category":"page"},{"location":"module_examples/Example421_AbstractQuantities_TestFunctions/","page":"421: Current Calculation for AbstractQuantities","title":"421: Current Calculation for AbstractQuantities","text":"first problem","category":"page"},{"location":"module_examples/Example421_AbstractQuantities_TestFunctions/","page":"421: Current Calculation for AbstractQuantities","title":"421: Current Calculation for AbstractQuantities","text":" boundary_dirichlet!(sysQ, dspec, 2, Δu)\n boundary_dirichlet!(sysQ, cspec, 2, Δu)\n\n UQ = solve(sysQ; inival = UQ)\n\n # get current\n factoryQ = TestFunctionFactory(sysQ)\n tfQ = testfunction(factoryQ, [1], [2])\n IQ = integrate(sysQ, tfQ, UQ)\n\n val = 0.0\n for ii in dspec.regionspec # current is calculated regionwise\n val = val + IQ[ii]\n end\n Icspec[count] = IQ[cspec]\n Idspec[count] = val","category":"page"},{"location":"module_examples/Example421_AbstractQuantities_TestFunctions/","page":"421: Current Calculation for AbstractQuantities","title":"421: Current Calculation for AbstractQuantities","text":"second problem","category":"page"},{"location":"module_examples/Example421_AbstractQuantities_TestFunctions/","page":"421: Current Calculation for AbstractQuantities","title":"421: Current Calculation for AbstractQuantities","text":" boundary_dirichlet!(sys, icc, 2, Δu)\n\n U = solve(sys; inival = U)\n\n factory = TestFunctionFactory(sys)\n tf = testfunction(factory, [1], [2])\n I = integrate(sys, tf, U)\n\n Iicc[count] = I[icc]\n\n count = count + 1\n end # bias loop","category":"page"},{"location":"module_examples/Example421_AbstractQuantities_TestFunctions/","page":"421: Current Calculation for AbstractQuantities","title":"421: Current Calculation for AbstractQuantities","text":"plot","category":"page"},{"location":"module_examples/Example421_AbstractQuantities_TestFunctions/","page":"421: Current Calculation for AbstractQuantities","title":"421: Current Calculation for AbstractQuantities","text":" dvws = views(UQ, dspec, subgrids, sysQ)\n cvws = views(UQ, cspec, subgrids, sysQ)\n\n vis = GridVisualizer(; layout = (2, 1), resolution = (600, 300), Plotter = Plotter)\n\n for i in eachindex(dvws)\n scalarplot!(vis[1, 1], subgrids[i], dvws[i]; flimits = (-0.5, 1.5),\n title = @sprintf(\"Solution with rate=%.2f\", data.rate),\n label = \"discont quantity\", clear = false, color = :red)\n scalarplot!(vis[1, 1], subgrids[i], cvws[i]; label = \"cont quantity\",\n clear = false, color = :green)\n end\n scalarplot!(vis[1, 1], grid, U[icc, :]; label = \"without quantity\", clear = false,\n linestyle = :dot, color = :blue)\n\n scalarplot!(vis[2, 1], biasval, Idspec; clear = false,\n title = @sprintf(\"IV with rate=%.2f\", data.rate),\n label = \"discont quantity\", color = :red)\n scalarplot!(vis[2, 1], biasval, Icspec; clear = false, title = \"Current\",\n label = \"cont quantity\", color = :green)\n scalarplot!(vis[2, 1], biasval, Iicc; clear = false, label = \"discont quantity\",\n linestyle = :dot, color = :blue, show = true)\n\n reveal(vis)\n sleep(0.2)\n end # rate loop\n\n errorIV = norm(Idspec - Icspec, 2)\n\n return errorIV\nend\n\nusing Test\nfunction runtests()\n testval = 6.085802139465579e-7\n @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval\nend\n\nend","category":"page"},{"location":"module_examples/Example421_AbstractQuantities_TestFunctions/","page":"421: Current Calculation for AbstractQuantities","title":"421: Current Calculation for AbstractQuantities","text":"","category":"page"},{"location":"module_examples/Example421_AbstractQuantities_TestFunctions/","page":"421: Current Calculation for AbstractQuantities","title":"421: Current Calculation for AbstractQuantities","text":"This page was generated using Literate.jl.","category":"page"},{"location":"post/#Postprocessing","page":"Postprocessing","title":"Postprocessing","text":"","category":"section"},{"location":"post/#Plotting","page":"Postprocessing","title":"Plotting","text":"","category":"section"},{"location":"post/","page":"Postprocessing","title":"Postprocessing","text":"Plotting can be performed using the package GridVisualize.jl. This package extends the API with a couple of methods:","category":"page"},{"location":"post/","page":"Postprocessing","title":"Postprocessing","text":"GridVisualize.gridplot\nGridVisualize.gridplot!\nGridVisualize.scalarplot\nGridVisualize.scalarplot!\nVoronoiFVM.plothistory","category":"page"},{"location":"post/#GridVisualize.gridplot","page":"Postprocessing","title":"GridVisualize.gridplot","text":"gridplot(sys::VoronoiFVM.AbstractSystem; kwargs...) -> Any\n\n\nPlot grid behind system\n\n\n\n\n\n","category":"function"},{"location":"post/#GridVisualize.gridplot!","page":"Postprocessing","title":"GridVisualize.gridplot!","text":"gridplot!(\n vis,\n sys::VoronoiFVM.AbstractSystem;\n kwargs...\n) -> Any\n\n\nPlot grid behind system\n\n\n\n\n\n","category":"function"},{"location":"post/#GridVisualize.scalarplot","page":"Postprocessing","title":"GridVisualize.scalarplot","text":"scalarplot(\n sys::VoronoiFVM.AbstractSystem,\n sol::AbstractMatrix;\n species,\n scale,\n kwargs...\n) -> Any\n\n\nPlot one species from solution\n\n\n\n\n\nscalarplot(\n sys::VoronoiFVM.AbstractSystem,\n sol::VoronoiFVM.AbstractTransientSolution;\n species,\n scale,\n tscale,\n kwargs...\n) -> Any\n\n\nPlot one species from transient solution\n\n\n\n\n\n","category":"function"},{"location":"post/#GridVisualize.scalarplot!","page":"Postprocessing","title":"GridVisualize.scalarplot!","text":"scalarplot!(\n vis,\n sys::VoronoiFVM.AbstractSystem,\n sol::AbstractMatrix;\n species,\n scale,\n kwargs...\n) -> Any\n\n\nPlot one species from solution\n\n\n\n\n\nscalarplot!(\n vis,\n sys::VoronoiFVM.AbstractSystem,\n sol::VoronoiFVM.AbstractTransientSolution;\n species,\n scale,\n tscale,\n tlabel,\n kwargs...\n) -> Any\n\n\nPlot one species from transient solution\n\n\n\n\n\n","category":"function"},{"location":"post/#VoronoiFVM.plothistory","page":"Postprocessing","title":"VoronoiFVM.plothistory","text":"plothistory(tsol; \n plots=[:timestepsizes,\n :timestepupdates,\n :newtonsteps,\n :newtonupdates], \n size=(700,600), \n logmin=1.0e-20,\n Plotter=GridVisualize.default_plotter(),\n kwargs...)\n\nPlot solution history stored in tsol. The plots argument allows to choose which plots are shown.\n\n\n\n\n\n","category":"function"},{"location":"post/#Norms-and-volumes","page":"Postprocessing","title":"Norms & volumes","text":"","category":"section"},{"location":"post/","page":"Postprocessing","title":"Postprocessing","text":"LinearAlgebra.norm\nlpnorm\nl2norm\nw1pseminorm\nh1seminorm\nw1pnorm\nh1norm\nlpw1pseminorm\nl2h1seminorm\nlpw1pnorm\nl2h1norm\nnodevolumes","category":"page"},{"location":"post/#LinearAlgebra.norm","page":"Postprocessing","title":"LinearAlgebra.norm","text":"norm(system, u)\nnorm(system, u, p)\n\n\nCalculate Euklidean norm of the degree of freedom vector.\n\n\n\n\n\n","category":"function"},{"location":"post/#VoronoiFVM.lpnorm","page":"Postprocessing","title":"VoronoiFVM.lpnorm","text":"lpnorm(sys, u, p)\nlpnorm(sys, u, p, species_weights)\n\n\nCalculate weighted discrete L^p norm of a solution vector.\n\n\n\n\n\n","category":"function"},{"location":"post/#VoronoiFVM.l2norm","page":"Postprocessing","title":"VoronoiFVM.l2norm","text":"l2norm(sys, u)\nl2norm(sys, u, species_weights)\n\n\nCalculate weigthed discrete L^2(Omega) norm of a solution vector. \n\n\n\n\n\n","category":"function"},{"location":"post/#VoronoiFVM.w1pseminorm","page":"Postprocessing","title":"VoronoiFVM.w1pseminorm","text":"w1pseminorm(sys, u, p)\nw1pseminorm(sys, u, p, species_weights)\n\n\nCalculate weighted discrete W^1p(Omega) seminorm of a solution vector.\n\n\n\n\n\n","category":"function"},{"location":"post/#VoronoiFVM.h1seminorm","page":"Postprocessing","title":"VoronoiFVM.h1seminorm","text":"h1seminorm(sys, u)\nh1seminorm(sys, u, species_weights)\n\n\nCalculate weighted discrete H^1(Omega) seminorm of a solution vector.\n\n\n\n\n\n","category":"function"},{"location":"post/#VoronoiFVM.w1pnorm","page":"Postprocessing","title":"VoronoiFVM.w1pnorm","text":"w1pnorm(sys, u, p)\nw1pnorm(sys, u, p, species_weights)\n\n\nCalculate weighted discrete W^1p(Omega) norm of a solution vector.\n\n\n\n\n\n","category":"function"},{"location":"post/#VoronoiFVM.h1norm","page":"Postprocessing","title":"VoronoiFVM.h1norm","text":"h1norm(sys, u)\nh1norm(sys, u, species_weights)\n\n\nCalculate weighted discrete H^1(Omega) norm of a solution vector.\n\n\n\n\n\n","category":"function"},{"location":"post/#VoronoiFVM.lpw1pseminorm","page":"Postprocessing","title":"VoronoiFVM.lpw1pseminorm","text":"lpw1pseminorm(sys, u, p)\nlpw1pseminorm(sys, u, p, species_weights)\n\n\nCalculate weighted discrete L^p(0TW^1p(Omega)) seminorm of a transient solution.\n\n\n\n\n\n","category":"function"},{"location":"post/#VoronoiFVM.l2h1seminorm","page":"Postprocessing","title":"VoronoiFVM.l2h1seminorm","text":"l2h1seminorm(sys, u)\nl2h1seminorm(sys, u, species_weights)\n\n\nCalculate weighted discrete L^2(0TH^1(Omega)) seminorm of a transient solution.\n\n\n\n\n\n","category":"function"},{"location":"post/#VoronoiFVM.lpw1pnorm","page":"Postprocessing","title":"VoronoiFVM.lpw1pnorm","text":"lpw1pnorm(sys, u, p)\nlpw1pnorm(sys, u, p, species_weights)\n\n\nCalculate weighted discrete L^p(0TW^1p(Omega)) norm of a transient solution.\n\n\n\n\n\n","category":"function"},{"location":"post/#VoronoiFVM.l2h1norm","page":"Postprocessing","title":"VoronoiFVM.l2h1norm","text":"l2h1norm(sys, u)\nl2h1norm(sys, u, species_weights)\n\n\nCalculate weighted discrete L^2(0TH^1(Omega)) norm of a transient solution.\n\n\n\n\n\n","category":"function"},{"location":"post/#VoronoiFVM.nodevolumes","page":"Postprocessing","title":"VoronoiFVM.nodevolumes","text":"nodevolumes(system)\n\n\nCalculate volumes of Voronoi cells.\n\n\n\n\n\n","category":"function"},{"location":"post/#Solution-integrals","page":"Postprocessing","title":"Solution integrals","text":"","category":"section"},{"location":"post/","page":"Postprocessing","title":"Postprocessing","text":"integrate(system::VoronoiFVM.AbstractSystem, F::Function, U::AbstractMatrix; boundary = false)\nVoronoiFVM.edgeintegrate","category":"page"},{"location":"post/#VoronoiFVM.integrate-Tuple{VoronoiFVM.AbstractSystem, Function, AbstractMatrix}","page":"Postprocessing","title":"VoronoiFVM.integrate","text":"integrate(system,F,U; boundary=false)\n\nIntegrate node function (same signature as reaction or storage) F of solution vector region-wise over domain or boundary. The result is an nspec x nregion vector.\n\n\n\n\n\n","category":"method"},{"location":"post/#VoronoiFVM.edgeintegrate","page":"Postprocessing","title":"VoronoiFVM.edgeintegrate","text":"edgeintegrate(system,F,U; boundary=false)\n\nIntegrate edge function (same signature as flux function) F of solution vector region-wise over domain or boundary. The result is an nspec x nregion vector.\n\n\n\n\n\n","category":"function"},{"location":"post/#Nodal-flux-reconstruction","page":"Postprocessing","title":"Nodal flux reconstruction","text":"","category":"section"},{"location":"post/","page":"Postprocessing","title":"Postprocessing","text":"nodeflux","category":"page"},{"location":"post/#VoronoiFVM.nodeflux","page":"Postprocessing","title":"VoronoiFVM.nodeflux","text":"nodeflux(system, U)\n\n\nReconstruction of edge flux as vector function on the nodes of the triangulation. The result can be seen as a piecewiesw linear vector function in the FEM space spanned by the discretization nodes exhibiting the flux density. \n\nThe reconstruction is based on the \"magic formula\" R. Eymard, T. Gallouet, R. Herbin, IMA Journal of Numerical Analysis (2006) 26, 326−353, Lemma 2.4 .\n\nThe return value is a dim x nspec x nnodes vector. The flux of species i can e.g. plotted via GridVisualize.vectorplot.\n\nExample:\n\n ispec=3\n vis=GridVisualizer(Plotter=Plotter)\n scalarplot!(vis,grid,solution[ispec,:],clear=true,colormap=:summer)\n vectorplot!(vis,grid,nf[:,ispec,:],clear=false)\n reveal(vis)\n\nCAVEAT: there is a possible unsolved problem with the values at domain corners in the code. Please see any potential boundary artifacts as a manifestation of this issue and report them.\n\n\n\n\n\n","category":"function"},{"location":"post/#Boundary-flux-calculation","page":"Postprocessing","title":"Boundary flux calculation","text":"","category":"section"},{"location":"post/","page":"Postprocessing","title":"Postprocessing","text":"Modules = [VoronoiFVM]\nPages = [\"vfvm_testfunctions.jl\"]","category":"page"},{"location":"post/#VoronoiFVM.TestFunctionFactory","page":"Postprocessing","title":"VoronoiFVM.TestFunctionFactory","text":"mutable struct TestFunctionFactory\n\nData structure containing DenseSystem used to calculate test functions for boundary flux calculations.\n\nsystem::VoronoiFVM.AbstractSystem: Original system\n\ntfsystem::VoronoiFVM.System{Tv, Tc, Ti, Tm, Matrix{Ti}, Matrix{Tv}} where {Tv, Tc, Ti, Tm}: Test function system\n\ncontrol::SolverControl: Solver control\n\n\n\n\n\n","category":"type"},{"location":"post/#VoronoiFVM.TestFunctionFactory-Tuple{VoronoiFVM.AbstractSystem}","page":"Postprocessing","title":"VoronoiFVM.TestFunctionFactory","text":"TestFunctionFactory(\n system::VoronoiFVM.AbstractSystem;\n control\n) -> TestFunctionFactory\n\n\nConstructor for TestFunctionFactory from System\n\n\n\n\n\n","category":"method"},{"location":"post/#VoronoiFVM.integrate-Union{Tuple{Tv}, Tuple{Tu}, Tuple{VoronoiFVM.AbstractSystem, Vector{Tv}, AbstractMatrix{Tu}}} where {Tu, Tv}","page":"Postprocessing","title":"VoronoiFVM.integrate","text":"integrate(system, tf, U; kwargs...)\n\n\nCalculate test function integral for steady state solution.\n\n\n\n\n\n","category":"method"},{"location":"post/#VoronoiFVM.integrate-Union{Tuple{Tv}, Tuple{VoronoiFVM.AbstractSystem, Any, AbstractMatrix{Tv}, AbstractMatrix{Tv}, Any}} where Tv","page":"Postprocessing","title":"VoronoiFVM.integrate","text":"integrate(system, tf, U, Uold, tstep; params)\n\n\nCalculate test function integral for transient solution.\n\n\n\n\n\n","category":"method"},{"location":"post/#VoronoiFVM.integrate_stdy-Union{Tuple{Tv}, Tuple{Tu}, Tuple{VoronoiFVM.AbstractSystem, Vector{Tv}, AbstractMatrix{Tu}}} where {Tu, Tv}","page":"Postprocessing","title":"VoronoiFVM.integrate_stdy","text":"integrate_stdy(system, tf, U)\n\n\nSteady state part of test function integral.\n\n\n\n\n\n","category":"method"},{"location":"post/#VoronoiFVM.integrate_tran-Union{Tuple{Tv}, Tuple{Tu}, Tuple{VoronoiFVM.AbstractSystem, Vector{Tv}, AbstractMatrix{Tu}}} where {Tu, Tv}","page":"Postprocessing","title":"VoronoiFVM.integrate_tran","text":"integrate_tran(system, tf, U)\n\n\nCalculate transient part of test function integral.\n\n\n\n\n\n","category":"method"},{"location":"post/#VoronoiFVM.testfunction-Tuple{TestFunctionFactory, Any, Any}","page":"Postprocessing","title":"VoronoiFVM.testfunction","text":"testfunction(factory::TestFunctionFactory, bc0, bc1) -> Any\n\n\nCreate testfunction which has Dirichlet zero boundary conditions for boundary regions in bc0 and Dirichlet one boundary conditions for boundary regions in bc1.\n\n\n\n\n\n","category":"method"},{"location":"post/#Impedance-calculatiom","page":"Postprocessing","title":"Impedance calculatiom","text":"","category":"section"},{"location":"post/","page":"Postprocessing","title":"Postprocessing","text":"Impedance calculation can be seen as a postprocessing step after the solution of the unexcited stationary system.","category":"page"},{"location":"post/","page":"Postprocessing","title":"Postprocessing","text":"Modules = [VoronoiFVM]\nPages = [\"vfvm_impedance.jl\"]","category":"page"},{"location":"post/#VoronoiFVM.AbstractImpedanceSystem","page":"Postprocessing","title":"VoronoiFVM.AbstractImpedanceSystem","text":"abstract type AbstractImpedanceSystem{Tv<:Number}\n\nAbstract type for impedance system.\n\n\n\n\n\n","category":"type"},{"location":"post/#VoronoiFVM.ImpedanceSystem","page":"Postprocessing","title":"VoronoiFVM.ImpedanceSystem","text":"mutable struct ImpedanceSystem{Tv} <: VoronoiFVM.AbstractImpedanceSystem{Tv}\n\nConcrete type for impedance system.\n\nsysnzval::AbstractArray{Complex{Tv}, 1} where Tv: Nonzero pattern of time domain system matrix\n\nstorderiv::AbstractMatrix: Derivative of storage term\n\nmatrix::AbstractArray{Complex{Tv}, 2} where Tv: Complex matrix of impedance system\n\nF::AbstractArray{Complex{Tv}, 2} where Tv: Right hand side of impedance system\n\nU0::AbstractMatrix: Stationary state\n\n\n\n\n\n","category":"type"},{"location":"post/#VoronoiFVM.ImpedanceSystem-Union{Tuple{Ti}, Tuple{Tc}, Tuple{Tv}, Tuple{VoronoiFVM.AbstractSystem{Tv, Tc, Ti}, AbstractMatrix, Any, Any}} where {Tv, Tc, Ti}","page":"Postprocessing","title":"VoronoiFVM.ImpedanceSystem","text":"ImpedanceSystem(system, U0, excited_spec, excited_bc)\n\n\nConstruct impedance system from time domain system sys and steady state solution U0 under the assumption of a periodic perturbation of species excited_spec at boundary excited_bc.\n\n\n\n\n\n","category":"method"},{"location":"post/#VoronoiFVM.ImpedanceSystem-Union{Tuple{Ti}, Tuple{Tc}, Tuple{Tv}, Tuple{VoronoiFVM.AbstractSystem{Tv, Tc, Ti}, AbstractMatrix}} where {Tv, Tc, Ti}","page":"Postprocessing","title":"VoronoiFVM.ImpedanceSystem","text":"ImpedanceSystem(system, U0; λ0)\n\n\nConstruct impedance system from time domain system sys and steady state solution U0\n\n\n\n\n\n","category":"method"},{"location":"post/#CommonSolve.solve!-Union{Tuple{Tv}, Tuple{AbstractArray{Complex{Tv}, 2}, VoronoiFVM.ImpedanceSystem{Tv}, Any}} where Tv","page":"Postprocessing","title":"CommonSolve.solve!","text":"solve!(UZ, impedance_system, ω)\n\n\nSolve the impedance system for given frequency ω.\n\n\n\n\n\n","category":"method"},{"location":"post/#VoronoiFVM.impedance-Tuple{VoronoiFVM.ImpedanceSystem, Vararg{Any, 4}}","page":"Postprocessing","title":"VoronoiFVM.impedance","text":"impedance(impedance_system,ω, U0 ,\n excited_spec, excited_bc, excited_bcval,\n dmeas_stdy,\n dmeas_tran \n )\n \n\nCalculate impedance.\n\nω: frequency \nU0: steady state slution\ndmeas_stdy: Derivative of steady state part of measurement functional\ndmeas_tran Derivative of transient part of the measurement functional\n\n\n\n\n\n","category":"method"},{"location":"post/#VoronoiFVM.measurement_derivative-Tuple{VoronoiFVM.AbstractSystem, Any, Any}","page":"Postprocessing","title":"VoronoiFVM.measurement_derivative","text":"measurement_derivative(system, measurement_functional, U0)\n\n\nCalculate the derivative of the scalar measurement functional at steady state U0\n\nUsually, this functional is a test function integral. Initially, we assume that its value depends on all unknowns of the system.\n\n\n\n\n\n","category":"method"},{"location":"post/#VoronoiFVM.unknowns-Union{Tuple{VoronoiFVM.ImpedanceSystem{Tv}}, Tuple{Tv}} where Tv","page":"Postprocessing","title":"VoronoiFVM.unknowns","text":"unknowns(impedance_system)\n\n\nCreate a vector of unknowns of the impedance system\n\n\n\n\n\n","category":"method"},{"location":"module_examples/Example125_TestFunctions1D/#125:-Terminal-flux-calculation-via-test-functions","page":"125: Terminal flux calculation via test functions","title":"125: Terminal flux calculation via test functions","text":"","category":"section"},{"location":"module_examples/Example125_TestFunctions1D/","page":"125: Terminal flux calculation via test functions","title":"125: Terminal flux calculation via test functions","text":"(source code)","category":"page"},{"location":"module_examples/Example125_TestFunctions1D/","page":"125: Terminal flux calculation via test functions","title":"125: Terminal flux calculation via test functions","text":"For a rather comprehensive explanation see 225: Terminal flux calculation via test functions, nD","category":"page"},{"location":"module_examples/Example125_TestFunctions1D/","page":"125: Terminal flux calculation via test functions","title":"125: Terminal flux calculation via test functions","text":"module Example125_TestFunctions1D\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\n\nfunction main(; n = 100, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise)\n h = 1 / n\n grid = VoronoiFVM.Grid(collect(0:h:1))\n\n eps::Vector{Float64} = [1, 1.0e-1]\n\n physics = VoronoiFVM.Physics(\n ; reaction = function (f, u, node)\n f[1] = 10 * (u[1] - u[2])\n f[2] = 10 * (u[2] - u[1])\n end, flux = function (f, u, edge)\n f[1] = eps[1] * (u[1, 1] - u[1, 2])\n f[2] = eps[2] * (u[2, 1] - u[2, 2])\n end, storage = function (f, u, node)\n f[1] = u[1]\n f[2] = u[2]\n end)\n sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly)\n\n enable_species!(sys, 1, [1])\n enable_species!(sys, 2, [1])\n\n boundary_neumann!(sys, 1, 1, 0.01)\n boundary_dirichlet!(sys, 2, 2, 0.0)\n\n factory = TestFunctionFactory(sys)\n tf1 = testfunction(factory, [2], [1])\n tf2 = testfunction(factory, [1], [2])\n\n inival = unknowns(sys)\n inival[2, :] .= 0.1\n inival[1, :] .= 0.1\n\n control = VoronoiFVM.NewtonControl()\n control.verbose = verbose\n control.damp_initial = 0.1\n I1 = 0\n p = GridVisualizer(; Plotter = Plotter, layout = (2, 1))\n for xeps in [1.0, 0.1, 0.01]\n eps = [xeps, xeps]\n U = solve(sys; inival, control)\n I1 = integrate(sys, tf1, U)\n coord = coordinates(grid)\n inival .= U\n scalarplot!(p[1, 1], grid, U[1, :])\n scalarplot!(p[2, 1], grid, U[2, :])\n reveal(p)\n u5 = U[5]\n end\n return I1[1]\nend\n\nusing Test\nfunction runtests()\n testval = 0.01\n @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval\nend\nend","category":"page"},{"location":"module_examples/Example125_TestFunctions1D/","page":"125: Terminal flux calculation via test functions","title":"125: Terminal flux calculation via test functions","text":"","category":"page"},{"location":"module_examples/Example125_TestFunctions1D/","page":"125: Terminal flux calculation via test functions","title":"125: Terminal flux calculation via test functions","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example410_ManySpecies/#410:-Many-Species","page":"410: Many Species","title":"410: Many Species","text":"","category":"section"},{"location":"module_examples/Example410_ManySpecies/","page":"410: Many Species","title":"410: Many Species","text":"(source code)","category":"page"},{"location":"module_examples/Example410_ManySpecies/","page":"410: Many Species","title":"410: Many Species","text":"Test stationary diffusion for 50 species.","category":"page"},{"location":"module_examples/Example410_ManySpecies/","page":"410: Many Species","title":"410: Many Species","text":"module Example410_ManySpecies\nusing Printf\nusing VoronoiFVM\nusing SparseArrays\nusing ExtendableGrids\nusing GridVisualize\nusing LinearAlgebra\n\nfunction main(; n = 11, nspec = 50, Plotter = nothing, unknown_storage = :dense, assembly = :edgewise)\n grid = simplexgrid(range(0, 1; length = n))\n\n function flux(f, u, edge)\n for ispec = 1:nspec\n f[ispec] = u[ispec, 1] - u[ispec, 2]\n end\n end\n physics = VoronoiFVM.Physics(; flux = flux)\n sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly)\n for ispec = 1:nspec\n enable_species!(sys, ispec, [1])\n boundary_dirichlet!(sys, ispec, 1, 0)\n boundary_dirichlet!(sys, ispec, 2, 1)\n end\n sol = solve(sys)\n norm(sol)\nend\n\nusing Test\nfunction runtests()\n testval = 13.874436925511608\n @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval\nend\n\nend","category":"page"},{"location":"module_examples/Example410_ManySpecies/","page":"410: Many Species","title":"410: Many Species","text":"","category":"page"},{"location":"module_examples/Example410_ManySpecies/","page":"410: Many Species","title":"410: Many Species","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example203_CoordinateSystems/#203:-Various-coordinate-systems","page":"203: Various coordinate systems","title":"203: Various coordinate systems","text":"","category":"section"},{"location":"module_examples/Example203_CoordinateSystems/","page":"203: Various coordinate systems","title":"203: Various coordinate systems","text":"(source code)","category":"page"},{"location":"module_examples/Example203_CoordinateSystems/","page":"203: Various coordinate systems","title":"203: Various coordinate systems","text":"module Example203_CoordinateSystems\n\nusing VoronoiFVM\nusing LinearAlgebra\nusing ExtendableGrids\nusing GridVisualize\n\nfunction plot(grid, numerical, exact, Plotter)\n vis = GridVisualizer(; Plotter = Plotter, layout = (2, 1))\n scalarplot!(vis[1, 1], grid, numerical[1, :]; title = \"numerical\")\n scalarplot!(vis[2, 1], grid, exact; title = \"exact\", show = true)\nend\n\nfunction flux(f, u, edge)\n f[1] = u[1, 1] - u[1, 2]\nend\n\n\"\"\"\n symlapdisk(r,r2)\n\nExact solution of homogeneous Dirichlet problem `-Δu=1` on disk of radius r2.\n\"\"\"\nsymlapdisk(r, r2) = 0.25 * (r2^2 - r^2)\n\n\"\"\"\n maindisk(;nref=0, r2=5.0, Plotter=nothing)\n\nSolve homogeneuous Dirichlet problem `-Δu=1`\non disk of radius r2, exact solution is `(r_2^2-r^2)/4`.\n\nIn this case, the discretization appears to be exact.\n\"\"\"\nfunction maindisk(; nref = 0, r2 = 5.0, Plotter = nothing, assembly = :edgewise)\n h = 0.1 * 2.0^(-nref)\n R = collect(0:h:r2)\n grid = VoronoiFVM.Grid(R)\n circular_symmetric!(grid)\n source(f, node) = f[1] = 1.0\n sys = VoronoiFVM.System(grid; source, flux, species = [1], assembly = assembly)\n boundary_dirichlet!(sys; species = 1, region = 2, value = 0.0)\n sol = solve(sys)\n exact = symlapdisk.(coordinates(grid)[1, :], r2)\n plot(grid, sol, exact, Plotter)\n norm(sol[1, :] - exact, Inf) < 1.0e-14\nend\n\n\"\"\"\n maincylinder(;nref=0, r2=5.0, z1=0, z2=1, Plotter=nothing)\n\nSolve homogeneuous Dirichlet problem `-Δu=1`\non disk of radius r2, exact solution is `(r_2^2-r^2)/4`.\n\nIn this case, the discretization appears to be exact.\n\"\"\"\nfunction maincylinder(;\n nref = 0,\n r2 = 5.0,\n z1 = 0.0,\n z2 = 1.0,\n Plotter = nothing,\n assembly = :edgewise,)\n h = 0.1 * 2.0^(-nref)\n R = collect(0:h:r2)\n Z = collect(z1:h:z2)\n grid = VoronoiFVM.Grid(R, Z)\n circular_symmetric!(grid)\n source(f, node) = f[1] = 1.0\n sys = VoronoiFVM.System(grid; source, flux, species = [1], assembly = assembly)\n boundary_dirichlet!(sys; species = 1, region = 2, value = 0.0)\n sol = solve(sys)\n exact = symlapdisk.(coordinates(grid)[1, :], r2)\n plot(grid, sol, exact, Plotter)\n norm(sol[1, :] - exact, Inf) < 1.0e-14\nend\n\n\"\"\"\n maincylinder_unstruct(;Plotter=nothing)\n\nSolve homogeneuous Dirichlet problem `-Δu=1`\non disk of radius r2, exact solution is `(r_2^2-r^2)/4`.\n\nIn this case, the discretization appears to be exact.\n\"\"\"\nfunction maincylinder_unstruct(;\n Plotter = nothing,\n assembly = :edgewise)\n if VERSION < v\"1.7\"","category":"page"},{"location":"module_examples/Example203_CoordinateSystems/","page":"203: Various coordinate systems","title":"203: Various coordinate systems","text":"no pkdir","category":"page"},{"location":"module_examples/Example203_CoordinateSystems/","page":"203: Various coordinate systems","title":"203: Various coordinate systems","text":" return true\n end\n nref = 0\n r2 = 5.0\n z1 = 0.0\n z2 = 1.0\n h = 0.1 * 2.0^(-nref)\n grid = simplexgrid(joinpath(pkgdir(VoronoiFVM), \"assets\", \"cyl_unstruct.sg\"))\n circular_symmetric!(grid)\n source(f, node) = f[1] = 1.0\n sys = VoronoiFVM.System(grid; source, flux, species = [1], assembly = assembly)\n boundary_dirichlet!(sys; species = 1, region = 2, value = 0.0)\n sol = solve(sys)\n exact = symlapdisk.(coordinates(grid)[1, :], r2)\n plot(grid, sol, exact, Plotter)\n norm(sol[1, :] - exact, Inf) < 0.0012\nend\n\n\"\"\"\n symlapring(r,r1,r2)\n\nExact solution of Dirichlet problem `-Δu=0` on ring between radii r1 and r2,\nwith boundary value 1 at r1 and 0 at r2.\n\"\"\"\nsymlapring(r, r1, r2) = (log(r) - log(r2)) / (log(r1) - log(r2))\n\n\"\"\"\n mainring(;nref=0, r2=5.0, Plotter=nothing)\n\nof Dirichlet problem `-Δu=0` on ring between radii r1 and r2,\nwith boundary value 1 at r1 and 0 at r2. Test of quadratic convergence.\n\"\"\"\nfunction mainring(; nref = 0, r1 = 1.0, r2 = 5.0, Plotter = nothing, assembly = :edgewise)\n h = 0.1 * 2.0^(-nref)\n R = collect(r1:h:r2)\n grid = VoronoiFVM.Grid(R)\n circular_symmetric!(grid)\n source(f, node) = f[1] = 0.0\n sys = VoronoiFVM.System(grid; source, flux, species = [1], assembly = assembly)\n boundary_dirichlet!(sys; species = 1, region = 1, value = 1.0)\n boundary_dirichlet!(sys; species = 1, region = 2, value = 0.0)\n sol = solve(sys)\n exact = symlapring.(coordinates(grid)[1, :], r1, r2)\n plot(grid, sol, exact, Plotter)\n norm(sol[1, :] - exact, Inf) / h^2 < 0.01\nend\n\n\"\"\"\n maincylindershell(;nref=0, r2=5.0, z1=0.0, z2=1.0, Plotter=nothing)\n\nof Dirichlet problem `-Δu=0` on cylindershell between radii r1 and r2,\nwith boundary value 1 at r1 and 0 at r2. Test of quadratic convergence.\n\"\"\"\nfunction maincylindershell(;\n nref = 0,\n r1 = 1.0,\n r2 = 5.0,\n z1 = 0.0,\n z2 = 1.0,\n Plotter = nothing,\n assembly = :edgewise,)\n h = 0.1 * 2.0^(-nref)\n R = collect(r1:h:r2)\n Z = collect(z1:h:z2)\n grid = VoronoiFVM.Grid(R, Z)\n circular_symmetric!(grid)\n source(f, node) = f[1] = 0.0\n sys = VoronoiFVM.System(grid; source, flux, species = [1], assembly = assembly)\n boundary_dirichlet!(sys; species = 1, region = 4, value = 1.0)\n boundary_dirichlet!(sys; species = 1, region = 2, value = 0.0)\n sol = solve(sys)\n exact = symlapring.(coordinates(grid)[1, :], r1, r2)\n plot(grid, sol, exact, Plotter)\n norm(sol[1, :] - exact, Inf) / h^2 < 0.01\nend\n\n\"\"\"\n symlapsphere(r,r2)\n\nExact solution of homogeneous Dirichlet problem `-Δu=1` on sphere of radius r2.\n\"\"\"\nsymlapsphere(r, r2) = (r2^2 - r^2) / 6.0\n\n\"\"\"\n mainsphere(;nref=0, r2=5.0, Plotter=nothing)\n\nSolve homogeneuous Dirichlet problem `-Δu=1`\non sphere of radius r2, exact solution is `(r_2^2-r^2)/4`.\n\nIn this case, the discretization appears to be exact.\n\"\"\"\nfunction mainsphere(; nref = 0, r2 = 5.0, Plotter = nothing, assembly = :edgewise)\n h = 0.1 * 2.0^(-nref)\n R = collect(0:h:r2)\n grid = VoronoiFVM.Grid(R)\n spherical_symmetric!(grid)\n source(f, node) = f[1] = 1.0\n sys = VoronoiFVM.System(grid; source, flux, species = [1], assembly = assembly)\n boundary_dirichlet!(sys; species = 1, region = 2, value = 0.0)\n sol = solve(sys)\n exact = symlapsphere.(coordinates(grid)[1, :], r2)\n plot(grid, sol, exact, Plotter)\n norm(sol[1, :] - exact, Inf) < 1.0e-14\nend\n\n\"\"\"\n symlapsphereshell(r,r1,r2)\n\nExact solution of Dirichlet problem `-Δu=0` on sphereshell between radii r1 and r2,\nwith boundary value 1 at r1 and 0 at r2.\n\"\"\"\nsymlapsphereshell(r, r1, r2) = (r2 * r1 / r - r1) / (r2 - r1)\n\n\"\"\"\n mainsphereshell(;nref=0, r2=5.0, Plotter=nothing)\n\nof Dirichlet problem `-Δu=0` on sphereshell between radii r1 and r2,\nwith boundary value 1 at r1 and 0 at r2. Test of quadratic convergence.\n\"\"\"\nfunction mainsphereshell(;\n nref = 0,\n r1 = 1.0,\n r2 = 5.0,\n Plotter = nothing,\n assembly = :edgewise,)\n h = 0.1 * 2.0^(-nref)\n R = collect(r1:h:r2)\n grid = VoronoiFVM.Grid(R)\n spherical_symmetric!(grid)\n source(f, node) = f[1] = 0.0\n sys = VoronoiFVM.System(grid; source, flux, species = [1], assembly = assembly)\n boundary_dirichlet!(sys; species = 1, region = 1, value = 1.0)\n boundary_dirichlet!(sys; species = 1, region = 2, value = 0.0)\n sol = solve(sys)\n exact = symlapsphereshell.(coordinates(grid)[1, :], r1, r2)\n plot(grid, sol, exact, Plotter)\n norm(sol[1, :] - exact, Inf) / h^2 < 0.04\nend","category":"page"},{"location":"module_examples/Example203_CoordinateSystems/","page":"203: Various coordinate systems","title":"203: Various coordinate systems","text":"Called by unit test","category":"page"},{"location":"module_examples/Example203_CoordinateSystems/","page":"203: Various coordinate systems","title":"203: Various coordinate systems","text":"using Test#\nfunction runtests()\n @test maindisk(; assembly = :edgewise) &&\n mainring(; assembly = :edgewise) &&\n maincylinder(; assembly = :edgewise) &&\n maincylinder_unstruct(; assembly = :edgewise) &&\n maincylindershell(; assembly = :edgewise) &&\n mainsphere(; assembly = :edgewise) &&\n mainsphereshell(; assembly = :edgewise) &&\n maindisk(; assembly = :cellwise) &&\n mainring(; assembly = :cellwise) &&\n maincylinder(; assembly = :cellwise) &&\n maincylinder_unstruct(; assembly = :cellwise) &&\n maincylindershell(; assembly = :cellwise) &&\n mainsphere(; assembly = :cellwise) &&\n mainsphereshell(; assembly = :cellwise)\nend\n\nend","category":"page"},{"location":"module_examples/Example203_CoordinateSystems/","page":"203: Various coordinate systems","title":"203: Various coordinate systems","text":"","category":"page"},{"location":"module_examples/Example203_CoordinateSystems/","page":"203: Various coordinate systems","title":"203: Various coordinate systems","text":"This page was generated using Literate.jl.","category":"page"},{"location":"devel/#Development-hints","page":"Development hints","title":"Development hints","text":"","category":"section"},{"location":"devel/","page":"Development hints","title":"Development hints","text":"Here, a bit of development hints are given which mainly concern tests and documentation generation.","category":"page"},{"location":"devel/#Pluto-notebooks","page":"Development hints","title":"Pluto notebooks","text":"","category":"section"},{"location":"devel/","page":"Development hints","title":"Development hints","text":"The pluto notebooks in this package are \"triple use\":","category":"page"},{"location":"devel/","page":"Development hints","title":"Development hints","text":"As typical Pluto notebooks, they are self-contained in the senses that they contain their own Project and Manifest files. So users can just download and execute them.\nIf they run with the environmnet variable PLUTO_PROJECT set to some julia environment, this environment will activated at the start of the notebook. In particular, they use Revise.jl so they can be run during development","category":"page"},{"location":"devel/","page":"Development hints","title":"Development hints","text":"of VoronoiFVM.jl. See also https://github.com/fonsp/Pluto.jl/issues/1788 .","category":"page"},{"location":"devel/","page":"Development hints","title":"Development hints","text":"During CI tests, they are run as scripts. For this purpose they are wrapped into temporariy modules, and @test macros can used in the notebooks.","category":"page"},{"location":"module_examples/Example103_ConvectionDiffusion1D/#103:-1D-Convection-diffusion-equation","page":"103: 1D Convection-diffusion equation","title":"103: 1D Convection-diffusion equation","text":"","category":"section"},{"location":"module_examples/Example103_ConvectionDiffusion1D/","page":"103: 1D Convection-diffusion equation","title":"103: 1D Convection-diffusion equation","text":"(source code)","category":"page"},{"location":"module_examples/Example103_ConvectionDiffusion1D/","page":"103: 1D Convection-diffusion equation","title":"103: 1D Convection-diffusion equation","text":"Solve the equation","category":"page"},{"location":"module_examples/Example103_ConvectionDiffusion1D/","page":"103: 1D Convection-diffusion equation","title":"103: 1D Convection-diffusion equation","text":"partial_t u -nabla ( D nabla u - v u) = 0","category":"page"},{"location":"module_examples/Example103_ConvectionDiffusion1D/","page":"103: 1D Convection-diffusion equation","title":"103: 1D Convection-diffusion equation","text":"in Omega=(01) with homogeneous Neumann boundary condition at x=0 and outflow boundary condition at x=1.","category":"page"},{"location":"module_examples/Example103_ConvectionDiffusion1D/","page":"103: 1D Convection-diffusion equation","title":"103: 1D Convection-diffusion equation","text":"module Example103_ConvectionDiffusion1D\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\n\n# Bernoulli function used in the exponential fitting discretization\nfunction bernoulli(x)\n if abs(x) < nextfloat(eps(typeof(x)))\n return 1\n end\n return x / (exp(x) - 1)\nend\n\nfunction exponential_flux!(f, u, edge, data)\n vh = project(edge, data.v)\n Bplus = data.D * bernoulli(vh / data.D)\n Bminus = data.D * bernoulli(-vh / data.D)\n f[1] = Bminus * u[1, 1] - Bplus * u[1, 2]\nend\n\nfunction outflow!(f, u, node, data)\n if node.region == 2\n f[1] = data.v[1] * u[1]\n end\nend\n\nfunction main(; n = 10, Plotter = nothing, D = 0.01, v = 1.0, tend = 100)\n\n # Create a one-dimensional discretization\n h = 1.0 / n\n grid = VoronoiFVM.Grid(0:h:1)\n\n data = (v = [v], D = D)\n\n sys = VoronoiFVM.System(grid,\n VoronoiFVM.Physics(; flux = exponential_flux!, data = data,\n breaction = outflow!))\n\n # Add species 1 to region 1\n enable_species!(sys, 1, [1])\n\n # Set boundary conditions\n boundary_neumann!(sys, 1, 1, 0.0)\n\n # Create a solution array\n inival = unknowns(sys)\n inival[1, :] .= map(x -> 1 - 2x, grid)\n\n # Transient solution of the problem\n control = VoronoiFVM.NewtonControl()\n control.Δt = 0.01 * h\n control.Δt_min = 0.01 * h\n control.Δt_max = 0.1 * tend\n tsol = solve(sys; inival, times = [0, tend], control)\n\n vis = GridVisualizer(; Plotter = Plotter)\n for i = 1:length(tsol.t)\n scalarplot!(vis[1, 1], grid, tsol[1, :, i]; flimits = (0, 1),\n title = \"t=$(tsol.t[i])\", show = true)\n sleep(0.01)\n end\n tsol\nend\n\nusing Test\nfunction runtests()\n tsol = main()\n @test maximum(tsol) <= 1.0 && maximum(tsol[end]) < 1.0e-20\nend\n\nend","category":"page"},{"location":"module_examples/Example103_ConvectionDiffusion1D/","page":"103: 1D Convection-diffusion equation","title":"103: 1D Convection-diffusion equation","text":"","category":"page"},{"location":"module_examples/Example103_ConvectionDiffusion1D/","page":"103: 1D Convection-diffusion equation","title":"103: 1D Convection-diffusion equation","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example150_Impedance1D/#150:-Impedance-calculation","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"","category":"section"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"(source code)","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"Impedance calculation for","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"C ut - (D ux)_x + Ru = 0 in (0,1) u(0,t)=1 + exp(iωt) u(1,t)=0","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"Measurement: I(t)= D u_x(1,t)","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"Steady state:","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"(D u0x)x + Ru0 = 0","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"u0(0,t)=1 u0(1,t)=0","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"Small signal ansatz for ω","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"u(x,t)= u0(x)+ ua(x) exp(iωt)","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"iωC ua - (D uax)x + R u_a =0 ua(0)=1 ua(1)=0","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"module Example150_Impedance1D\n\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\n\nfunction main(; nref = 0, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise,\n L = 1.0, R = 1.0, D = 1.0, C = 1.0,\n ω0 = 1.0e-3, ω1 = 5.0e1)","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"Create array which is refined close to 0","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":" h0 = 0.005 / 2.0^nref\n h1 = 0.1 / 2.0^nref\n\n X = VoronoiFVM.geomspace(0, L, h0, h1)","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"Create discretitzation grid","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":" grid = VoronoiFVM.Grid(X)","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"Create and fill data","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":" data = (R = R, D = D, C = C)","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"Declare constitutive functions","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":" flux = function (f, u, edge, data)\n f[1] = data.D * (u[1, 1] - u[1, 2])\n end\n\n storage = function (f, u, node, data)\n f[1] = data.C * u[1]\n end\n\n reaction = function (f, u, node, data)\n f[1] = data.R * u[1]\n end\n\n excited_bc = 1\n excited_bcval = 1.0\n excited_spec = 1\n meas_bc = 2","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"Create physics struct","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":" physics = VoronoiFVM.Physics(; data = data,\n flux = flux,\n storage = storage,\n reaction = reaction)","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"Create discrete system and enable species","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":" sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly)\n\n enable_species!(sys, excited_spec, [1])","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"Create test functions for current measurement","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":" factory = TestFunctionFactory(sys)\n measurement_testfunction = testfunction(factory, [excited_bc], [meas_bc])\n\n boundary_dirichlet!(sys, excited_spec, excited_bc, excited_bcval)\n boundary_dirichlet!(sys, excited_spec, meas_bc, 0.0)\n\n steadystate = solve(sys)\n\n function meas_stdy(meas, U)\n u = reshape(U, sys)\n meas[1] = -VoronoiFVM.integrate_stdy(sys, measurement_testfunction, u)[excited_spec]\n nothing\n end\n\n function meas_tran(meas, U)\n u = reshape(U, sys)\n meas[1] = -VoronoiFVM.integrate_tran(sys, measurement_testfunction, u)[excited_spec]\n nothing\n end\n\n dmeas_stdy = measurement_derivative(sys, meas_stdy, steadystate)\n dmeas_tran = measurement_derivative(sys, meas_tran, steadystate)","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"Create Impeadancs system from steady state","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":" isys = VoronoiFVM.ImpedanceSystem(sys, steadystate, excited_spec, excited_bc)","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"Prepare recording of impedance results","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":" allomega = zeros(0)","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"for calculated data","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":" allI0 = zeros(Complex{Float64}, 0)\n allIL = zeros(Complex{Float64}, 0)","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"for exact data","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":" allIx0 = zeros(Complex{Float64}, 0)\n allIxL = zeros(Complex{Float64}, 0)\n\n ω = ω0\n\n UZ = unknowns(isys)\n while ω < ω1","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"solve impedance system","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":" solve!(UZ, isys, ω)","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"calculate approximate solution obtain measurement in frequency domain","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":" IL = impedance(isys, ω, steadystate, dmeas_stdy, dmeas_tran)","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"record approximate solution","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":" push!(allomega, ω)\n push!(allIL, IL)","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"record exact solution","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":" iω = 1im * ω\n z = sqrt(iω * data.C / data.D + data.R / data.D)\n eplus = exp(z * L)\n eminus = exp(-z * L)\n IxL = 2.0 * data.D * z / (eplus - eminus)\n\n push!(allIxL, 1 / IxL)","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"increase omega","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":" ω = ω * 1.1\n end\n\n p = GridVisualizer(; Plotter = Plotter)\n scalarplot!(p, real(allIxL), imag(allIxL); label = \"exact\", color = :red,\n linestyle = :dot)\n scalarplot!(p, real(allIL), imag(allIL); label = \"calc\", show = true, clear = false,\n color = :blue, linestyle = :solid)\n\n sum(allIL)\nend\n\nusing Test\nfunction runtests()\n testval = 57.92710286186797 + 23.163945443946027im\n @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval\nend\n\nend","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"","category":"page"},{"location":"module_examples/Example150_Impedance1D/","page":"150: Impedance calculation","title":"150: Impedance calculation","text":"This page was generated using Literate.jl.","category":"page"},{"location":"plutostatichtml_examples/interfaces1d/","page":"Internal interfaces (1D)","title":"Internal interfaces (1D)","text":"\n\n\n\n
    begin\n    import Pkg as _Pkg\n    haskey(ENV, \"PLUTO_PROJECT\") && _Pkg.activate(ENV[\"PLUTO_PROJECT\"])\n    using Revise\n    using VoronoiFVM\n    using ExtendableGrids\n    using GridVisualize\n    using PlutoUI\n    using HypertextLiteral\n    using LinearAlgebra\n    using LinearSolve\n    using Test\n    using CairoMakie\n    CairoMakie.activate!(; type = \"svg\", visible = false)\n    GridVisualize.default_plotter!(CairoMakie)\nend
    \n
    CairoMakie
    \n\n\n\n\n\n

    Interface conditions in 1D

    Source

    This notebooks discusses handling of internal interfaces with VoronoiFVM.jl.

    Two subdomains

    For a simple stationary diffusion equation with an interior interface, we discuss possible interface conditions between two subdomains.

    Let $\\Omega=\\Omega_1\\cup\\Omega_2$ where $\\Omega_1=(-1,0)$ and $\\Omega_2=(0,1)$. Let $\\Gamma_1={-1}$,$\\Gamma_2={1}$ and $\\Gamma_3={0}$.

    Regard the following problem:

    $\\begin{aligned} -\\Delta u_1 &= 0 & \\text{in}\\quad \\Omega_1\\\\ -\\Delta u_2 &= 0 & \\text{in}\\quad \\Omega_2\\\\ \\end{aligned}$

    with exterior boundary conditions

    $u_1|_{\\Gamma_1} = g_1$ and $u_2|_{\\Gamma_2} = g_2$

    For the interior boundary (interface) conditions we set

    $\\nabla u_1|_{\\Gamma_3}+f_1(u_1,u_2)=0$

    $-\\nabla u_2|_{\\Gamma_3}+f_2(u_1,u_2)=0$

    where $f_1$, $f_2$ are discussed later.

    \n\n\n

    Set up

    \n\n\n

    Create a grid with two subdomins and an interface in the center.

    \n\n
    nref = 2
    \n
    2
    \n\n
    begin\n    hmax = 0.2 / 2.0^nref\n    hmin = 0.05 / 2.0^nref\n    X1 = geomspace(-1.0, 0.0, hmax, hmin)\n    X2 = geomspace(0.0, 1.0, hmin, hmax)\n    X = glue(X1, X2)\n    grid = VoronoiFVM.Grid(X)\n\n    bfacemask!(grid, [0.0], [0.0], 3)\n    ## Material 1 left of 0\n    cellmask!(grid, [-1.0], [0.0], 1)\n    ## Material 2 right of 0\n    cellmask!(grid, [0.0], [1.0], 2)\nend;
    \n\n\n
    gridplot(grid; legend = :rt, resolution = (600, 200))
    \n\n\n\n

    For later use (plotting) extract the two subgrids from the grid

    \n\n
    subgrid1 = subgrid(grid, [1]);
    \n\n\n
    subgrid2 = subgrid(grid, [2]);
    \n\n\n\n

    Define the diffusion flux for the two species in their respective subdomains

    \n\n
    function flux!(f, u, edge)\n    if edge.region == 1\n        f[1] = u[1, 1] - u[1, 2]\n    end\n    if edge.region == 2\n        f[2] = u[2, 1] - u[2, 2]\n    end\nend
    \n
    flux! (generic function with 1 method)
    \n\n\n

    Specify the outer boundary values.

    \n\n
    const g_1 = 1.0
    \n
    1.0
    \n\n
    const g_2 = 0.1
    \n
    0.1
    \n\n\n

    Create the system. We pass the interface condition function as a parameter.

    \n\n
    function make_system(breaction)\n    physics = VoronoiFVM.Physics(; flux = flux!, breaction = breaction)\n\n    ## Create system\n    sys = VoronoiFVM.System(grid, physics; unknown_storage = :sparse)\n\n    ##  Enable species in their respective subregions\n    enable_species!(sys, 1, [1])\n    enable_species!(sys, 2, [2])\n\n    ## Set boundary conditions\n    for ispec = 1:2\n        boundary_dirichlet!(sys, ispec, 1, g_1)\n        boundary_dirichlet!(sys, ispec, 2, g_2)\n    end\n    sys\nend
    \n
    make_system (generic function with 1 method)
    \n\n\n

    Stationary solution with zero initial value

    \n\n
    function mysolve(sys)\n    U = solve(sys)\n    U1 = view(U[1, :], subgrid1)\n    U2 = view(U[2, :], subgrid2)\n    U1, U2\nend
    \n
    mysolve (generic function with 1 method)
    \n\n\n

    Plot the results

    \n\n
    function plot(U1, U2; title = \"\")\n    vis = GridVisualizer(; resolution = (600, 300))\n    scalarplot!(vis,\n                subgrid1,\n                U1;\n                clear = false,\n                show = false,\n                color = :green,\n                label = \"u1\")\n    scalarplot!(vis,\n                subgrid2,\n                U2;\n                clear = false,\n                show = true,\n                color = :blue,\n                label = \"u2\",\n                legend = :rt,\n                title = title,\n                flimits = (-0.5, 1.5))\nend
    \n
    plot (generic function with 1 method)
    \n\n\n

    No interface reaction

    This means we set $f_1(u_1,u_2)=0$ and $f_2(u_1,u_2)=0$.

    \n\n
    function noreaction(f, u, node) end
    \n
    noreaction (generic function with 1 method)
    \n\n
    system1 = make_system(noreaction);
    \n\n\n
    plot(mysolve(system1)...)
    \n\n\n\n

    The solution consists of two constants defined by the respective Dirichlet boundary conditions at the outer boundary.

    \n\n\n

    Mass action law reaction $u_1 \\leftrightharpoons u_2$

    This is a rather general ansatz where we assume a backward-forward reaction between the two species meeting at the interface with reaction constants $k_1$ and $k_2$, respectively.

    According to the mass action law, this translates to a reaction rate

    $r(u_1,u_2)=k_1u_1 - k_2u_2$

    and correspondingly

    $f_1(u_1,u_2)=r$

    $f_2(u_1,u_2)=-r$

    Note, that $f_i$ is monotonically increasing in $u_i$ and monotonically decreasing in the respective other argument, leading to an M-Property of the overall discretization matrix.

    Note that the \"no reaction\" case is just a special case where $k_1,k_2=0$.

    \n\n
    function mal_reaction(f, u, node)\n    if node.region == 3\n        react = k1 * u[1] - k2 * u[2]\n        f[1] = react\n        f[2] = -react\n    end\nend
    \n
    mal_reaction (generic function with 1 method)
    \n\n
    system2 = make_system(mal_reaction)
    \n
    VoronoiFVM.System{Float64, Float64, Int32, Int64, SparseArrays.SparseMatrixCSC{Int32, Int32}, VoronoiFVM.SparseSolutionArray{Float64, Int32}}(num_species=2)\n
    \n\n
    begin\n    const k1 = 0.1\n    const k2 = 10\nend
    \n
    10
    \n\n\n\n\n\n

    The back reaction is 100 times stronger than the forward reaction. This means that species 2 is consumed, creating species 1.

    \n\n\n

    Penalty enforcing continuity

    Setting $k_1,k_2$ to a large number leads to another special case of the above reaction - similar to the penalty method to implement the Dirichlet boundary conditions, this lets the reaction equation dominate, which in this case forces $u_1-u_2=0$ at the interface, and thus continuity.

    \n\n
    function penalty_reaction(f, u, node)\n    if node.region == 3\n        react = 1.0e10 * (u[1] - u[2])\n        f[1] = react\n        f[2] = -react\n    end\nend
    \n
    penalty_reaction (generic function with 1 method)
    \n\n
    system3 = make_system(penalty_reaction);
    \n\n\n
    plot(mysolve(system3)...)
    \n\n\n\n

    Penalty enforcing fixed jump

    Instead of enforcing continuity, one can enforce a fixed jump.

    \n\n
    const jump = 0.2
    \n
    0.2
    \n\n
    function penalty_jump_reaction(f, u, node)\n    if node.region == 3\n        react = 1.0e10 * (u[1] - u[2] - jump)\n        f[1] = react\n        f[2] = -react\n    end\nend
    \n
    penalty_jump_reaction (generic function with 1 method)
    \n\n
    system3jump = make_system(penalty_jump_reaction);
    \n\n\n
    plot(mysolve(system3jump)...)
    \n\n\n\n

    Interface recombination

    Here, we implement an annihilation reaction $u_1 + u_2 \\to \\emptyset$ According to the mass action law, this is implemented via

    $r(u_1,u_2)=k_r u_1 u_2$

    $f_1(u_1,u_2)=r$

    $f_2(u_1,u_2)=r$

    \n\n
    function recombination(f, u, node)\n    if node.region == 3\n        react = k_r * (u[1] * u[2])\n        f[1] = react\n        f[2] = react\n    end\nend;
    \n\n\n
    system4 = make_system(recombination);
    \n\n\n
    const k_r = 1000
    \n
    1000
    \n\n
    plot(mysolve(system4)...)
    \n\n\n\n

    Bot species are consumed at the interface.

    \n\n\n

    Thin conductive interface layer

    Let us assume that the interface is of thickness $d$ which is however small with respect to $\\Omega$ that we want to derive an interface condition from the assumption of an exact continuous solution within the interface.

    So let $\\Omega_I=(x_l,x_r)$ be the interface region where we have $-\\Delta u_I=0$ with values $u_l$, $u_r$ at the boundaries.

    Then we have for the flux in the interface region, $q_I=\\nabla u = \\frac1{d}(u_r - u_l)$

    Continuity of fluxes then gives $f_1=q_I$ and $f_2=-q_I$.

    Continuity of $u$ gives $u_{1,I}=u_l, u_{2,I}=u_r$ This gives

    $r=q_I=\\frac{1}{d}(u_1-u_{2})$

    $f_1(u_1,v_1)=r$

    $f_2(u_1,v_1)=-r$

    and therefore another special case of the mass action law condition.

    \n\n
    const d = 1
    \n
    1
    \n\n
    function thinlayer(f, u, node)\n    if node.region == 3\n        react = (u[1] - u[2]) / d\n        f[1] = react\n        f[2] = -react\n    end\nend
    \n
    thinlayer (generic function with 1 method)
    \n\n
    system5 = make_system(thinlayer);
    \n\n\n
    plot(mysolve(system5)...)
    \n\n\n\n

    The solution looks very similar to the case of the jump condition, however here, the size of the jump is defined by the physics of the interface.

    \n\n","category":"page"},{"location":"plutostatichtml_examples/interfaces1d/#Multiple-domains","page":"Internal interfaces (1D)","title":"Multiple domains","text":"","category":"section"},{"location":"plutostatichtml_examples/interfaces1d/","page":"Internal interfaces (1D)","title":"Internal interfaces (1D)","text":"
    \n

    From the above discussion it seems that discontinuous interface conditions can be formulated in a rather general way via linear or nonlinear robin boundary conditions for each of the adjacent discontinuous species. Technically, it is necessary to be able to access the adjacent bulk data.

    \n\n\n

    In order to streamline the handling of multiple interfaces, we propose an API layer on top of the species handling of VoronoiFVM. We call these \"meta species\" \"quantities\".

    \n\n\n

    We define a grid with N=6 subregions

    \n\n
    N = 6
    \n
    6
    \n\n
    begin\n    XX = collect(0:0.1:1)\n    local xcoord = XX\n    for i = 1:(N - 1)\n        xcoord = glue(xcoord, XX .+ i)\n    end\n    grid2 = simplexgrid(xcoord)\n    for i = 1:N\n        cellmask!(grid2, [i - 1], [i], i)\n    end\n    for i = 1:(N - 1)\n        bfacemask!(grid2, [i], [i], i + 2)\n    end\nend
    \n\n\n
    gridplot(grid2; legend = :lt, resolution = (600, 200))
    \n\n\n\n

    To work with quantities, we first introduce a new constructor call without the \"physics\" parameter:

    \n\n
    system6 = VoronoiFVM.System(grid2)
    \n
    VoronoiFVM.System{Float64, Float64, Int32, Int64, Matrix{Int32}, Matrix{Float64}}(num_species=0)\n
    \n\n\n

    First, we introduce a continuous quantity which we name \"cspec\". Note that the \"species number\" can be assigned automatically if not given explicitly.

    \n\n
    const cspec = ContinuousQuantity(system6, 1:N; ispec = 1)
    \n
    ContinuousQuantity{Int32}(1, 1)
    \n\n\n

    A discontinuous quantity can be introduced as well. by default, each reagion gets a new species number. This can be overwritten by the user. It is important that the speces numbers of neighboring regions differ.

    \n\n
    const dspec = DiscontinuousQuantity(system6, 1:N; regionspec = [2 + i % 2 for i = 1:N])
    \n
    DiscontinuousQuantity{Int32}(Int32[3, 2, 3, 2, 3, 2], 2)
    \n\n\n

    For both quantities, we define simple diffusion fluxes:

    \n\n
    function flux2(f, u, edge)\n    f[dspec] = u[dspec, 1] - u[dspec, 2]\n    f[cspec] = u[cspec, 1] - u[cspec, 2]\nend
    \n
    flux2 (generic function with 1 method)
    \n\n\n

    Define a thin layer interface condition for dspec and an interface source for cspec.

    \n\n
    function breaction2(f, u, node)\n    if node.region > 2\n        react = (u[dspec, 1] - u[dspec, 2]) / d1\n        f[dspec, 1] = react\n        f[dspec, 2] = -react\n\n        f[cspec] = -q1\n    end\nend
    \n
    breaction2 (generic function with 1 method)
    \n\n\n

    Add physics to the system, set dirichlet bc at both ends, and extract subgrids for plotting (until there will be a plotting API for this...)

    \n\n
    begin\n    physics!(system6, VoronoiFVM.Physics(; flux = flux2, breaction = breaction2))\n\n    ## Set boundary conditions\n    boundary_dirichlet!(system6, dspec, 1, g_1)\n    boundary_dirichlet!(system6, dspec, 2, g_2)\n    boundary_dirichlet!(system6, cspec, 1, 0)\n    boundary_dirichlet!(system6, cspec, 2, 0)\n\n    # ensure that `solve` is called only after this cell\n    # as mutating circumvents the reactivity of the notebook\n    physics_ok = true\nend;
    \n\n\n
    allsubgrids = subgrids(dspec, system6)
    \n
    6-element Vector{ExtendableGrid{Float64, Int32}}:\n ExtendableGrids.ExtendableGrid{Float64, Int32};\ndim: 1 nodes: 11 cells: 10 bfaces: 2\n\n\n ExtendableGrids.ExtendableGrid{Float64, Int32};\ndim: 1 nodes: 11 cells: 10 bfaces: 2\n\n\n ExtendableGrids.ExtendableGrid{Float64, Int32};\ndim: 1 nodes: 11 cells: 10 bfaces: 2\n\n\n ExtendableGrids.ExtendableGrid{Float64, Int32};\ndim: 1 nodes: 11 cells: 10 bfaces: 2\n\n\n ExtendableGrids.ExtendableGrid{Float64, Int32};\ndim: 1 nodes: 11 cells: 10 bfaces: 2\n\n\n ExtendableGrids.ExtendableGrid{Float64, Int32};\ndim: 1 nodes: 11 cells: 10 bfaces: 2\n\n
    \n\n
    if physics_ok\n    sol6 = solve(system6; inival = 0.5)\nend;
    \n\n\n
    const d1 = 0.1
    \n
    0.1
    \n\n
    const q1 = 0.2
    \n
    0.2
    \n\n
    function plot2(U, subgrids, system6)\n    dvws = VoronoiFVM.views(U, dspec, allsubgrids, system6)\n    cvws = VoronoiFVM.views(U, cspec, allsubgrids, system6)\n    vis = GridVisualizer(; resolution = (600, 300), legend = :rt)\n    scalarplot!(vis,\n                allsubgrids,\n                grid2,\n                dvws;\n                flimits = (-0.5, 1.5),\n                clear = false,\n                color = :red,\n                label = \"discontinuous species\")\n    scalarplot!(vis,\n                allsubgrids,\n                grid2,\n                cvws;\n                flimits = (-0.5, 1.5),\n                clear = false,\n                color = :green,\n                label = \"continuous species\")\n    reveal(vis)\nend
    \n
    plot2 (generic function with 1 method)
    \n\n
    plot2(sol6, subgrids, system6)
    \n\n\n","category":"page"},{"location":"plutostatichtml_examples/interfaces1d/#Testing","page":"Internal interfaces (1D)","title":"Testing","text":"","category":"section"},{"location":"plutostatichtml_examples/interfaces1d/","page":"Internal interfaces (1D)","title":"Internal interfaces (1D)","text":"
    \n
    \n\n
    if d1 == 0.1 && N == 6\n    @test norm(system6, sol6, 2) ≈ 7.0215437706445245\nend
    \n
    Test Passed
    \n\n\n
    \n\n\n\n\n\n
    \n
    \n

    Built with Julia 1.10.2 and

    \nCairoMakie 0.11.5
    \nExtendableGrids 1.2.3
    \nGridVisualize 1.5.0
    \nHypertextLiteral 0.9.5
    \nLinearSolve 2.22.1
    \nPkg 1.10.0
    \nPlutoUI 0.7.55
    \nRevise 3.5.13
    \nVoronoiFVM 1.16.0\n
    \n\n","category":"page"},{"location":"plutostatichtml_examples/interfaces1d/","page":"Internal interfaces (1D)","title":"Internal interfaces (1D)","text":"EditURL = \"https://github.com/j-fu/VoronoiFVM.jl/blob/main/nothing\"","category":"page"},{"location":"plutostatichtml_examples/ode-nlstorage1d/","page":"OrdinaryDiffEq.jl changing mass matrix","title":"OrdinaryDiffEq.jl changing mass matrix","text":"\n\n\n\n
    begin\n    import Pkg as _Pkg\n    haskey(ENV, \"PLUTO_PROJECT\") && _Pkg.activate(ENV[\"PLUTO_PROJECT\"])\n    using Test\t\n    using Revise\n    using Printf\n    using VoronoiFVM\n    using OrdinaryDiffEq\n    using LinearAlgebra\n    using PlutoUI, HypertextLiteral,UUIDs\n    using DataStructures\n    using GridVisualize,CairoMakie\n    CairoMakie.activate!(type=\"svg\")\nend
    \n\n\n","category":"page"},{"location":"plutostatichtml_examples/ode-nlstorage1d/#1D-Nonlinear-Storage","page":"OrdinaryDiffEq.jl changing mass matrix","title":"1D Nonlinear Storage","text":"","category":"section"},{"location":"plutostatichtml_examples/ode-nlstorage1d/","page":"OrdinaryDiffEq.jl changing mass matrix","title":"OrdinaryDiffEq.jl changing mass matrix","text":"
    \n
    \n\n
    TableOfContents(aside=false)
    \n\n\n\n

    This equation comes from the transformation of the nonlinear diffusion equation

    $$\\partial_t v - \\Delta v^m = 0$$

    to

    $$\\partial_t u^\\frac{1}{m} -\\Delta u = 0$$

    in $\\Omega=(-1,1)$ with homogeneous Neumann boundary conditions. We can derive an exact solution from the Barenblatt solution of the equation for u.

    \n\n
    function barenblatt(x,t,m)\n    tx=t^(-1.0/(m+1.0))\n    xx=x*tx\n    xx=xx*xx\n    xx=1- xx*(m-1)/(2.0*m*(m+1));\n    if xx<0.0\n        xx=0.0\n    end\n    return tx*xx^(1.0/(m-1.0))\nend
    \n
    barenblatt (generic function with 1 method)
    \n\n
    begin\n    const m=2\n    const ε=1.0e-10\n    const n=50\n    const t0=1.0e-3\n    const tend=1.0e-2\nend
    \n
    0.01
    \n\n
    X=collect(-1:2.0/n:1)
    \n
    51-element Vector{Float64}:\n -1.0\n -0.96\n -0.92\n -0.88\n -0.84\n -0.8\n -0.76\n  ⋮\n  0.8\n  0.84\n  0.88\n  0.92\n  0.96\n  1.0
    \n\n
    u0=map(x->barenblatt(x,t0,m)^m,X)
    \n
    51-element Vector{Float64}:\n 0.0\n 0.0\n 0.0\n 0.0\n 0.0\n 0.0\n 0.0\n ⋮\n 0.0\n 0.0\n 0.0\n 0.0\n 0.0\n 0.0
    \n\n
    begin\n    grid=VoronoiFVM.Grid(X)\nend
    \n
    ExtendableGrids.ExtendableGrid{Float64, Int32};\ndim: 1 nodes: 51 cells: 50 bfaces: 2\n\n
    \n\n\n

    Direct implementation with VoronoiFVM

    \n\n
        function flux!(f,u,edge)\n        f[1]=u[1,1]-u[1,2]\n    end
    \n
    flux! (generic function with 1 method)
    \n\n\n

    Storage term needs to be regularized as its derivative at 0 is infinity:

    \n\n
        function storage!(f,u,node)\n        f[1]=(ε+u[1])^(1.0/m)\n    end
    \n
    storage! (generic function with 1 method)
    \n\n
    begin\n    physics=VoronoiFVM.Physics(\n        flux=flux!,\n        storage=storage!)\n    sys=VoronoiFVM.System(grid,physics,species=1)\n        inival=unknowns(sys)\n    inival[1,:]=u0\n    control=VoronoiFVM.SolverControl()\n    tsol=VoronoiFVM.solve(sys;inival,times=(t0,tend),Δt_min=1.0e-4,Δt=1.0e-4,Δu_opt=0.1,force_first_step=true,log=true)\n    summary(sys.history)\nend
    \n
    (seconds = 3.27, tasm = 1.14, tlinsolve = 0.0397, steps = 731, iters = 2920, maxabsnorm = 1.73e-12, maxrelnorm = 1.75e-11, maxroundoff = 9.19e-15, iters_per_step = 4.0, facts_per_step = 0.0, liniters_per_step = 0.0)
    \n\n\n

    Implementation as DAE

    \n\n\n

    If we want to solve the problem with DifferentialEquations.jl solvers, we see that the problem structure does not fit into the setting of that package due to the nonlinearity under the time derivative. Here we propose a reformulation to a DAE as a way to achieve this possibility:

    $$\\begin{cases}\n\t\\partial_t w -\\Delta u &= 0\\\\\n w^m - u &=0\n\\end{cases}$$

    \n\n
    function dae_storage!(y,u,node)\n    y[1]=u[2]\nend
    \n
    dae_storage! (generic function with 1 method)
    \n\n
    function dae_reaction!(y,u,node)\n    y[2]= u[2]^m-u[1]\nend
    \n
    dae_reaction! (generic function with 1 method)
    \n\n\n

    First, we test this with the implicit Euler method of VoronoiFVM

    \n\n
    begin\n    dae_physics=VoronoiFVM.Physics(\n        flux=flux!,\n       storage=dae_storage!,\n        reaction=dae_reaction!\n    )\n    dae_sys=VoronoiFVM.System(grid,dae_physics,species=[1,2])\n    dae_inival=unknowns(dae_sys)\n    dae_inival[1,:].=u0\n    dae_inival[2,:].=u0.^(1/m)\n    dae_control=VoronoiFVM.SolverControl()\n    dae_tsol=VoronoiFVM.solve(dae_sys;inival=dae_inival,times=(t0,tend),Δt_min=1.0e-4,Δt=1.0e-4,Δu_opt=0.1,force_first_step=true,log=true)\n    summary(dae_sys.history)\nend
    \n
    (seconds = 2.27, tasm = 0.677, tlinsolve = 0.0343, steps = 732, iters = 2205, maxabsnorm = 9.72e-11, maxrelnorm = 9.82e-10, maxroundoff = 6.99e-13, iters_per_step = 3.02, facts_per_step = 0.0, liniters_per_step = 0.0)
    \n\n\n

    Implementation via OrdinaryDiffEq.jl

    \n\n\n
    OrderedDict{String, UnionAll} with 5 entries:\n  \"QNDF2 (Like matlab's ode15s)\" => QNDF2\n  \"Rodas5\"                       => Rodas5\n  \"Rosenbrock23 (Rosenbrock)\"    => Rosenbrock23\n  \"FBDF\"                         => FBDF\n  \"Implicit Euler\"               => ImplicitEuler
    \n\n\n

    method:

    \n\n
    begin\n    de_sys=VoronoiFVM.System(grid,dae_physics,species=[1,2])\n    problem = ODEProblem(de_sys,dae_inival,(t0,tend))\n    de_odesol=solve(problem,\n        diffeqmethods[method](),\n        adaptive=true,\n        reltol=1.0e-3,\n        abstol=1.0e-3,\n        initializealg=NoInit()\n        )          \n        de_tsol=reshape(de_odesol,de_sys)\nend;
    \n\n\n\n\n\n\n

    t=0.0019999

    \n\n
    exact=map(x->barenblatt(x,tend,m).^m,X)
    \n
    51-element Vector{Float64}:\n 0.0\n 0.0\n 0.0\n 0.0\n 0.0\n 0.0\n 0.0\n ⋮\n 0.0\n 0.0\n 0.0\n 0.0\n 0.0\n 0.0
    \n\n
    @test norm(tsol[1,:,end]-exact,Inf)<0.1
    \n
    Test Passed
    \n\n
    @test norm(dae_tsol[1,:,end]-exact,Inf)<0.1
    \n
    Test Passed
    \n\n
     @test norm(de_tsol[1,:,end]-exact,Inf)<0.05
    \n
    Test Passed
    \n\n\n
    myaside (generic function with 1 method)
    \n\n
    function plotsolutions()\n    vis=GridVisualizer(resolution=(380,200),dim=1,Plotter=CairoMakie,legend=:lt);\n    u=tsol(t)\n    u_dae=dae_tsol(t)\n    u_de=de_tsol(t)\n    scalarplot!(vis,X,map(x->barenblatt(x,t,m).^m,X),clear=true,color=:red,linestyle=:solid,flimits=(0,100),label=\"exact\")\n    scalarplot!(vis,grid,u_dae[1,:],clear=false,color=:green, linestyle=:solid,label=\"vfvm_dae\")\n    scalarplot!(vis,grid,u_de[1,:],clear=false,color=:blue, markershape=:cross,linestyle=:dot,label=\"vfvm_diffeq\")\n    scalarplot!(vis,grid,u[1,:],clear=false,color=:black,markershape=:none, linestyle=:dash,title=\"t=$(t)\",label=\"vfvm_default\")\n    reveal(vis)\nend
    \n
    plotsolutions (generic function with 1 method)
    \n
    \n

    Built with Julia 1.10.2 and

    \nCairoMakie 0.11.6
    \nDataStructures 0.18.16
    \nGridVisualize 1.5.0
    \nHypertextLiteral 0.9.5
    \nOrdinaryDiffEq 6.66.0
    \nPkg 1.10.0
    \nPlutoUI 0.7.55
    \nRevise 3.5.13
    \nVoronoiFVM 1.17.1\n
    \n\n","category":"page"},{"location":"plutostatichtml_examples/ode-nlstorage1d/","page":"OrdinaryDiffEq.jl changing mass matrix","title":"OrdinaryDiffEq.jl changing mass matrix","text":"EditURL = \"https://github.com/j-fu/VoronoiFVM.jl/blob/main/nothing\"","category":"page"},{"location":"module_examples/Example215_NonlinearPoisson2D_BoundaryReaction/#215:-2D-Nonlinear-Poisson-with-boundary-reaction","page":"215: 2D Nonlinear Poisson with boundary reaction","title":"215: 2D Nonlinear Poisson with boundary reaction","text":"","category":"section"},{"location":"module_examples/Example215_NonlinearPoisson2D_BoundaryReaction/","page":"215: 2D Nonlinear Poisson with boundary reaction","title":"215: 2D Nonlinear Poisson with boundary reaction","text":"(source code)","category":"page"},{"location":"module_examples/Example215_NonlinearPoisson2D_BoundaryReaction/","page":"215: 2D Nonlinear Poisson with boundary reaction","title":"215: 2D Nonlinear Poisson with boundary reaction","text":"module Example215_NonlinearPoisson2D_BoundaryReaction\n\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\nusing ExtendableSparse\n\nfunction main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise,\n tend = 100, max_lureuse = 0)\n h = 1.0 / convert(Float64, n)\n X = collect(0.0:h:1.0)\n Y = collect(0.0:h:1.0)\n\n grid = VoronoiFVM.Grid(X, Y)\n\n eps = 1.0e-2\n physics = VoronoiFVM.Physics(; breaction = function (f, u, node)\n if node.region == 2\n f[1] = 1 * (u[1] - u[2])\n f[2] = 1 * (u[2] - u[1])\n else\n f[1] = 0\n f[2] = 0\n end\n end, flux = function (f, u, edge)\n f[1] = eps * (u[1, 1] - u[1, 2])\n f[2] = eps * (u[2, 1] - u[2, 2])\n end, storage = function (f, u, node)\n f[1] = u[1]\n f[2] = u[2]\n end)\n\n sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly)\n enable_species!(sys, 1, [1])\n enable_species!(sys, 2, [1])\n\n inival = unknowns(sys)\n inival[1, :] .= map((x, y) -> exp(-5.0 * ((x - 0.5)^2 + (y - 0.5)^2)), grid)\n inival[2, :] .= 0\n\n control = VoronoiFVM.NewtonControl()\n control.verbose = verbose\n control.reltol_linear = 1.0e-5\n control.max_lureuse = max_lureuse\n\n tstep = 0.01\n time = 0.0\n istep = 0\n u25 = 0\n\n p = GridVisualizer(; Plotter = Plotter, layout = (2, 1))\n while time < tend\n time = time + tstep\n U = solve(sys; inival, control, tstep)\n inival .= U\n if verbose\n @printf(\"time=%g\\n\", time)\n end\n I = integrate(sys, physics.storage, U)\n Uall = sum(I)\n tstep *= 1.2\n istep = istep + 1\n u25 = U[25]\n scalarplot!(p[1, 1], grid, U[1, :];\n title = @sprintf(\"U1: %.3g U1+U2:%8.3g\", I[1, 1], Uall),\n flimits = (0, 1))\n scalarplot!(p[2, 1], grid, U[2, :]; title = @sprintf(\"U2: %.3g\", I[2, 1]),\n flimits = (0, 1))\n reveal(p)\n end\n return u25\nend\n\nusing Test\nfunction runtests()\n testval = 0.2760603343272377\n @test main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval\nend\nend","category":"page"},{"location":"module_examples/Example215_NonlinearPoisson2D_BoundaryReaction/","page":"215: 2D Nonlinear Poisson with boundary reaction","title":"215: 2D Nonlinear Poisson with boundary reaction","text":"","category":"page"},{"location":"module_examples/Example215_NonlinearPoisson2D_BoundaryReaction/","page":"215: 2D Nonlinear Poisson with boundary reaction","title":"215: 2D Nonlinear Poisson with boundary reaction","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example405_GenericOperator/#405:-Generic-operator","page":"405: Generic operator","title":"405: Generic operator","text":"","category":"section"},{"location":"module_examples/Example405_GenericOperator/","page":"405: Generic operator","title":"405: Generic operator","text":"(source code)","category":"page"},{"location":"module_examples/Example405_GenericOperator/","page":"405: Generic operator","title":"405: Generic operator","text":"Handle an operator which does not fit into the storage/flux/reaction API. This uses automatic sparsity detection.","category":"page"},{"location":"module_examples/Example405_GenericOperator/","page":"405: Generic operator","title":"405: Generic operator","text":"module Example405_GenericOperator\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\n\nfunction main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse)\n # Same as Example102 with upwind\n\n # Create a one-dimensional discretization\n h = 1.0 / convert(Float64, n)\n X = collect(0:h:1)\n grid = VoronoiFVM.Grid(X)\n\n # A parameter which is \"passed\" to the flux function via scope\n D = 1.0e-2\n v = 1.0\n\n # This generic operator works on the full solution seen as linear vector, and indexing\n # into it needs to be performed with the help of idx (defined below for a solution vector)\n # Here, instead of the flux function we provide a \"generic operator\"\n # which provides the stiffness part of the problem. Its sparsity is detected automatically\n # using Symbolics.jl\n function generic_operator!(f, u, sys)\n f .= 0.0\n for i = 1:(length(X) - 1)\n du = D * (u[idx[1, i]] - u[idx[1, i + 1]]) / (X[i + 1] - X[i]) +\n v * (v > 0 ? u[idx[1, i]] : u[idx[1, i + 1]])\n f[idx[1, i]] += du\n f[idx[1, i + 1]] -= du\n end\n end\n\n # Create a physics structure\n physics = VoronoiFVM.Physics(; generic = generic_operator!)\n\n # Create a finite volume system - either\n # in the dense or the sparse version.\n # The difference is in the way the solution object\n # is stored - as dense or as sparse matrix\n\n sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage)\n\n # Add species 1 to region 1\n enable_species!(sys, 1, [1])\n\n # Set boundary conditions\n boundary_dirichlet!(sys, 1, 1, 0.0)\n boundary_dirichlet!(sys, 1, 2, 1.0)\n\n # Create a solution array\n inival = unknowns(sys; inival = 0.5)\n solution = unknowns(sys)\n\n idx = unknown_indices(solution)\n\n # Stationary solution of the problem\n solution = solve(sys; inival = 0.5, verbose)\n\n scalarplot(grid, solution[1, :]; title = \"Nonlinear Poisson\", Plotter = Plotter,\n resolution = (300, 300))\n return sum(solution)\nend\n\nusing Test\nfunction runtests()\n testval = 1.099999999614456\n @test main(; unknown_storage = :sparse) ≈ testval &&\n main(; unknown_storage = :dense) ≈ testval\nend\n\nend","category":"page"},{"location":"module_examples/Example405_GenericOperator/","page":"405: Generic operator","title":"405: Generic operator","text":"","category":"page"},{"location":"module_examples/Example405_GenericOperator/","page":"405: Generic operator","title":"405: Generic operator","text":"This page was generated using Literate.jl.","category":"page"},{"location":"plutostatichtml_examples/flux-reconstruction/","page":"Obtaining vector fields","title":"Obtaining vector fields","text":"\n\n\n\n\n

    Flux reconstruction and visualization for the Laplace operator

    https://github.com/j-fu/VoronoiFVM.jl/blob/master/pluto-examples/outflow

    \n\n\n

    We demonstrate the reconstruction of the gradient vector field from the solution of the Laplace operator and its visualization.

    \n\n\n\n\n
    begin\n    import Pkg as _Pkg\n    haskey(ENV, \"PLUTO_PROJECT\") && _Pkg.activate(ENV[\"PLUTO_PROJECT\"])\n    using Revise\n    using Test\n    using SimplexGridFactory, Triangulate, ExtendableGrids, VoronoiFVM\n    using PlutoUI, GridVisualize\n    using CairoMakie\n    CairoMakie.activate!(; type = \"svg\", visible = false)\n    GridVisualize.default_plotter!(CairoMakie)\nend;
    \n\n\n","category":"page"},{"location":"plutostatichtml_examples/flux-reconstruction/#Grid","page":"Obtaining vector fields","title":"Grid","text":"","category":"section"},{"location":"plutostatichtml_examples/flux-reconstruction/","page":"Obtaining vector fields","title":"Obtaining vector fields","text":"
    \n
    \n\n\n

    Define a \"Swiss cheese domain\" with punched-out holes, where each hole boundary corresponds to a different boundary condition.

    \n\n
    function swiss_cheese_2d()\n    function circlehole!(builder, center, radius; n = 20)\n        points = [point!(builder, center[1] + radius * sin(t), center[2] + radius * cos(t))\n                  for t in range(0, 2π; length = n)]\n        for i = 1:(n - 1)\n            facet!(builder, points[i], points[i + 1])\n        end\n        facet!(builder, points[end], points[1])\n        holepoint!(builder, center)\n    end\n\n    builder = SimplexGridBuilder(; Generator = Triangulate)\n    cellregion!(builder, 1)\n    maxvolume!(builder, 0.1)\n    regionpoint!(builder, 0.1, 0.1)\n\n    p1 = point!(builder, 0, 0)\n    p2 = point!(builder, 10, 0)\n    p3 = point!(builder, 10, 10)\n    p4 = point!(builder, 0, 10)\n\n    facetregion!(builder, 1)\n    facet!(builder, p1, p2)\n    facet!(builder, p2, p3)\n    facet!(builder, p3, p4)\n    facet!(builder, p4, p1)\n\n    holes = [1.0 2.0\n             8.0 9.0\n             2.0 8.0\n             8.0 4.0\n             9.0 1.0\n             3.0 4.0\n             4.0 6.0\n             7.0 9.0\n             4.0 7.0\n             7.0 5.0\n             2.0 1.0\n             4.0 1.0\n             4.0 8.0\n             3.0 6.0\n             4.0 9.0\n             6.0 9.0\n             3.0 5.0\n             1.0 4.0]'\n\n    radii = [\n        0.15,\n        0.15,\n        0.1,\n        0.35,\n        0.2,\n        0.3,\n        0.1,\n        0.4,\n        0.1,\n        0.4,\n        0.2,\n        0.2,\n        0.2,\n        0.35,\n        0.15,\n        0.25,\n        0.15,\n        0.25,\n    ]\n\n    for i = 1:length(radii)\n        facetregion!(builder, i + 1)\n        circlehole!(builder, holes[:, i], radii[i])\n    end\n\n    simplexgrid(builder)\nend
    \n
    swiss_cheese_2d (generic function with 1 method)
    \n\n
    grid = swiss_cheese_2d()
    \n
    ExtendableGrids.ExtendableGrid{Float64, Int32};\ndim: 2 nodes: 1434 cells: 2443 bfaces: 459\n\n
    \n\n\n\n\n","category":"page"},{"location":"plutostatichtml_examples/flux-reconstruction/#System-solution","page":"Obtaining vector fields","title":"System + solution","text":"","category":"section"},{"location":"plutostatichtml_examples/flux-reconstruction/","page":"Obtaining vector fields","title":"Obtaining vector fields","text":"
    \n
    \n\n
    mutable struct Params\n    val11::Float64\nend
    \n\n\n
    params = Params(5)
    \n
    Params(5.0)
    \n\n\n

    Simple flux function for Laplace operator

    \n\n
    flux(y, u, edge, data) = y[1] = u[1, 1] - u[1, 2];
    \n\n\n\n

    At hole #11, the value will be bound to a slider defined below

    \n\n
    function bc(y, u, bnode, data)\n    boundary_dirichlet!(y, u, bnode; region = 2, value = 10.0)\n    boundary_dirichlet!(y, u, bnode; region = 3, value = 0.0)\n    boundary_dirichlet!(y, u, bnode; region = 11, value = data.val11)\nend
    \n
    bc (generic function with 1 method)
    \n\n\n

    Define a finite volume system with Dirichlet boundary conditions at some of the holes

    \n\n
    system = VoronoiFVM.System(grid; flux = flux, species = 1, bcondition = bc, data = params)
    \n
    VoronoiFVM.System{Float64, Float64, Int32, Int64, Matrix{Int32}, Matrix{Float64}}(num_species=1)\n
    \n\n\n

    Solve, and trigger solution upon boundary value change

    \n\n
    begin\n    params.val11 = val11\n    sol = solve(system)\nend;
    \n\n\n
    @test params.val11 != 5.0 || isapprox(sum(sol), 7842.2173682050525; rtol = 1.0e-12)
    \n
    Test Passed
    \n\n","category":"page"},{"location":"plutostatichtml_examples/flux-reconstruction/#Flux-reconstruction","page":"Obtaining vector fields","title":"Flux reconstruction","text":"","category":"section"},{"location":"plutostatichtml_examples/flux-reconstruction/","page":"Obtaining vector fields","title":"Obtaining vector fields","text":"
    \n
    \n\n\n

    Reconstruct the node flux. It is a $d\\times n_{spec}\\times n_{nodes}$ tensor. nf[:,ispec,:] then is a vector function representing the flux density of species ispec in each node of the domain. This readily can be fed into GridVisualize.vectorplot.

    The reconstruction is based on the \"magic formula\" R. Eymard, T. Gallouet, R. Herbin, IMA Journal of Numerical Analysis (2006) 26, 326−353 (Arxive version), Lemma 2.4 .

    \n\n
    nf = nodeflux(system, sol)
    \n
    2×1×1434 Array{Float64, 3}:\n[:, :, 1] =\n  0.05468367090633706\n -0.05468367090633706\n\n[:, :, 2] =\n 0.01852257911924369\n 0.014818063295393813\n\n[:, :, 3] =\n 0.0\n 0.0\n\n;;; … \n\n[:, :, 1432] =\n 0.020523328431052094\n 0.036034112502081016\n\n[:, :, 1433] =\n 0.028906869669800477\n 0.012529162794698063\n\n[:, :, 1434] =\n 0.03545781788403497\n 0.0342143068249023
    \n\n
    @test params.val11 != 5.0 || isapprox(sum(nf), 978.000534849034; rtol = 1.0e-14)
    \n
    Test Passed
    \n\n
    vis = GridVisualizer(; dim = 2, resolution = (400, 400))
    \nGridVisualizer(Plotter=CairoMakie)\n\n\n

    $v_{11}:$5.0

    \n\n\n

    Joint plot of solution and flux reconstruction

    \n\n
    begin\n    scalarplot!(vis, grid, sol[1, :]; levels = 9, colormap = :summer, clear = true)\n    vectorplot!(vis, grid, nf[:, 1, :]; clear = false, spacing = 0.5, vscale = 1.5)\n    reveal(vis)\nend
    \n\n\n\n

    The 1D case

    \n\n
    src(x) = exp(-x^2 / 0.01)
    \n
    src (generic function with 1 method)
    \n\n
    source1d(y, node) = y[1] = src(node[1])
    \n
    source1d (generic function with 1 method)
    \n\n
    flux1d(y, u, edge) = y[1] = u[1, 1]^2 - u[1, 2]^2
    \n
    flux1d (generic function with 1 method)
    \n\n
    function bc1d(y, u, bnode)\n    boundary_dirichlet!(y, u, bnode; region = 1, value = 0.01)\n    boundary_dirichlet!(y, u, bnode; region = 2, value = 0.01)\nend
    \n
    bc1d (generic function with 1 method)
    \n\n
    grid1d = simplexgrid(-1:0.01:1)
    \n
    ExtendableGrids.ExtendableGrid{Float64, Int32};\ndim: 1 nodes: 201 cells: 200 bfaces: 2\n\n
    \n\n
    sys1d = VoronoiFVM.System(grid1d;\n                          flux = flux1d,\n                          bcondition = bc1d,\n                          source = source1d,\n                          species = [1],)
    \n
    VoronoiFVM.System{Float64, Float64, Int32, Int64, Matrix{Int32}, Matrix{Float64}}(num_species=1)\n
    \n\n
    sol1d = solve(sys1d; inival = 0.1)
    \n
    1×201 Matrix{Float64}:\n 0.01  0.0314043  0.0432719  0.0525231  …  0.0525231  0.0432719  0.0314043  0.01
    \n\n
    nf1d = nodeflux(sys1d, sol1d)
    \n
    1×1×201 Array{Float64, 3}:\n[:, :, 1] =\n -0.08862269254527583\n\n[:, :, 2] =\n -0.08862269254527581\n\n[:, :, 3] =\n -0.08862269254527581\n\n;;; … \n\n[:, :, 199] =\n 0.08862269254527581\n\n[:, :, 200] =\n 0.08862269254527581\n\n[:, :, 201] =\n 0.08862269254527583
    \n\n
    let\n    vis1d = GridVisualizer(; dim = 1, resolution = (500, 250), legend = :lt)\n    scalarplot!(vis1d, grid1d, map(src, grid1d); label = \"rhs\", color = :blue)\n    scalarplot!(vis1d, grid1d, sol1d[1, :]; label = \"solution\", color = :red, clear = false)\n    vectorplot!(vis1d, grid1d, nf1d[:, 1, :]; label = \"flux\", clear = false, color = :green)\n    reveal(vis1d)\nend
    \n\n\n
    @test nf1d[1, 1, 101] ≈ 0.0
    \n
    Test Passed
    \n\n
    @test nf1d[1, 1, 1] ≈ -nf1d[1, 1, end]
    \n
    Test Passed
    \n\n\n
    \n\n\n
    \n
    \n

    Built with Julia 1.10.2 and

    \nCairoMakie 0.11.5
    \nExtendableGrids 1.2.3
    \nGridVisualize 1.5.0
    \nPkg 1.10.0
    \nPlutoUI 0.7.55
    \nRevise 3.5.13
    \nSimplexGridFactory 1.0.0
    \nTriangulate 2.3.2
    \nVoronoiFVM 1.16.0\n
    \n\n","category":"page"},{"location":"plutostatichtml_examples/flux-reconstruction/","page":"Obtaining vector fields","title":"Obtaining vector fields","text":"EditURL = \"https://github.com/j-fu/VoronoiFVM.jl/blob/main/nothing\"","category":"page"},{"location":"allindex/#Index","page":"Index","title":"Index","text":"","category":"section"},{"location":"allindex/#Exported","page":"Index","title":"Exported","text":"","category":"section"},{"location":"allindex/","page":"Index","title":"Index","text":"VoronoiFVM","category":"page"},{"location":"allindex/#VoronoiFVM","page":"Index","title":"VoronoiFVM","text":"BICGstabIteration\nCGIteration\nContinuousQuantity\nDirectSolver\nDiscontinuousQuantity\nEquationBlock\nEquationwise\nGMRESIteration\nInterfaceQuantity\nNewtonControl\nNewtonSolverHistory\nNoBlock\nPointBlock\nSolverControl\nTestFunctionFactory\nTransientSolution\nTransientSolverHistory\nbfacenodefactors\nbfacevelocities\nboundary_dirichlet!\nboundary_neumann!\nboundary_robin!\ncalc_divergences\ncartesian!\ncircular_symmetric!\ncoordinates\ndata\ndetails\ndof\nedgelength\nedgevelocities\nembed!\nembedparam\nenable_boundary_species!\nenable_discontinuous_species!\nenable_species!\neval_jacobian!\neval_rhs!\nevaluate_residual_and_jacobian\nevolve!\nfbernoulli\nfbernoulli_pm\nfixed_timesteps!\nfreqdomain_impedance\ngetdof\nh1norm\nh1seminorm\nhasoutflownode\nhistory\nhistory_details\nhistory_summary\nimpedance\ninplace_linsolve!\nintegrate\nisoutflownode\nl2h1norm\nl2h1seminorm\nl2norm\nlpnorm\nlpw1pnorm\nlpw1pseminorm\nmass_matrix\nmeas\nmeasurement_derivative\nnodeflux\nnodevolumes\nnum_dof\nnum_species\noutflownode\nparameters\npartitioning\nphysics!\nplothistory\nprepare_diffeq!\nproject\nramp\nregion\nsetdof!\nsolve\nsolve!\nspherical_symmetric!\nsubgrids\ntestfunction\ntime\ntransient_solution\nunknown_indices\nunknowns\nupdate_grid!\nvalue\nviewK\nviewL\nviews\nw1pnorm\nw1pseminorm\n\n\n\n\n\n","category":"module"},{"location":"allindex/#Types-and-Constructors","page":"Index","title":"Types and Constructors","text":"","category":"section"},{"location":"allindex/","page":"Index","title":"Index","text":"Modules = [VoronoiFVM]\nOrder=[:type]","category":"page"},{"location":"allindex/#Constants","page":"Index","title":"Constants","text":"","category":"section"},{"location":"allindex/","page":"Index","title":"Index","text":"Modules = [VoronoiFVM]\nOrder=[:constant]","category":"page"},{"location":"allindex/#Methods","page":"Index","title":"Methods","text":"","category":"section"},{"location":"allindex/","page":"Index","title":"Index","text":"Modules = [VoronoiFVM]\nOrder=[:function]","category":"page"},{"location":"module_examples/Example002_EdgeReaction/#002:-check-edge-reaction","page":"002: check edge reaction","title":"002: check edge reaction","text":"","category":"section"},{"location":"module_examples/Example002_EdgeReaction/","page":"002: check edge reaction","title":"002: check edge reaction","text":"module Example002_EdgeReaction\n\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing ExtendableSparse\nusing GridVisualize\nusing LinearAlgebra\nusing SimplexGridFactory\nusing Triangulate\n\nfunction main(; nref = 0, dim = 2, Plotter = nothing, verbose = \"and\", case = :compare_max, assembly = :edgewise)\n X = 0:(0.25 * 2.0^-nref):1\n i0::Int = 0\n i1::Int = 0\n if dim == 1\n grid = simplexgrid(X)\n i0 = 1\n i1 = 2\n elseif dim == 2\n b = SimplexGridBuilder(; Generator = Triangulate)\n p00 = point!(b, 0, 0)\n p10 = point!(b, 1, 0)\n p11 = point!(b, 1, 1)\n p01 = point!(b, 0, 1)\n pxx = point!(b, 0.3, 0.3)\n\n facetregion!(b, 1)\n facet!(b, p00, p10)\n facetregion!(b, 2)\n facet!(b, p10, p11)\n facetregion!(b, 3)\n facet!(b, p11, p01)\n facetregion!(b, 4)\n facet!(b, p01, p00)\n grid = simplexgrid(b; maxvolume = 0.01 * 4.0^(-nref))\n i0 = 1\n i1 = 3\n elseif dim == 3\n grid = simplexgrid(X, X, X)\n i0 = 5\n i1 = 6\n end\n\n function storage!(y, u, node)\n y[1] = u[1]\n end\n\n function flux!(y, u, edge)\n y[1] = u[1, 1] - u[1, 2]\n end","category":"page"},{"location":"module_examples/Example002_EdgeReaction/","page":"002: check edge reaction","title":"002: check edge reaction","text":"Three ways to give a constant reaction term. As a consequence, these need to yield the same solution. 1: classical node reaction, multiplied by control volume size","category":"page"},{"location":"module_examples/Example002_EdgeReaction/","page":"002: check edge reaction","title":"002: check edge reaction","text":" function reaction!(y, u, node)\n y[1] = -1\n end","category":"page"},{"location":"module_examples/Example002_EdgeReaction/","page":"002: check edge reaction","title":"002: check edge reaction","text":"2: Edge reaction. Here we give it as a constant, and wie need to turn the multiplication with σ/h into a multiplication with the half diamond volume.","category":"page"},{"location":"module_examples/Example002_EdgeReaction/","page":"002: check edge reaction","title":"002: check edge reaction","text":"Half diamond volume calculation /|\n / | \n / |s \n –––- h A=sh/2d . Our formfactor: σ=s/h => A=σh^2","category":"page"},{"location":"module_examples/Example002_EdgeReaction/","page":"002: check edge reaction","title":"002: check edge reaction","text":"make transfer area to volume","category":"page"},{"location":"module_examples/Example002_EdgeReaction/","page":"002: check edge reaction","title":"002: check edge reaction","text":"τ=1/h v= sh/2d = σh^2/2d","category":"page"},{"location":"module_examples/Example002_EdgeReaction/","page":"002: check edge reaction","title":"002: check edge reaction","text":" function edgereaction!(y, u, edge)\n h = meas(edge)\n y[1] = -1 * h^2 / (2 * dim)\n end","category":"page"},{"location":"module_examples/Example002_EdgeReaction/","page":"002: check edge reaction","title":"002: check edge reaction","text":"3: \"Joule heat:\" |∇ϕ|^2=1 after 3.17 in Bradji/Herbin Here we divide twice by \"h\" to get the constant squared gradient. The multiplication with dim in 3.17 compensates the division we had before","category":"page"},{"location":"module_examples/Example002_EdgeReaction/","page":"002: check edge reaction","title":"002: check edge reaction","text":" ϕ = grid[Coordinates][1, :]\n\n function edgereaction2!(y, u, edge)\n ϕK = ϕ[edge.node[1]]\n ϕL = ϕ[edge.node[2]]\n y[1] = -(ϕK - ϕL) * (ϕK - ϕL) / 2\n end\n\n if case == :compare_max\n function bcondition!(y, u, node)\n boundary_dirichlet!(y, u, node; species = 1, region = 1, value = 0)\n boundary_dirichlet!(y, u, node; species = 1, region = 2, value = 0)\n boundary_dirichlet!(y, u, node; species = 1, region = 3, value = 0)\n boundary_dirichlet!(y, u, node; species = 1, region = 4, value = 0)\n boundary_dirichlet!(y, u, node; species = 1, region = 5, value = 0)\n boundary_dirichlet!(y, u, node; species = 1, region = 6, value = 0)\n end\n\n sys_noderea = VoronoiFVM.System(grid; bcondition = bcondition!, flux = flux!,\n reaction = reaction!, storage = storage!,\n species = [1], is_linear = true, assembly)\n sys_edgerea = VoronoiFVM.System(grid; bcondition = bcondition!, flux = flux!,\n edgereaction = edgereaction!, storage = storage!,\n species = [1], is_linear = true, assembly)\n sys_edgerea2 = VoronoiFVM.System(grid; bcondition = bcondition!, flux = flux!,\n edgereaction = edgereaction2!, storage = storage!,\n species = [1], is_linear = true, assembly)\n\n sol_noderea = solve(sys_noderea; verbose)\n sol_edgerea = solve(sys_edgerea; verbose)\n sol_edgerea2 = solve(sys_edgerea2; verbose)\n\n vis = GridVisualizer(; Plotter, layout = (2, 2))\n scalarplot!(vis[1, 1], grid, sol_noderea[1, :]; title = \"node reaction\",\n colormap = :hot)\n scalarplot!(vis[2, 1], grid, sol_edgerea[1, :]; title = \"edgerea1\", colormap = :hot)\n scalarplot!(vis[1, 2], grid, sol_edgerea2[1, :]; title = \"edgerea2\",\n colormap = :hot)\n\n reveal(vis)\n return maximum.([sol_noderea, sol_edgerea, sol_edgerea2])\n end\n\n if case == :compare_flux\n function bcondition2!(y, u, node)\n boundary_dirichlet!(y, u, node; species = 1, region = i1, value = 0)\n end\n\n sys2_noderea = VoronoiFVM.System(grid; bcondition = bcondition2!, flux = flux!,\n reaction = reaction!, storage = storage!,\n species = [1], is_linear = true)\n sys2_edgerea = VoronoiFVM.System(grid; bcondition = bcondition2!, flux = flux!,\n edgereaction = edgereaction!, storage = storage!,\n species = [1], is_linear = true)\n sys2_edgerea2 = VoronoiFVM.System(grid; bcondition = bcondition2!, flux = flux!,\n edgereaction = edgereaction2!, storage = storage!,\n species = [1], is_linear = true)\n\n sol2_noderea = solve(sys2_noderea; verbose)\n sol2_edgerea = solve(sys2_edgerea; verbose)\n sol2_edgerea2 = solve(sys2_edgerea2; verbose)\n\n tfac2_noderea = TestFunctionFactory(sys2_noderea)\n tfc2_noderea = testfunction(tfac2_noderea, [i0], [i1])\n\n tfac2_edgerea = TestFunctionFactory(sys2_edgerea)\n tfc2_edgerea = testfunction(tfac2_edgerea, [i0], [i1])\n\n tfac2_edgerea2 = TestFunctionFactory(sys2_edgerea2)\n tfc2_edgerea2 = testfunction(tfac2_edgerea2, [i0], [i1])\n\n vis = GridVisualizer(; Plotter, layout = (2, 2))\n scalarplot!(vis[1, 1], grid, sol2_noderea[1, :]; title = \"node reaction\",\n colormap = :hot)\n scalarplot!(vis[2, 1], grid, sol2_edgerea[1, :]; title = \"edgerea1\",\n colormap = :hot)\n scalarplot!(vis[1, 2], grid, sol2_edgerea2[1, :]; title = \"edgerea2\",\n colormap = :hot)\n reveal(vis)\n\n I_noderea = integrate(sys2_noderea, tfc2_noderea, sol2_noderea)\n I_edgerea = integrate(sys2_edgerea, tfc2_edgerea, sol2_edgerea)\n I_edgerea2 = integrate(sys2_edgerea2, tfc2_edgerea2, sol2_edgerea2)\n\n return I_noderea, I_edgerea, I_edgerea2\n end\nend\n\nusing Test\nfunction runtests()\n res = fill(false, 3)\n for dim = 1:3\n result_max = main(; case = :compare_max, assembly = :cellwise)\n result_flux = main(; case = :compare_flux, assembly = :cellwise)\n res[dim] = isapprox(result_max[1], result_max[2]; atol = 1.0e-6) &&\n isapprox(result_max[1], result_max[3]; atol = 1.0e-3) &&\n isapprox(result_flux[1], result_flux[2]; atol = 1.0e-10) &&\n isapprox(result_flux[1], result_flux[3]; atol = 1.0e-10)\n end\n res1 = all(a -> a, res)\n\n res = fill(false, 3)\n for dim = 1:3\n result_max = main(; case = :compare_max, assembly = :edgwise)\n result_flux = main(; case = :compare_flux, assembly = :edgwise)\n res[dim] = isapprox(result_max[1], result_max[2]; atol = 1.0e-6) &&\n isapprox(result_max[1], result_max[3]; atol = 1.0e-3) &&\n isapprox(result_flux[1], result_flux[2]; atol = 1.0e-10) &&\n isapprox(result_flux[1], result_flux[3]; atol = 1.0e-10)\n end\n res2 = all(a -> a, res)\n\n @test res1 && res2\nend\n\nend","category":"page"},{"location":"module_examples/Example002_EdgeReaction/","page":"002: check edge reaction","title":"002: check edge reaction","text":"","category":"page"},{"location":"module_examples/Example002_EdgeReaction/","page":"002: check edge reaction","title":"002: check edge reaction","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/#422:-Drift-Diffusion-with-Discontinuous-and-Interface-Potentials","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":"","category":"section"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":"(source code)","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":"Nondimensionalized semiconductor device equations (with artificial doping) with Discontinuousquantities and additional Interfacequantities.","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":"module Example422_InterfaceQuantities\n\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\n\nfunction main(; n = 5, Plotter = nothing, tend = 20.0, unknown_storage = :sparse,\n reactionN = 5.0e0, reactionP = 5.0e0, assembly = :edgewise)\n\n ################################################################################\n #### grid\n ################################################################################\n h1 = 1.0\n h2 = 1.0\n h_total = h1 + h2","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":"region numbers","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":" region1 = 1\n region2 = 2\n regions = [region1, region2]\n numberOfRegions = length(regions)","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":"boundary region numbers","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":" bregion1 = 1\n bregion2 = 2\n bjunction = 3\n\n coord_1 = collect(range(0.0; stop = h1, length = n))\n coord_2 = collect(range(h1; stop = h1 + h2, length = n))\n coord = glue(coord_1, coord_2)\n\n grid = simplexgrid(coord)","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":"specify inner regions","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":" cellmask!(grid, [0.0], [h1], region1)\n cellmask!(grid, [h1], [h1 + h2], region2)","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":"specify outer regions","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":" bfacemask!(grid, [0.0], [0.0], bregion1)\n bfacemask!(grid, [h_total], [h_total], bregion2)","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":"inner interfaces","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":" bfacemask!(grid, [h1], [h1], bjunction)\n\n #gridplot(grid, Plotter = nothing, legend=:rt)\n\n ################################################################################\n ######### system\n ################################################################################\n\n sys = VoronoiFVM.System(grid; unknown_storage = unknown_storage, assembly = assembly)\n iphin = DiscontinuousQuantity(sys, 1:numberOfRegions; id = 1)\n iphip = DiscontinuousQuantity(sys, 1:numberOfRegions; id = 2)\n iphinb = InterfaceQuantity(sys, [bjunction]; id = 3)\n iphipb = InterfaceQuantity(sys, [bjunction]; id = 4)\n ipsi = ContinuousQuantity(sys, 1:numberOfRegions; id = 5)\n\n NA = [10.0, 0.0]\n ND = [0.0, 10.0]\n\n function storage!(f, u, node)\n etan = -((u[iphin] - u[ipsi]))\n etap = ((u[iphip] - u[ipsi]))\n\n f[iphin] = -exp(etan)\n f[iphip] = exp(etap)\n\n f[ipsi] = 0.0\n end\n\n function reaction!(f, u, node)\n etan = -((u[iphin] - u[ipsi]))\n etap = ((u[iphip] - u[ipsi]))\n\n f[ipsi] = -(ND[node.region] - exp(etan) + exp(etap) - NA[node.region])\n ########################\n r0 = 1.0e-4\n recomb = r0 * exp(etan) * exp(etap)\n\n f[iphin] = -recomb\n f[iphip] = recomb\n end\n\n function flux!(f, u, node)\n f[ipsi] = -(u[ipsi, 2] - u[ipsi, 1])\n\n ########################\n bp, bm = fbernoulli_pm(-(u[ipsi, 2] - u[ipsi, 1]))\n\n etan1 = -((u[iphin, 1] - u[ipsi, 1]))\n etap1 = ((u[iphip, 1] - u[ipsi, 1]))\n\n etan2 = -((u[iphin, 2] - u[ipsi, 2]))\n etap2 = ((u[iphip, 2] - u[ipsi, 2]))\n\n f[iphin] = (bm * exp(etan2) - bp * exp(etan1))\n f[iphip] = -(bp * exp(etap2) - bm * exp(etap1))\n end\n\n function breaction!(f, u, bnode)\n if bnode.region == bjunction","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":"left values","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":" nleft = exp(-((u[iphin, 1] - u[ipsi])))\n pleft = exp(((u[iphip, 1] - u[ipsi])))","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":"interface species","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":" n_interf = exp(-((u[iphinb] - u[ipsi])))\n p_interf = exp(((u[iphipb] - u[ipsi])))","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":"right values","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":" nright = exp(-((u[iphin, 2] - u[ipsi])))\n pright = exp(((u[iphip, 2] - u[ipsi])))\n ################","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":"left and right reaction for n","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":" f[iphin, 1] = reactionN * (nleft - n_interf)\n f[iphin, 2] = reactionN * (nright - n_interf)","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":"left and right reaction for p","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":" f[iphip, 1] = reactionP * (pleft - p_interf)\n f[iphip, 2] = reactionP * (pright - p_interf)","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":"interface species reaction","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":" f[iphinb] = -(f[iphin, 1] + f[iphin, 2])\n f[iphipb] = -(f[iphip, 1] + f[iphip, 2])\n end\n end\n\n function bstorage!(f, u, bnode)\n f[ipsi] = 0.0\n\n if bnode.region == bjunction\n etan = -((u[iphinb] - u[ipsi]))\n etap = ((u[iphipb] - u[ipsi]))\n\n f[iphinb] = -exp(etan)\n f[iphipb] = exp(etap)\n end\n end\n\n physics!(sys,\n VoronoiFVM.Physics(; flux = flux!,\n storage = storage!,\n reaction = reaction!,\n breaction = breaction!,\n bstorage = bstorage!))\n\n boundary_dirichlet!(sys, iphin, bregion1, 4.0)\n boundary_dirichlet!(sys, iphip, bregion1, 4.0)\n boundary_dirichlet!(sys, ipsi, bregion1, 0.0)\n boundary_dirichlet!(sys, iphin, bregion2, 0.0)\n boundary_dirichlet!(sys, iphip, bregion2, 0.0)\n boundary_dirichlet!(sys, ipsi, bregion2, 5.0)\n\n ################################################################################\n ######### time loop\n ################################################################################\n\n # Create a solution array\n sol = unknowns(sys)\n sol .= 0.0\n t0 = 1.0e-6\n ntsteps = 10\n tvalues = range(t0; stop = tend, length = ntsteps)\n\n for istep = 2:ntsteps\n t = tvalues[istep] # Actual time\n Δt = t - tvalues[istep - 1] # Time step size\n\n #println(\"Δt = \", t)\n\n sol = solve(sys; inival = sol, tstep = Δt)\n end # time loop\n\n ################################################################################\n ######### Bias Loop\n ################################################################################\n biasval = range(0; stop = 2.0, length = 10)\n Idspec = zeros(0)\n\n for Δu in biasval\n boundary_dirichlet!(sys, iphin, bregion1, 4.0 + Δu)\n boundary_dirichlet!(sys, iphip, bregion1, 4.0 + Δu)\n boundary_dirichlet!(sys, ipsi, bregion1, 0.0 + Δu)\n\n #println(\"Δu = \", Δu)\n\n sol = solve(sys; inival = sol)\n\n # get current\n factory = TestFunctionFactory(sys)\n tf = testfunction(factory, [1], [2])\n I = integrate(sys, tf, sol)\n\n val = 0.0\n for ii = 1:(length(I) - 1)\n val = val + I[ii]\n end\n\n push!(Idspec, val)\n end # bias loop\n\n ################################################################################\n ######### Plotting\n ################################################################################\n\n vis = GridVisualizer(; Plotter = Plotter, layout = (2, 1))\n\n subgridBulk = subgrids(iphin, sys)\n phin_sol = views(sol, iphin, subgridBulk, sys)\n phip_sol = views(sol, iphip, subgridBulk, sys)\n psi_sol = views(sol, ipsi, subgridBulk, sys)\n\n for i in eachindex(phin_sol)\n scalarplot!(vis[1, 1], subgridBulk[i], phin_sol[i]; clear = false, color = :green)\n scalarplot!(vis[1, 1], subgridBulk[i], phip_sol[i]; clear = false, color = :red)\n scalarplot!(vis[1, 1], subgridBulk[i], psi_sol[i]; clear = false, color = :blue)\n end\n\n scalarplot!(vis[2, 1], biasval, Idspec; clear = false, color = :red)\n\n bgrid = subgrids(iphinb, sys)\n sol_bound = views(sol, iphinb, bgrid, sys)\n\n return sol_bound[1]\nend # main\n\nusing Test\nfunction runtests()\n testval = 0.35545473758267826\n @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval &&\n main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval\nend\n\nend # module","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":"","category":"page"},{"location":"module_examples/Example422_InterfaceQuantities/","page":"422: Drift-Diffusion with Discontinuous and Interface Potentials","title":"422: Drift-Diffusion with Discontinuous and Interface Potentials","text":"This page was generated using Literate.jl.","category":"page"},{"location":"solver/#Solvers","page":"Solvers","title":"Solvers","text":"","category":"section"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"The package comes with a built-in solve method which solves stationary problems, homotopy embedding problems and transient problems via the implicit Euler method. In particular, the transient solver allows to use nonlinear storage terms.","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"Alternatively, OrdinaryDiffEq.jl based solvers can be used for transient problems.","category":"page"},{"location":"solver/#Built-in-solver","page":"Solvers","title":"Built-in solver","text":"","category":"section"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"This solver and its default parameters are tuned for robustness, possibly at the expense of solution speed. Careful tuning of the parameters, or – in the case of transient problems – the choice of a OrdinaryDiffEq.jl based solver can significantly improve the performance.","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"Overview:","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"Solve method\nSolver control\nLinear solver stragies\nBlock preconditioning\nHistory handling\nMatrix extration","category":"page"},{"location":"solver/#Solve-method","page":"Solvers","title":"Solve method","text":"","category":"section"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"VoronoiFVM.solve(system::VoronoiFVM.AbstractSystem; kwargs...)","category":"page"},{"location":"solver/#CommonSolve.solve-Tuple{VoronoiFVM.AbstractSystem}","page":"Solvers","title":"CommonSolve.solve","text":"solve(system; kwargs...)\n\nBuilt-in solution method for VoronoiFVM.System. \n\nKeyword arguments:\n\nGeneral for all solvers \ninival (default: 0) : Array created via unknowns or number giving the initial value.\ncontrol (default: nothing): Pass instance of SolverControl\nAll elements of SolverControl can be used as kwargs. Eventually overwrites values given via control\nparams: Parameters (Parameter handling is experimental and may change)\nStationary solver: Invoked if neither times nor embed, nor tstep are given as keyword argument.\ntime (default: 0.0): Set time value.\nReturns a DenseSolutionArray or SparseSolutionArray\nEmbedding (homotopy) solver: Invoked if embed kwarg is given. Use homotopy embedding + damped Newton's method to solve stationary problem or to solve series of parameter dependent problems. Parameter step control is performed according to solver control data. kwargs and default values are:\nembed (default: nothing ): vector of parameter values to be reached exactly\nIn addition, all kwargs of the implicit Euler solver (besides times) are handled. Returns a transient solution object sol containing the stored solution(s), see TransientSolution.\nImplicit Euler transient solver: Invoked if times kwarg is given. Use implicit Euler method + damped Newton's method to solve time dependent problem. Time step control is performed according to solver control data. kwargs and default values are:\ntimes (default: nothing ): vector of time values to be reached exactly\npre (default: (sol,t)->nothing ): callback invoked before each time step\npost (default: (sol,oldsol, t, Δt)->nothing ): callback invoked after each time step\nsample (default: (sol,t)->nothing ): callback invoked after timestep for all times in times[2:end].\ndelta (default: (system, u,v,t, Δt)->norm(sys,u-v,Inf) ): Value used to control the time step size Δu\nIf control.handle_error is true, if time step solution throws an error, stepsize is lowered, and step solution is called again with a smaller time value. If control.Δt Δu_max_factor*Δu_opt will be rejected.\n\nforce_first_step::Bool: Force first timestep.\n\nnum_final_steps::Int64: Number of final steps to adjust at end of time interval in order to prevent breakdown of step size.\n\nhandle_exceptions::Bool: Handle exceptions during transient solver and parameter embedding. If true, exceptions in Newton solves are caught, the embedding resp. time step is lowered, and solution is retried. Moreover, if embedding or time stepping fails (e.g. due to reaching minimal step size), a warning is issued, and a solution is returned with all steps calculated so far.\nOtherwise (by default) errors are thrown.\n\nstore_all::Bool: Store all steps of transient/embedding problem:\n\nin_memory::Bool: Store transient/embedding solution in memory\n\nlog::Any: Record history\n\nedge_cutoff::Float64: Edge parameter cutoff for rectangular triangles.\n\npre::Function: Function pre(sol,t) called before time/embedding step\n\npost::Function: Function post(sol,oldsol,t,Δt) called after successful time/embedding step\n\nsample::Function: Function sample(sol,t) to be called for each t in times[2:end]\n\ndelta::Function: Time step error estimator. A function Δu=delta(system,u,uold,t,Δt) to calculate Δu.\n\ntol_absolute::Union{Nothing, Float64}\ntol_relative::Union{Nothing, Float64}\ndamp::Union{Nothing, Float64}\ndamp_grow::Union{Nothing, Float64}\nmax_iterations::Union{Nothing, Int64}\ntol_linear::Union{Nothing, Float64}\nmax_lureuse::Union{Nothing, Int64}\nmynorm::Union{Nothing, Function}\nmyrnorm::Union{Nothing, Function}\n\n\n\n\n\n","category":"type"},{"location":"solver/#Linear-solver-strategies","page":"Solvers","title":"Linear solver strategies","text":"","category":"section"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"VoronoiFVM.LinearSolverStrategy\nDirectSolver\nCGIteration\nBICGstabIteration\nGMRESIteration","category":"page"},{"location":"solver/#VoronoiFVM.LinearSolverStrategy","page":"Solvers","title":"VoronoiFVM.LinearSolverStrategy","text":"VoronoiFVM.LinearSolverStrategy\n\nAn linear solver strategy provides the possibility to construct SolverControl objects as follows:\n\n SolverControl(strategy,sys;kwargs...)\n\n, e.g.\n\n SolverControl(GMRESIteration(UMFPackFactorization(), EquationBlock()),sys; kwargs...)\n\nA linear solver strategy combines a Krylov method with a preconditioner which by default is calculated from the linearization of the initial value of the Newton iteration. For coupled systems, a blocking strategy can be chosen. The EquationBlock strategy calculates preconditioners or LU factorization separately for each species equation and combines them to a block Jacobi preconditioner. The PointBlock strategy treats the linear system as consisting of nspecies x nspecies blocks. \n\nWhich is the best strategy, depends on the space dimension. The following is a rule of thumb for choosing strategies\n\nFor 1D problems use direct solvers\nFor 2D stationary problems, use direct solvers, for transient problems consider iterative solvers which can take advantage of the diagonal dominance of the implicit timestep problem\nFor 3D problems avoid direct solvers\n\nCurrently available strategies are:\n\nDirectSolver\nCGIteration\nBICGstabIteration\nGMRESIteration\n\nNotable LU Factorizations/direct solvers are:\n\nUMFPACKFactorization (using LinearSolve)\nKLUFactorization (using LinearSolve)\nSparspakFactorization (using LinearSolve), SparspakLU (using ExtendableSparse,Sparspak)\nMKLPardisoLU (using ExtendableSparse, Pardiso), openmp parallel\nAMGSolver (using AMGCLWrap), openmp parallel\nRLXSolver (using AMGCLWrap), openmp parallel\n\nNotable incomplete factorizations/preconditioners\n\nIncomplete LU factorizations written in Julia:\nILUZeroPreconditioner\nILUTPrecondidtioner (using ExtendableSparse, IncompleteLU)\nAlgebraic multigrid written in Julia: (using ExtendableSparse, AlgebraicMultigrid)\nRS_AMGPreconditioner\nSA_AMGPreconditioner\nAMGCL based preconditioners (using ExtendableSparse, AMGCLWrap), openmp parallel\nAMGCL_AMGPreconditioner\nAMGCL_RLXPreconditioner\n\nBlocking strategies are:\n\nNoBlock\nEquationBlock\nPointBlock\n\n\n\n\n\n","category":"type"},{"location":"solver/#VoronoiFVM.DirectSolver","page":"Solvers","title":"VoronoiFVM.DirectSolver","text":"DirectSolver(;factorization=UMFPACKFactorization())\n\nLU Factorization solver.\n\n\n\n\n\n","category":"type"},{"location":"solver/#VoronoiFVM.CGIteration","page":"Solvers","title":"VoronoiFVM.CGIteration","text":"CGIteration(;factorization=UMFPACKFactorization())\nCGIteration(factorization)\n\nCG Iteration from Krylov.jl via LinearSolve.jl.\n\n\n\n\n\n","category":"type"},{"location":"solver/#VoronoiFVM.BICGstabIteration","page":"Solvers","title":"VoronoiFVM.BICGstabIteration","text":"BICGstabIteration(;factorization=UMFPACKFactorization())\nBICGstabIteration(factorization)\n\nBICGstab Iteration from Krylov.jl via LinearSolve.jl.\n\n\n\n\n\n","category":"type"},{"location":"solver/#VoronoiFVM.GMRESIteration","page":"Solvers","title":"VoronoiFVM.GMRESIteration","text":"GMRESIteration(;factorization=ILUZeroFactorization(), memory=20, restart=true)\nGMRESIteration(factorization; memory=20, restart=true)\n\nGMRES Iteration from Krylov.jl via LinearSolve.jl.\n\n\n\n\n\n","category":"type"},{"location":"solver/#Block-preconditioning","page":"Solvers","title":"Block preconditioning","text":"","category":"section"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"This feature is under development as of 1.6.","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"VoronoiFVM.BlockStrategy\nNoBlock\nEquationBlock\nPointBlock\nEquationwise\npartitioning","category":"page"},{"location":"solver/#VoronoiFVM.BlockStrategy","page":"Solvers","title":"VoronoiFVM.BlockStrategy","text":"VoronoiFVM.BlockStrategy\n\nAbstract supertype for various block preconditioning strategies.\n\n\n\n\n\n","category":"type"},{"location":"solver/#VoronoiFVM.NoBlock","page":"Solvers","title":"VoronoiFVM.NoBlock","text":"NoBlock()\n\nNo blocking.\n\n\n\n\n\n","category":"type"},{"location":"solver/#VoronoiFVM.EquationBlock","page":"Solvers","title":"VoronoiFVM.EquationBlock","text":"EquationBlock()\n\nEquation-wise blocking. Can be combined with any preconditioner resp. factorization including direct solvers.\n\n\n\n\n\n","category":"type"},{"location":"solver/#VoronoiFVM.PointBlock","page":"Solvers","title":"VoronoiFVM.PointBlock","text":"PointBlock()\n\nPoint-wise blocking. Currently only together with ILUZeroFactorization. This requires a system with unknown_storage=:dense.\n\n\n\n\n\n","category":"type"},{"location":"solver/#VoronoiFVM.Equationwise","page":"Solvers","title":"VoronoiFVM.Equationwise","text":"struct Equationwise\n\nEquationwise partitioning mode.\n\n\n\n\n\n","category":"type"},{"location":"solver/#VoronoiFVM.partitioning","page":"Solvers","title":"VoronoiFVM.partitioning","text":"partitioning(system, _)\n\n\nCalculate partitioning of system unknowns.\n\n\n\n\n\n","category":"function"},{"location":"solver/#History-handling","page":"Solvers","title":"History handling","text":"","category":"section"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"If log is set to true in solve, the history of newton iterations and time/embedding steps is recorded and. For the respective previous solution step it can be obtained via history(system).","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"NewtonSolverHistory\nTransientSolverHistory\nBase.summary(::NewtonSolverHistory)\nBase.summary(::TransientSolverHistory)\ndetails\nhistory\nhistory_details\nhistory_summary","category":"page"},{"location":"solver/#VoronoiFVM.NewtonSolverHistory","page":"Solvers","title":"VoronoiFVM.NewtonSolverHistory","text":"mutable struct NewtonSolverHistory <: AbstractVector{Float64}\n\nHistory information for one Newton solve of a nonlinear system. As an abstract vector it provides the history of the update norms. See summary and details for other ways to extract information.\n\nnlu::Int64: number of Jacobi matrix factorizations\nnlin::Int64: number of linear iteration steps / factorization solves\ntime::Float64: Elapsed time for solution\ntasm::Float64: Elapsed time for assembly\ntlinsolve::Float64: Elapsed time for linear solve\nupdatenorm::Any: History of norms of u_i+1-u_i\nl1normdiff::Any: History of norms of u_i+1_1 - u_i_1 u_i_1\n\n\n\n\n\n","category":"type"},{"location":"solver/#VoronoiFVM.TransientSolverHistory","page":"Solvers","title":"VoronoiFVM.TransientSolverHistory","text":"mutable struct TransientSolverHistory <: AbstractVector{NewtonSolverHistory}\n\nHistory information for transient solution/parameter embedding\n\nAs an abstract vector it provides the histories of each implicit Euler/embedding step. See summary and details for other ways to extract information.\n\nhistories::Any: Histories of each implicit Euler Newton iteration\ntimes::Any: Time values\nupdates::Any: Update norms used for step control\n\n\n\n\n\n","category":"type"},{"location":"solver/#Base.summary-Tuple{NewtonSolverHistory}","page":"Solvers","title":"Base.summary","text":"summary(h::NewtonSolverHistory)\n\nReturn named tuple summarizing history.\n\n\n\n\n\n","category":"method"},{"location":"solver/#Base.summary-Tuple{TransientSolverHistory}","page":"Solvers","title":"Base.summary","text":"summary(h::TransientSolverHistory)\n\nReturn named tuple summarizing history.\n\n\n\n\n\n","category":"method"},{"location":"solver/#VoronoiFVM.details","page":"Solvers","title":"VoronoiFVM.details","text":"details(h::NewtonSolverHistory)\n\nReturn array of named tuples with info on each iteration step\n\n\n\n\n\ndetails(h::TransientSolverHistory)\n\nReturn array of details of each solver step\n\n\n\n\n\n","category":"function"},{"location":"solver/#VoronoiFVM.history","page":"Solvers","title":"VoronoiFVM.history","text":"history(tsol)\n\nReturn solver history if log was set to true. See see NewtonSolverHistory, TransientSolverHistory.\n\n\n\n\n\nhistory(sys)\n\nReturn solver history from last solve call, if log was set to true. See see NewtonSolverHistory, TransientSolverHistory.\n\n\n\n\n\n","category":"function"},{"location":"solver/#VoronoiFVM.history_details","page":"Solvers","title":"VoronoiFVM.history_details","text":"history_details(tsol)\n\nReturn details of solver history from last solve call, if log was set to true. See details.\n\n\n\n\n\nhistory_details(sys)\n\nReturn details of solver history from last solve call, if log was set to true. See details.\n\n\n\n\n\n","category":"function"},{"location":"solver/#VoronoiFVM.history_summary","page":"Solvers","title":"VoronoiFVM.history_summary","text":"history_summary(tsol)\n\nReturn summary of solver history from last solve call, if log was set to true.\n\n\n\n\n\nhistory_summary(sys)\n\nReturn summary of solver history from last solve call, if log was set to true.\n\n\n\n\n\n","category":"function"},{"location":"solver/#Matrix-extraction","page":"Solvers","title":"Matrix extraction","text":"","category":"section"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"For testing and teaching purposes, one can obtain residual and linearization at a given vector of unknowns","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"evaluate_residual_and_jacobian","category":"page"},{"location":"solver/#VoronoiFVM.evaluate_residual_and_jacobian","page":"Solvers","title":"VoronoiFVM.evaluate_residual_and_jacobian","text":"evaluate_residual_and_jacobian(system,u;\n t=0.0, tstep=Inf,embed=0.0)\n\nEvaluate residual and jacobian at solution value u. Returns a solution vector containing a copy of residual, and an ExendableSparseMatrix containing a copy of the linearization at u.\n\n\n\n\n\n","category":"function"},{"location":"solver/#diffeq","page":"Solvers","title":"OrdinaryDiffEq.jl transient solver","text":"","category":"section"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"For transient problems, as an alternative to the built-in implicit Euler method, (stiff) ODE solvers from OrdinaryDiffEq.jl can be used.","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"The interface just provides two methods: creation of an ODEProblem from a VoronoiFVM.System and a reshape method which turns the output of the ode solver into a TransientSolution.","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"The basic usage pattern is as follows: use OrdinaryDiffEq.jl and replace the call to the built-in solver","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"sol=solve(sys; times=(t0,t1), inival=inival)","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"by","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"problem = ODEProblem(sys,inival,(t0,t1))\nodesol = solve(problem, solver)\nsol=reshape(odesol,sys)","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"Here, solver is some ODE/DAE solver from OrdinaryDiffEq.jl. It is preferable to choose methods able to handle stiff problems. Moreover, often, discretized PDE systems (e.g. containing elliptic equations) are differential agebraic equation (DAE) systems which should be solved by DAE solvers. Some choices to start with are Rosenbrock methods like Rosenbrock23 and multistep methods like QNDF and FBDF.","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"If the DifferentialEquations.jl package is loaded, the solver parameter can be omitted, and some default is chosen.","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"The solution odesol returned by solve conforms to the ArrayInterface but \"forgot\" the VoronoiFVM species structure. Using ","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"Accessing odesol(t) will return an interpolated solution vector giving the value of the solution at moment t. Using reshape(::AbstractVector, ::VoronoiFVM.AbstractSystem) on (odesol(t),system) it can be turned into into a sparse or dense array reflecting the species structure of system. The order of the interpolation depends on the ODE solver.","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"Using reshape(::AbstractDiffEqArray,::VoronoiFVM.AbstractSystem) on (odesol, system) returns a TransientSolution knowing the species structure.","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"SciMLBase.ODEProblem(::VoronoiFVM.AbstractSystem, inival, tspan, callback)\nreshape(::AbstractDiffEqArray,::VoronoiFVM.AbstractSystem)\nSciMLBase.ODEFunction(sys::VoronoiFVM.AbstractSystem; jacval=unknowns(sys,0), tjac=0)","category":"page"},{"location":"solver/#SciMLBase.ODEProblem-Tuple{VoronoiFVM.AbstractSystem, Any, Any, Any}","page":"Solvers","title":"SciMLBase.ODEProblem","text":"ODEProblem(system,inival,tspan,callback=SciMLBase.CallbackSet())\n\nCreate an ODEProblem in mass matrix form which can be handeled by ODE solvers from DifferentialEquations.jl.\n\nParameters:\n\nsystem: A VoronoiFVM.System\ninival: Initial value. Consider to pass a stationary solution at tspan[1].\ntspan: Time interval \ncallback : (optional) callback for ODE solver \n\nThe method returns an ODEProblem which can be solved by solve().\n\n\n\n\n\n","category":"method"},{"location":"solver/#Base.reshape-Tuple{AbstractDiffEqArray, VoronoiFVM.AbstractSystem}","page":"Solvers","title":"Base.reshape","text":"reshape(ode_solution, system, times=nothing)\n\nCreate a TransientSolution from the output of the ode solver which reflects the species structure of the system ignored by the ODE solver. Howvever the interpolation behind reshaped_sol(t) will be linear and ignores the possibility of higher order interpolations with ode_sol.\n\nIf times is specified, the (possibly higher ordee) interpolated solution at the given moments of time will be returned.\n\n\n\n\n\n","category":"method"},{"location":"solver/#SciMLBase.ODEFunction-Tuple{VoronoiFVM.AbstractSystem}","page":"Solvers","title":"SciMLBase.ODEFunction","text":" ODEFunction(system,inival=unknowns(system,inival=0),t0=0)\n\nCreate an ODEPFunction in mass matrix form to be handeled by ODE solvers from DifferentialEquations.jl.\n\nParameters:\n\nsystem: A VoronoiFVM.System\njacval (optional): Initial value. Default is a zero vector. Consider to pass a stationary solution at time tjac.\ntjac (optional): tjac, Default: 0\n\nThe jacval and tjac are passed for a first evaluation of the Jacobian, allowing to detect the sparsity pattern which is passed to the solver.\n\n\n\n\n\n","category":"method"},{"location":"solver/#Legacy-API","page":"Solvers","title":"Legacy API","text":"","category":"section"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"During the development of the code, a number of API variants have been developed which are supported for backward compatibility.","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"VoronoiFVM.solve(inival, system::VoronoiFVM.AbstractSystem, times; kwargs...)\nVoronoiFVM.solve(inival, system::VoronoiFVM.AbstractSystem; kwargs...)\nVoronoiFVM.solve!(solution,inival, system::VoronoiFVM.AbstractSystem; kwargs...)\nNewtonControl\nVoronoiFVM.SolverStrategies","category":"page"},{"location":"solver/#CommonSolve.solve-Tuple{Any, VoronoiFVM.AbstractSystem, Any}","page":"Solvers","title":"CommonSolve.solve","text":" solve(inival, system, times; kwargs...)\n\nAlias for solve(system::VoronoiFVM.AbstractSystem; kwargs...) with the corresponding keyword arguments.\n\n\n\n\n\n","category":"method"},{"location":"solver/#CommonSolve.solve-Tuple{Any, VoronoiFVM.AbstractSystem}","page":"Solvers","title":"CommonSolve.solve","text":" solve(inival, system; control=SolverControl(),params, tstep=Inf)\n\nAlias for solve(system::VoronoiFVM.AbstractSystem; kwargs...) with the corresponding keyword arguments.\n\nSolve stationary problem(if tstep==Inf) or one step implicit Euler step using Newton's method with inival as initial value. Returns a solution array.\n\n\n\n\n\n","category":"method"},{"location":"solver/#CommonSolve.solve!-Tuple{Any, Any, VoronoiFVM.AbstractSystem}","page":"Solvers","title":"CommonSolve.solve!","text":"solve!(solution, inival, system; \n control=SolverControl(), \n tstep=Inf)\n\nMutating version of solve(inival,system)\n\n\n\n\n\n","category":"method"},{"location":"solver/#VoronoiFVM.NewtonControl","page":"Solvers","title":"VoronoiFVM.NewtonControl","text":"NewtonControl\n\nLegacy name of SolverControl\n\n\n\n\n\n","category":"type"},{"location":"solver/#VoronoiFVM.SolverStrategies","page":"Solvers","title":"VoronoiFVM.SolverStrategies","text":"SolverStrategies\n\ncompat: Only available in 1.5\nPlease replace this functionality by the new strategy API in 1.6 as follows:direct_umfpack() = DirectSolver(UMFPACKFactorization()) \ngmres_umfpack() = GMRESIteration(UMFPACKFactorization()) \ngmres_eqnblock_umfpack() = GMRESIteration(UMFPACKFactorization(), EquationBlock()) \ngmres_iluzero() = GMRESIteration(ILUZeroPreconditioner()) \ngmres_eqnblock_iluzero() = GMRESIteration(ILUZeroPreconditioner(), EquationBlock())\ngmres_pointblock_iluzero() = GMRESIteration(ILUZeroPreconditioner(), PointBlock()) \n\n\n\n\n\n","category":"module"},{"location":"plutostatichtml_examples/ode-brusselator/","page":"OrdinaryDiffEq.jl brusselator","title":"OrdinaryDiffEq.jl brusselator","text":"\n\n\n\n
    begin\n    import Pkg as _Pkg\n    haskey(ENV, \"PLUTO_PROJECT\") && _Pkg.activate(ENV[\"PLUTO_PROJECT\"])\n    using Test\n    using Revise\n    using Printf\n    using VoronoiFVM\n    using OrdinaryDiffEq\n    using LinearAlgebra\n    using PlutoUI\n    using ExtendableGrids\n    using DataStructures\n    using GridVisualize,CairoMakie\n    default_plotter!(CairoMakie)\n    CairoMakie.activate!(type=\"svg\")\nend
    \n\n\n","category":"page"},{"location":"plutostatichtml_examples/ode-brusselator/#A-Brusselator-problem","page":"OrdinaryDiffEq.jl brusselator","title":"A Brusselator problem","text":"","category":"section"},{"location":"plutostatichtml_examples/ode-brusselator/","page":"OrdinaryDiffEq.jl brusselator","title":"OrdinaryDiffEq.jl brusselator","text":"
    \n
    \n\n\n

    Two species diffusing and interacting via a reaction

    $$\\begin{aligned}\n \\partial_t u_1 - \\nabla \\cdot (D_1 \\nabla u_1) &+ (B+1)u_1-A-u_1^2u_2 =0\\\\\n \\partial_t u_2 - \\nabla \\cdot (D_2 \\nabla u_2) &+ u_1^2u_2 -B u_1 =0\\\\\n\\end{aligned}$$

    \n\n
    begin \n    const bruss_A=2.25\n    const bruss_B=7.0\n    const bruss_D_1=0.025\n    const bruss_D_2=0.25\n    const pert=0.1\n    const bruss_tend=150\nend;\n
    \n\n\n
    function bruss_storage(f,u,node)\n    f[1]=u[1]\n    f[2]=u[2]\nend;
    \n\n\n
    function bruss_diffusion(f,u,edge)\n    f[1]=bruss_D_1*(u[1,1]-u[1,2])\n    f[2]=bruss_D_2*(u[2,1]-u[2,2])\t\nend;
    \n\n\n
    function bruss_reaction(f,u,node)\n    f[1]= (bruss_B+1.0)*u[1]-bruss_A-u[1]^2*u[2]\n    f[2]= u[1]^2*u[2]-bruss_B*u[1]\nend;
    \n\n\n
    begin\n    \nfunction ODESolver(system,inival,solver)\n    problem = ODEProblem(system,inival,(0,bruss_tend))\n    odesol = solve(problem,\n                                         solver,\n                                         dt=1.0e-5,reltol=1.0e-4)\n    reshape(odesol,system)\nend;\n\n    sys0=VoronoiFVM.System(simplexgrid(0:0.1:1),species=[1,2],flux=bruss_diffusion, storage=bruss_storage, reaction=bruss_reaction);\n    problem0 = ODEProblem(sys0,unknowns(sys0),(0,0.1))\n\n    for method in diffeqmethods\n        solve(problem0,method.second())#precompile\n   end\nend
    \n\n\n\n
    OrderedDict{String, UnionAll} with 4 entries:\n  \"Rosenbrock23 (Rosenbrock)\"    => Rosenbrock23\n  \"QNDF2 (Like matlab's ode15s)\" => QNDF2\n  \"FBDF\"                         => FBDF\n  \"Implicit Euler\"               => ImplicitEuler
    \n\n
    if bruss_dim==1\n        bruss_X=-1:0.01:1\n        bruss_grid=simplexgrid(bruss_X)\n    else\n        bruss_X=-1:0.1:1\n        bruss_grid=simplexgrid(bruss_X,bruss_X)\nend;
    \n\n\n
    bruss_system=VoronoiFVM.System(bruss_grid,species=[1,2],\n            flux=bruss_diffusion, storage=bruss_storage, reaction=bruss_reaction);
    \n\n\n
    begin\n    inival=unknowns(bruss_system,inival=0)\n    coord=bruss_grid[Coordinates]\n    fpeak(x)=exp(-norm(10*x)^2)\n    for i=1:size(inival,2)\n   \t \t\tinival[1,i]=fpeak(coord[:,i])\n   \t \t\tinival[2,i]=0\n            #\n    end\nend
    \n\n\n
    t_run=@elapsed bruss_tsol=ODESolver(bruss_system,inival,diffeqmethods[bruss_method]());
    \n\n\n
    (t_run=t_run,VoronoiFVM.details(bruss_system.history)...)
    \n
    (t_run = 5.009043213, nd = 1926, njac = 855, nf = 2780)
    \n\n\n

    dim:$\\;$ method: $\\;$ t: 150.0

    \n\n\n\n\n\n\n
    \n

    Built with Julia 1.10.2 and

    \nCairoMakie 0.11.6
    \nDataStructures 0.18.16
    \nExtendableGrids 1.2.3
    \nGridVisualize 1.5.0
    \nOrdinaryDiffEq 6.66.0
    \nPkg 1.10.0
    \nPlutoUI 0.7.55
    \nRevise 3.5.13
    \nVoronoiFVM 1.17.1\n
    \n\n","category":"page"},{"location":"plutostatichtml_examples/ode-brusselator/","page":"OrdinaryDiffEq.jl brusselator","title":"OrdinaryDiffEq.jl brusselator","text":"EditURL = \"https://github.com/j-fu/VoronoiFVM.jl/blob/main/nothing\"","category":"page"},{"location":"quantities/#Quantities","page":"Quantities","title":"Quantities","text":"","category":"section"},{"location":"quantities/","page":"Quantities","title":"Quantities","text":"The concept of quantities is implemented on top of the concept of species numbers. They have been introduces in order to be able to handle discontinuities at interfaces.","category":"page"},{"location":"quantities/","page":"Quantities","title":"Quantities","text":"Modules = [VoronoiFVM]\nPages = [\"vfvm_quantities.jl\"]\nOrder = [:type, :constant, :function]","category":"page"},{"location":"quantities/#VoronoiFVM.AbstractQuantity","page":"Quantities","title":"VoronoiFVM.AbstractQuantity","text":"abstract type AbstractQuantity{Ti<:Integer}\n\nAbstract supertype of quantities\n\n\n\n\n\n","category":"type"},{"location":"quantities/#VoronoiFVM.ContinuousQuantity","page":"Quantities","title":"VoronoiFVM.ContinuousQuantity","text":"struct ContinuousQuantity{Ti} <: VoronoiFVM.AbstractQuantity{Ti}\n\nA continuous quantity is represented by exactly one species number\n\nispec::Any: Species number representing the quantity\n\nid::Any: Quantity identifier allowing to use the quantity as index in parameter fields\n\n\n\n\n\n","category":"type"},{"location":"quantities/#VoronoiFVM.ContinuousQuantity-Union{Tuple{Tm}, Tuple{Ti}, Tuple{Tc}, Tuple{Tv}, Tuple{VoronoiFVM.AbstractSystem{Tv, Tc, Ti, Tm}, Any}} where {Tv, Tc, Ti, Tm}","page":"Quantities","title":"VoronoiFVM.ContinuousQuantity","text":" ContinuousQuantity(system,regions; ispec=0, id=0)\n\nAdd continuous quantity to the regions listed in regions.\n\nUnless specified in ispec, the species number is generated automatically.\n\nUnless specified by id, the quantity ID is generated automatically.\n\n\n\n\n\n","category":"method"},{"location":"quantities/#VoronoiFVM.DiscontinuousQuantity","page":"Quantities","title":"VoronoiFVM.DiscontinuousQuantity","text":"struct DiscontinuousQuantity{Ti} <: VoronoiFVM.AbstractQuantity{Ti}\n\nA discontinuous quantity is represented by different species in neighboring regions.\n\nregionspec::Vector: Species numbers representing the quantity in each region\n\nid::Any: Quantity identifier allowing to use the quantity as index in parameter fields\n\n\n\n\n\n","category":"type"},{"location":"quantities/#VoronoiFVM.DiscontinuousQuantity-Union{Tuple{Tm}, Tuple{Ti}, Tuple{Tc}, Tuple{Tv}, Tuple{VoronoiFVM.AbstractSystem{Tv, Tc, Ti, Tm}, AbstractVector}} where {Tv, Tc, Ti, Tm}","page":"Quantities","title":"VoronoiFVM.DiscontinuousQuantity","text":" DiscontinuousQuantity(system,regions; regionspec=nothing, id=0)\n\nAdd discontinuous quantity to the regions listed in regions.\n\nUnless specified in regionspec, the species numbers for each region are generated automatically.\n\nUnless specified by id, the quantity ID is generated automatically.\n\n\n\n\n\n","category":"method"},{"location":"quantities/#VoronoiFVM.InterfaceQuantity","page":"Quantities","title":"VoronoiFVM.InterfaceQuantity","text":"struct InterfaceQuantity{Ti} <: VoronoiFVM.AbstractQuantity{Ti}\n\nAn interface quantity is represented by exactly one species number\n\nispec::Any: Species number representing the quantity\n\nbregspec::Vector: boundary regions, where interface quantity is defined\n\nid::Any: Quantity identifier allowing to use the quantity as index in parameter fields\n\n\n\n\n\n","category":"type"},{"location":"quantities/#VoronoiFVM.InterfaceQuantity-Union{Tuple{Tm}, Tuple{Ti}, Tuple{Tc}, Tuple{Tv}, Tuple{VoronoiFVM.AbstractSystem{Tv, Tc, Ti, Tm}, AbstractVector}} where {Tv, Tc, Ti, Tm}","page":"Quantities","title":"VoronoiFVM.InterfaceQuantity","text":" InterfaceQuantity(system,regions; ispec=0, id=0)\n\nAdd interface quantity to the boundary regions given in breg.\n\nUnless specified in ispec, the species number is generated automatically.\n\nUnless specified by id, the quantity ID is generated automatically.\n\n\n\n\n\n","category":"method"},{"location":"quantities/#Base.getindex-Tuple{AbstractArray, VoronoiFVM.AbstractQuantity}","page":"Quantities","title":"Base.getindex","text":"A[q]\n\nAccess columns of vectors A using id of quantity q. This is meant for vectors indexed by species.\n\n\n\n\n\n","category":"method"},{"location":"quantities/#Base.getindex-Tuple{AbstractMatrix, VoronoiFVM.AbstractQuantity, Any}","page":"Quantities","title":"Base.getindex","text":"M[q,i]\n\nAccess columns M using id of quantity q. This is meant for nspecies x nregions matrices e.g. defining parameters.\n\n\n\n\n\n","category":"method"},{"location":"quantities/#Base.getindex-Tuple{ContinuousQuantity, VoronoiFVM.AbstractNode}","page":"Quantities","title":"Base.getindex","text":"node[quantity]\nedge[quantity]\n\nReturn species number on AbstractNode or AbstractEdge\n\n\n\n\n\n","category":"method"},{"location":"quantities/#Base.getindex-Tuple{DiscontinuousQuantity, VoronoiFVM.BNode, Any}","page":"Quantities","title":"Base.getindex","text":"bnode[quantity,ireg]\n\nReturn species number of discontinuous quantity region ireg adjacent to BNode.\n\n\n\n\n\n","category":"method"},{"location":"quantities/#Base.getindex-Tuple{DiscontinuousQuantity, VoronoiFVM.BNode}","page":"Quantities","title":"Base.getindex","text":"bnode[quantity]\n\nReturn species number of discontinuous quantity region ireg adjacent to BNode for outer boundary nodes.\n\n\n\n\n\n","category":"method"},{"location":"quantities/#Base.getindex-Tuple{VoronoiFVM.AbstractEdgeData, VoronoiFVM.AbstractQuantity, Any}","page":"Quantities","title":"Base.getindex","text":"u[q,j]\n\nReturn value of quantity in unknowns on edge in flux callbacks.\n\n\n\n\n\n","category":"method"},{"location":"quantities/#Base.getindex-Tuple{VoronoiFVM.AbstractNodeData, VoronoiFVM.AbstractQuantity}","page":"Quantities","title":"Base.getindex","text":"u[q]\n\nReturn value of quantity in unknowns on node in node callbacks.\n\n\n\n\n\n","category":"method"},{"location":"quantities/#Base.getindex-Tuple{VoronoiFVM.BNodeUnknowns, DiscontinuousQuantity, Any}","page":"Quantities","title":"Base.getindex","text":"u[q,ireg]\n\nReturn value of discontinuous quantity in unknowns adjacent to unknowns on boundary node.\n\n\n\n\n\n","category":"method"},{"location":"quantities/#Base.setindex!-Tuple{AbstractArray, Any, VoronoiFVM.AbstractQuantity}","page":"Quantities","title":"Base.setindex!","text":"A[q]\n\nSet element of A using id of quantity q This is meant for vectors indexed by species.\n\n\n\n\n\n","category":"method"},{"location":"quantities/#Base.setindex!-Tuple{AbstractMatrix, Any, VoronoiFVM.AbstractQuantity, Any}","page":"Quantities","title":"Base.setindex!","text":"M[q,i]\n\nSet element of M using id of quantity q. This is meant for nspecies x nregions matrices e.g. defining parameters.\n\n\n\n\n\n","category":"method"},{"location":"quantities/#Base.setindex!-Tuple{VoronoiFVM.AbstractNodeData, Any, VoronoiFVM.AbstractQuantity}","page":"Quantities","title":"Base.setindex!","text":"f[q]=value\n\nSet rhs value for quantity in callbacks\n\n\n\n\n\n","category":"method"},{"location":"quantities/#Base.setindex!-Tuple{VoronoiFVM.BNodeRHS, Any, DiscontinuousQuantity, Any}","page":"Quantities","title":"Base.setindex!","text":"f[q,ireg]=v\n\nSet rhs value for discontinuous quantity in adjacent regions of boundary node.\n\n\n\n\n\n","category":"method"},{"location":"quantities/#VoronoiFVM.boundary_dirichlet!-Tuple{VoronoiFVM.AbstractSystem, DiscontinuousQuantity, Any, Any}","page":"Quantities","title":"VoronoiFVM.boundary_dirichlet!","text":"boundary_dirichlet(system, quantity, ibc, value)\n\nSet Dirichlet boundary value for quantity at boundary ibc.\n\n\n\n\n\n","category":"method"},{"location":"quantities/#VoronoiFVM.num_quantities-Tuple{VoronoiFVM.AbstractSystem}","page":"Quantities","title":"VoronoiFVM.num_quantities","text":"num_quantities(system)\n\n\nNumber of quantities defined for system\n\n\n\n\n\n","category":"method"},{"location":"quantities/#VoronoiFVM.subgrids-Tuple{DiscontinuousQuantity, Any}","page":"Quantities","title":"VoronoiFVM.subgrids","text":"subgrids(quantity, system)\n\nReturn a vector of subgrids containing a subgrid for each region where discontinuous quantity is defined.\n\n\n\n\n\n","category":"method"},{"location":"quantities/#VoronoiFVM.subgrids-Tuple{InterfaceQuantity, Any}","page":"Quantities","title":"VoronoiFVM.subgrids","text":"subgrids(quantity, system)\n\nReturn the subgrid where interface quantity is defined.\n\n\n\n\n\n","category":"method"},{"location":"quantities/#VoronoiFVM.views-Tuple{Any, DiscontinuousQuantity, Any, Any}","page":"Quantities","title":"VoronoiFVM.views","text":"views(quantity, subgrids,system)\n\nReturn a vector of solutions containing the solutions with respect tp each region where discontinuous quantity is defined.\n\n\n\n\n\n","category":"method"},{"location":"plutostatichtml_examples/outflow/","page":"Outflow boundary conditions","title":"Outflow boundary conditions","text":"\n\n\n\n
    begin\n    import Pkg as _Pkg\n    haskey(ENV, \"PLUTO_PROJECT\") && _Pkg.activate(ENV[\"PLUTO_PROJECT\"])\n    using Revise\n    using Test\n    using VoronoiFVM\n    using GridVisualize\n    using CairoMakie\n    CairoMakie.activate!(; type = \"svg\", visible = false)\n    GridVisualize.default_plotter!(CairoMakie)\n    using SimplexGridFactory, Triangulate\n    using ExtendableGrids\n    using PlutoUI, HypertextLiteral, UUIDs\nend
    \n\n\n\n

    Outflow boundary conditions

    Source

    \n\n\n

    We show how to implment outflow boundary conditions when the velocities at the boundary are calculated by another equation in the system. A typical case is solute transport in porous media where fluid flow is calculated by Darcy's law which defines the convective velocity in the solute transport equation.

    \n\n\n

    Regard the following system of equations in domain $\\Omega\\subset \\mathbb R^d$:

    $$\\begin{aligned}\n \\nabla \\cdot \\vec v &=0\\\\\n\t\\vec v&=-k\\nabla p\\\\\n \\nabla \\cdot \\vec j &=0\\\\\n \\vec j&= - D\\nabla c - c\\vec v \t\n\\end{aligned}$$

    The variable p can be seen as a the pressure of a fluid in porous medium. c is the concentration of a transported species.

    We subdivide the boundary: $\\partial\\Omega=Γ_{in}\\cup Γ_{out}\\cup Γ_{noflow}$ abs set

    $$\\begin{aligned}\n p=&1 \t\\quad & c&=c_{in} & \\text{on}\\quad Γ_{in}\\\\\n p=&0 \t\\quad & \\vec j\\cdot \\vec n &= c\\vec v \\cdot \\vec n & \\text{on}\\quad Γ_{out}\\\\\n \\vec v\\cdot \\vec n &=0\t\\quad & \\vec j\\cdot \\vec n &= 0 & \\text{on}\\quad Γ_{noflow}\\\\\n\\end{aligned}$$

    \n\n","category":"page"},{"location":"plutostatichtml_examples/outflow/#Discretization-data","page":"Outflow boundary conditions","title":"Discretization data","text":"","category":"section"},{"location":"plutostatichtml_examples/outflow/","page":"Outflow boundary conditions","title":"Outflow boundary conditions","text":"
    \n
    \n\n
    Base.@kwdef struct FlowTransportData\n    k = 1.0\n    v_in = 1.0\n    c_in = 0.5\n    D = 1.0\n    Γ_in = 1\n    Γ_out = 2\n    ip = 1\n    ic = 2\nend
    \n
    FlowTransportData
    \n\n
    X = 0:0.1:1
    \n
    0.0:0.1:1.0
    \n\n
    darcyvelo(u, data) = data.k * (u[data.ip, 1] - u[data.ip, 2])
    \n
    darcyvelo (generic function with 1 method)
    \n\n
    function flux(y, u, edge, data)\n    vh = darcyvelo(u, data)\n    y[data.ip] = vh\n\n    bp, bm = fbernoulli_pm(vh / data.D)\n    y[data.ic] = data.D * (bm * u[data.ic, 1] - bp * u[data.ic, 2])\nend
    \n
    flux (generic function with 1 method)
    \n\n
    function bcondition(y, u, bnode, data)\n    boundary_neumann!(y, u, bnode; species = data.ip, region = data.Γ_in, value = data.v_in)\n    boundary_dirichlet!(y, u, bnode; species = data.ip, region = data.Γ_out, value = 0)\n    boundary_dirichlet!(y, u, bnode; species = data.ic, region = data.Γ_in, value = data.c_in)\nend
    \n
    bcondition (generic function with 1 method)
    \n\n\n

    This function describes the outflow boundary condition. It is called on edges (including interior ones) which have at least one ode on one of the outflow boundaries. Within this function outflownode can be used to identify that node. There is some ambiguity in the case that both nodes are outflow nodes, in that case it is assumed that the contribution is zero. In the present case this is guaranteed by the constant Dirichlet boundary condition for the pressure.

    \n\n
    function boutflow(y, u, edge, data)\n    y[data.ic] = -darcyvelo(u, data) * u[data.ic, outflownode(edge)]\nend
    \n
    boutflow (generic function with 1 method)
    \n\n
    function flowtransportsystem(grid; kwargs...)\n    data = FlowTransportData(; kwargs...)\n    VoronoiFVM.System(grid;\n                      flux,\n                      bcondition,\n                      boutflow,\n                      data,\n                      outflowboundaries = [data.Γ_out],\n                      species = [1, 2],)\nend
    \n
    flowtransportsystem (generic function with 1 method)
    \n\n
    function checkinout(sys, sol)\n    data = sys.physics.data\n    tfact = TestFunctionFactory(sys)\n    tf_in = testfunction(tfact, [data.Γ_out], [data.Γ_in])\n    tf_out = testfunction(tfact, [data.Γ_in], [data.Γ_out])\n    (; in = integrate(sys, tf_in, sol), out = integrate(sys, tf_out, sol))\nend
    \n
    checkinout (generic function with 1 method)
    \n\n","category":"page"},{"location":"plutostatichtml_examples/outflow/#1D-Case","page":"Outflow boundary conditions","title":"1D Case","text":"","category":"section"},{"location":"plutostatichtml_examples/outflow/","page":"Outflow boundary conditions","title":"Outflow boundary conditions","text":"
    \n
    \n\n
    grid = simplexgrid(X)
    \n
    ExtendableGrids.ExtendableGrid{Float64, Int32};\ndim: 1 nodes: 11 cells: 10 bfaces: 2\n\n
    \n\n
    sys1 = flowtransportsystem(grid);
    \n\n\n
    sol1 = solve(sys1; verbose = \"n\");
    \n\n\n\n\n\n
    t1 = checkinout(sys1, sol1)
    \n
    (in = [1.0, 0.5000000000000002], out = [-1.0, -0.5000000000000002])
    \n\n
    @test t1.in ≈ -t1.out
    \n
    Test Passed
    \n\n
    @test maximum(sol1[2, :]) ≈ 0.5
    \n
    Test Passed
    \n\n
    @test minimum(sol1[2, :]) ≈ 0.5
    \n
    Test Passed
    \n\n","category":"page"},{"location":"plutostatichtml_examples/outflow/#2D-Case","page":"Outflow boundary conditions","title":"2D Case","text":"","category":"section"},{"location":"plutostatichtml_examples/outflow/","page":"Outflow boundary conditions","title":"Outflow boundary conditions","text":"
    \n
    \n\n
    begin\n    g2 = simplexgrid(X, X)\n    bfacemask!(g2, [1, 0.3], [1, 0.7], 5)\nend
    \n
    ExtendableGrids.ExtendableGrid{Float64, Int32};\ndim: 2 nodes: 121 cells: 200 bfaces: 40\n\n
    \n\n
    gridplot(g2; size = (300, 300))
    \n\n\n
    sys2 = flowtransportsystem(g2; Γ_in = 4, Γ_out = 5);
    \n\n\n
    sol2 = solve(sys2; verbose = \"n\")
    \n
    2×121 Matrix{Float64}:\n 1.12521  1.02537  0.925877  0.826948  …  0.453027  0.377299  0.321519  0.298904\n 0.5      0.5      0.5       0.5          0.5       0.5       0.5       0.5
    \n\n
    let\n    vis = GridVisualizer(; size = (700, 300), layout = (1, 2))\n    scalarplot!(vis[1, 1], g2, sol2[1, :])\n    scalarplot!(vis[1, 2], g2, sol2[2, :]; limits = (0, 1))\n    reveal(vis)\nend
    \n\n\n
    t2 = checkinout(sys2, sol2)
    \n
    (in = [1.0000000000000013, 0.5000000000000006], out = [-1.0000000000000007, -0.5000000000000001])
    \n\n
    @test t2.in ≈ -t2.out
    \n
    Test Passed
    \n\n
    @test maximum(sol2[2, :]) ≈ 0.5
    \n
    Test Passed
    \n\n
    @test minimum(sol2[2, :]) ≈ 0.5
    \n
    Test Passed
    \n\n","category":"page"},{"location":"plutostatichtml_examples/outflow/#3D-Case","page":"Outflow boundary conditions","title":"3D Case","text":"","category":"section"},{"location":"plutostatichtml_examples/outflow/","page":"Outflow boundary conditions","title":"Outflow boundary conditions","text":"
    \n
    \n\n
    begin\n    g3 = simplexgrid(X, X, X)\n    bfacemask!(g3, [0.3, 0.3, 0], [0.7, 0.7, 0], 7)\nend
    \n
    ExtendableGrids.ExtendableGrid{Float64, Int32};\ndim: 3 nodes: 1331 cells: 6000 bfaces: 1200\n\n
    \n\n
    gridplot(g3; size = (300, 300))
    \n\n\n
    sys3 = flowtransportsystem(g3; Γ_in = 6, Γ_out = 7);
    \n\n\n
    sol3 = solve(sys3; verbose = \"n\")
    \n
    2×1331 Matrix{Float64}:\n 0.547438  0.539229  0.516946  0.488884  …  1.32867  1.32914  1.32951  1.32966\n 0.5       0.5       0.5       0.5          0.5      0.5      0.5      0.5
    \n\n
    let\n    vis = GridVisualizer(; size = (700, 300), layout = (1, 2))\n    scalarplot!(vis[1, 1], g3, sol3[1, :])\n    scalarplot!(vis[1, 2], g3, sol3[2, :]; limits = (0, 1))\n    reveal(vis)\nend
    \n\n\n
    t3 = checkinout(sys3, sol3)
    \n
    (in = [1.000000000000037, 0.5000000000000188], out = [-1.0000000000000389, -0.5000000000000194])
    \n\n
    @test t3.in ≈ -t3.out
    \n
    Test Passed
    \n\n
    @test maximum(sol3[2, :]) ≈ 0.5
    \n
    Test Passed
    \n\n
    @test minimum(sol3[2, :]) ≈ 0.5
    \n
    Test Passed
    \n\n\n
    \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
    \n

    Built with Julia 1.10.2 and

    \nCairoMakie 0.10.12
    \nExtendableGrids 1.2.3
    \nGridVisualize 1.1.7
    \nHypertextLiteral 0.9.5
    \nPkg 1.10.0
    \nPlutoUI 0.7.55
    \nRevise 3.5.13
    \nSimplexGridFactory 0.5.20
    \nTriangulate 2.3.2
    \nVoronoiFVM 1.16.0\n
    \n\n","category":"page"},{"location":"plutostatichtml_examples/outflow/","page":"Outflow boundary conditions","title":"Outflow boundary conditions","text":"EditURL = \"https://github.com/j-fu/VoronoiFVM.jl/blob/main/nothing\"","category":"page"},{"location":"module_examples/Example001_Solvers/#001:-New-linear-solver-API","page":"001: New linear solver API","title":"001: New linear solver API","text":"","category":"section"},{"location":"module_examples/Example001_Solvers/","page":"001: New linear solver API","title":"001: New linear solver API","text":"(source code)","category":"page"},{"location":"module_examples/Example001_Solvers/","page":"001: New linear solver API","title":"001: New linear solver API","text":"module Example001_Solvers\n\n# under development\n\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\nusing LinearSolve\nusing ExtendableSparse\nusing AMGCLWrap\nusing AlgebraicMultigrid\nusing LinearAlgebra\nusing Test\n\nfunction main(; n = 10, Plotter = nothing, assembly = :edgwwise, kwargs...)\n h = 1.0 / convert(Float64, n)\n X = collect(0.0:h:1.0)\n Y = collect(0.0:h:1.0)\n\n grid = VoronoiFVM.Grid(X, Y)\n nn = num_nodes(grid)\n\n eps = 1.0e-2\n\n function reaction(f, u, node)\n f[1] = u[1]^2\n end\n\n function flux(f, u, edge)\n f[1] = eps * (u[1, 1]^2 - u[1, 2]^2)\n end\n\n function source(f, node)\n x1 = node[1] - 0.5\n x2 = node[2] - 0.5\n f[1] = exp(-20.0 * (x1^2 + x2^2))\n end\n\n function storage(f, u, node)\n f[1] = u[1]\n end\n\n function bcondition(f, u, node)\n boundary_dirichlet!(f,\n u,\n node;\n species = 1,\n region = 2,\n value = ramp(node.time; dt = (0, 0.1), du = (0, 1)))\n boundary_dirichlet!(f,\n u,\n node;\n species = 1,\n region = 4,\n value = ramp(node.time; dt = (0, 0.1), du = (0, 1)))\n end\n\n sys = VoronoiFVM.System(grid; reaction, flux, source, storage, bcondition, assembly,\n species = [1])\n @info \"UMFPACK:\"\n umf_sol = solve(sys; inival = 0.5, method_linear = UMFPACKFactorization(), kwargs...)\n\n @info \"KLU:\"\n sol = solve(sys; inival = 0.5, method_linear = KLUFactorization(), kwargs...)\n @test norm(sol - umf_sol, Inf)<1.0e-7\n\n @info \"Sparspak:\"\n sol = solve(sys; inival = 0.5, method_linear = SparspakFactorization(), kwargs...)\n @test norm(sol - umf_sol, Inf)<1.0e-7\n\n @info \"Krylov-ilu0:\"\n sol = solve(sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(),\n precon_linear = ILUZeroPreconditioner(),\n kwargs...)\n @test norm(sol - umf_sol, Inf)<1.0e-7\n\n @info \"Krylov-block1\"\n sol = solve(sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(),\n precon_linear = BlockPreconditioner(; partitioning = [1:(nn ÷ 2), (nn ÷ 2 + 1):nn],\n factorization = ILU0Preconditioner()),\n kwargs...)\n @test norm(sol - umf_sol, Inf)<1.0e-7\n\n @info \"Krylov-block2\"\n sol = solve(sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(),\n precon_linear = BlockPreconditioner(; partitioning = [1:2:nn, 2:2:nn],\n factorization = UMFPACKFactorization()),\n kwargs...)\n @test norm(sol - umf_sol, Inf)<1.0e-7\n\n @info \"Krylov - delayed factorization:\"\n sol = solve(sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(),\n precon_linear = SparspakFactorization(),\n keepcurrent_linear =false,\n kwargs...)\n @test norm(sol - umf_sol, Inf)<1.0e-7\n\n @info \"Krylov - jacobi:\"\n sol = solve(sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(),\n precon_linear = JacobiPreconditioner(),\n keepcurrent_linear = true,\n kwargs...)\n @test norm(sol - umf_sol, Inf)<1.0e-7\n\n @info \"Krylov - SA_AMG:\"\n sol = solve(sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(),\n precon_linear = SA_AMGPreconditioner(),\n keepcurrent_linear = true,\n kwargs...)\n @test norm(sol - umf_sol, Inf)<1.0e-7\n\n @info \"Krylov - AMGCL_AMG:\"\n sol = solve(sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(),\n precon_linear = AMGCL_AMGPreconditioner(),\n keepcurrent_linear = true,\n kwargs...)\n @test norm(sol - umf_sol, Inf)<1.0e-7\n\nend\n\nfunction runtests()\n @testset \"edgewise\" begin\n main(; assembly = :edgewise)\n end\n @testset \"cellwise\" begin\n main(; assembly = :cellwise)\n end\nend\nend","category":"page"},{"location":"module_examples/Example001_Solvers/","page":"001: New linear solver API","title":"001: New linear solver API","text":"","category":"page"},{"location":"module_examples/Example001_Solvers/","page":"001: New linear solver API","title":"001: New linear solver API","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example430_ParameterDerivativesStationary/#430:-Parameter-Derivatives-(stationary)","page":"430: Parameter Derivatives (stationary)","title":"430: Parameter Derivatives (stationary)","text":"","category":"section"},{"location":"module_examples/Example430_ParameterDerivativesStationary/","page":"430: Parameter Derivatives (stationary)","title":"430: Parameter Derivatives (stationary)","text":"(source code)","category":"page"},{"location":"module_examples/Example430_ParameterDerivativesStationary/","page":"430: Parameter Derivatives (stationary)","title":"430: Parameter Derivatives (stationary)","text":"Explore different ways to calculate sensitivities. This is still experimental.","category":"page"},{"location":"module_examples/Example430_ParameterDerivativesStationary/","page":"430: Parameter Derivatives (stationary)","title":"430: Parameter Derivatives (stationary)","text":"module Example430_ParameterDerivativesStationary\n\nusing VoronoiFVM, ExtendableGrids\nusing GridVisualize\nusing ExtendableSparse\nusing ForwardDiff, DiffResults\nusing SparseDiffTools, SparseArrays\nusing ILUZero, LinearSolve\n\n\"\"\"\n f(P)\n\nParameter dependent function which creates system and solves it\n\"\"\"\nfunction f(P; n = 10)\n p = P[1]\n\n valuetype = typeof(p)\n nspecies = 1\n ispec = 1\n\n function flux!(f, u, edge)\n f[1] = (1 + p) * (u[1, 1]^2 - u[1, 2]^2)\n end\n\n function r!(f, u, edge)\n f[1] = p * u[1]^5\n end\n\n function bc!(f, u, node)\n boundary_dirichlet!(f, u, node, ispec, 1, 0.0)\n boundary_dirichlet!(f, u, node, ispec, 3, p)\n end\n\n X = collect(0:(1.0 / n):1)\n grid = VoronoiFVM.Grid(X, X)\n sys = VoronoiFVM.System(grid; valuetype, species = [1], flux = flux!, reaction = r!,\n bcondition = bc!)\n tff = VoronoiFVM.TestFunctionFactory(sys)\n tfc = testfunction(tff, [1], [3])\n sol = solve(sys; inival = 0.5)\n [integrate(sys, tfc, sol)[1]]\nend\n\n\"\"\"\n runf(;Plotter, n=10)\n\nRun parameter series, plot f(p), df(p).\nFor each p,create a new system. Use VoronoiFVM with dual numbers. Pass parameters via closure.\n\"\"\"\nfunction runf(; Plotter = nothing, n = 10)\n P = 0.1:0.05:2\n dresult = DiffResults.JacobianResult(ones(1))\n F = zeros(0)\n DF = zeros(0)\n ff(p) = f(p; n)\n @time for p ∈ P\n ForwardDiff.jacobian!(dresult, ff, [p])\n push!(F, DiffResults.value(dresult)[1])\n push!(DF, DiffResults.jacobian(dresult)[1])\n end\n vis = GridVisualizer(; Plotter, legend = :lt)\n scalarplot!(vis, P, F; color = :red, label = \"f\")\n scalarplot!(vis, P, DF; color = :blue, label = \"df\", clear = false, show = true)\n sum(DF)\nend\n\nfunction fluxg!(f, u, edge, data)\n f[1] = (1 + data.p) * (u[1, 1]^2 - u[1, 2]^2)\nend\n\nfunction rg!(f, u, edge, data)\n f[1] = data.p * u[1]^5\nend\n\nfunction bcg!(f, u, node, data)\n boundary_dirichlet!(f, u, node, 1, 1, 0.0)\n boundary_dirichlet!(f, u, node, 1, 3, data.p)\nend\n\nBase.@kwdef mutable struct MyData{Tv}\n p::Tv = 1.0\nend\n\n\"\"\"\n rung(;Plotter, n=10)\n\nSame as runf, but keep one system pass parameters via data.\n\"\"\"\nfunction rung(; Plotter = nothing, method_linear = SparspakFactorization(), n = 10)\n X = collect(0:(1.0 / n):1)\n grid = VoronoiFVM.Grid(X, X)","category":"page"},{"location":"module_examples/Example430_ParameterDerivativesStationary/","page":"430: Parameter Derivatives (stationary)","title":"430: Parameter Derivatives (stationary)","text":"ugly but simple. By KISS we should first provide this way.","category":"page"},{"location":"module_examples/Example430_ParameterDerivativesStationary/","page":"430: Parameter Derivatives (stationary)","title":"430: Parameter Derivatives (stationary)","text":" sys = nothing\n data = nothing\n tfc = nothing\n\n function g(P)\n Tv = eltype(P)\n if isnothing(sys)\n data = MyData(one(Tv))\n sys = VoronoiFVM.System(grid; valuetype = Tv, species = [1], flux = fluxg!,\n reaction = rg!, bcondition = bcg!, data,\n unknown_storage = :dense)\n tff = VoronoiFVM.TestFunctionFactory(sys)\n tfc = testfunction(tff, [1], [3])\n end\n data.p = P[1]\n sol = solve(sys; inival = 0.5, method_linear, precon_linear = ILUZeroPreconditioner())\n [integrate(sys, tfc, sol)[1]]\n end\n\n dresult = DiffResults.JacobianResult(ones(1))\n\n P = 0.1:0.05:2\n G = zeros(0)\n DG = zeros(0)\n @time for p ∈ P\n ForwardDiff.jacobian!(dresult, g, [p])\n push!(G, DiffResults.value(dresult)[1])\n push!(DG, DiffResults.jacobian(dresult)[1])\n end\n\n vis = GridVisualizer(; Plotter, legend = :lt)\n scalarplot!(vis, P, G; color = :red, label = \"g\")\n scalarplot!(vis, P, DG; color = :blue, label = \"dg\", clear = false, show = true)\n sum(DG)\nend\n\n#########################################################################\n\nfunction fluxh!(f, u, edge)\n p = parameters(u)[1]\n f[1] = (1 + p) * (u[1, 1]^2 - u[1, 2]^2)\nend\n\nfunction rh!(f, u, edge)\n p = parameters(u)[1]\n f[1] = p * u[1]^5\nend\n\nfunction bch!(f, u, node)\n p = parameters(u)[1]\n boundary_dirichlet!(f, u, node, 1, 1, 0.0)\n boundary_dirichlet!(f, u, node, 1, 3, p)\nend\n\n\"\"\"\n runh(;Plotter, n=10)\n\nSame as runf, but use \"normal\" calculation (don't solve in dual numbers), and calculate dudp during\nmain assembly loop.\n\nThis needs quite a bit of additional implementation + corresponding API and still lacks local assembly of the\nmeasurement derivative (when using testfunction based calculation) when calculating current.\n\"\"\"\nfunction runh(; Plotter = nothing, n = 10)\n X = collect(0:(1.0 / n):1)\n grid = VoronoiFVM.Grid(X, X)\n\n sys = VoronoiFVM.System(grid; species = [1], flux = fluxh!, reaction = rh!,\n bcondition = bch!, unknown_storage = :dense, nparams = 1)\n tff = VoronoiFVM.TestFunctionFactory(sys)\n tfc = testfunction(tff, [1], [3])\n\n function measp(params, u)\n Tp = eltype(params)\n up = Tp.(u)\n integrate(sys, tfc, up; params = params)[1]\n end\n\n params = [0.0]\n\n function mymeas!(meas, U)\n u = reshape(U, sys)\n meas[1] = integrate(sys, tfc, u; params)[1]\n nothing\n end\n\n dp = 0.05\n P = 0.1:dp:2\n U0 = solve(sys; inival = 0.5, params = [P[1]])\n\n ndof = num_dof(sys)\n colptr = [i for i = 1:(ndof + 1)]\n rowval = [1 for i = 1:ndof]\n nzval = [1.0 for in = 1:ndof]\n ∂m∂u = zeros(1, ndof)\n colors = matrix_colors(∂m∂u)\n\n H = zeros(0)\n DH = zeros(0)\n DHx = zeros(0)\n m = zeros(1)\n\n @time for p ∈ P\n params[1] = p\n sol = solve(sys; inival = 0.5, params)\n mymeas!(m, sol)\n push!(H, m[1])","category":"page"},{"location":"module_examples/Example430_ParameterDerivativesStationary/","page":"430: Parameter Derivatives (stationary)","title":"430: Parameter Derivatives (stationary)","text":"this one is expensive - we would need to assemble this jacobian via local calls","category":"page"},{"location":"module_examples/Example430_ParameterDerivativesStationary/","page":"430: Parameter Derivatives (stationary)","title":"430: Parameter Derivatives (stationary)","text":" forwarddiff_color_jacobian!(∂m∂u, mymeas!, vec(sol); colorvec = colors)","category":"page"},{"location":"module_examples/Example430_ParameterDerivativesStationary/","page":"430: Parameter Derivatives (stationary)","title":"430: Parameter Derivatives (stationary)","text":"need to have the full derivative of m vs p","category":"page"},{"location":"module_examples/Example430_ParameterDerivativesStationary/","page":"430: Parameter Derivatives (stationary)","title":"430: Parameter Derivatives (stationary)","text":" ∂m∂p = ForwardDiff.gradient(p -> measp(p, sol), params)\n\n dudp = sys.matrix \\ vec(sys.dudp[1])\n dmdp = -∂m∂u * dudp + ∂m∂p\n push!(DH, dmdp[1])\n end\n\n vis = GridVisualizer(; Plotter, legend = :lt)\n scalarplot!(vis, P, H; color = :red, label = \"h\")\n scalarplot!(vis, P, DH; color = :blue, label = \"dh\", clear = false, show = true)\n sum(DH)\nend\n\nusing Test\nfunction runtests()\n testval = 489.3432830184927\n @test runf() ≈ testval\n @test rung() ≈ testval\n @test runh() ≈ testval\nend\n\nend","category":"page"},{"location":"module_examples/Example430_ParameterDerivativesStationary/","page":"430: Parameter Derivatives (stationary)","title":"430: Parameter Derivatives (stationary)","text":"","category":"page"},{"location":"module_examples/Example430_ParameterDerivativesStationary/","page":"430: Parameter Derivatives (stationary)","title":"430: Parameter Derivatives (stationary)","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example424_AbstractQuantitiesInit/#424:-Initialization-of-Abstract-quantities","page":"424: Initialization of Abstract quantities","title":"424: Initialization of Abstract quantities","text":"","category":"section"},{"location":"module_examples/Example424_AbstractQuantitiesInit/","page":"424: Initialization of Abstract quantities","title":"424: Initialization of Abstract quantities","text":"(source code)","category":"page"},{"location":"module_examples/Example424_AbstractQuantitiesInit/","page":"424: Initialization of Abstract quantities","title":"424: Initialization of Abstract quantities","text":"module Example424_AbstractQuantitiesInit\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\nusing LinearAlgebra\n\nfunction main(; N = 5, Plotter = nothing, unknown_storage = :sparse, assembly = :edgewise)\n if 2 * (N ÷ 2) == N\n N = N + 1\n end\n\n xcoord = range(0, 2; length = N) |> collect\n grid = simplexgrid(xcoord)\n cellmask!(grid, [1], [2], 2)\n system = VoronoiFVM.System(grid; unknown_storage = unknown_storage, assembly = assembly)\n\n # First, we introduce a continuous quantity which we name \"cspec\". Note that the \"species number\" can be assigned automatically if not given explicitly.\n cspec = ContinuousQuantity(system, 1:2)\n\n # A discontinuous quantity can be introduced as well. by default, each reagion gets a new species number. This can be overwritten by the user.\n dspec = DiscontinuousQuantity(system, [1, 2])\n\n allsubgrids = VoronoiFVM.subgrids(dspec, system)\n\n function init(u, node)\n ireg = node.region\n if ireg == 1\n u[dspec] = 1\n u[cspec] = 10\n else\n u[dspec] = 2\n u[cspec] = 20\n end\n end\n\n function check(u)\n duviews = views(u, dspec, allsubgrids, system)\n cuviews = views(u, cspec, allsubgrids, system)\n result = Bool[]\n psh!(b) = push!(result, b)\n\n (duviews[1] == fill(1.0, (N - 1) ÷ 2 + 1)) |> psh!\n (duviews[2] == fill(2.0, (N - 1) ÷ 2 + 1)) |> psh!\n (cuviews[2] == fill(20.0, (N - 1) ÷ 2 + 1)) |> psh!\n (cuviews[1][1:(end - 1)] == fill(10.0, (N - 1) ÷ 2)) |> psh!\n\n all(result)\n end\n\n # \"Classical\" solution creation\n u = unknowns(system; inifunc = init)\n\n # We can use Base.map to create an initial value\n v = map(init, system)\n\n # We also can map an init function onto the system\n w = unknowns(system)\n map!(init, w, system)\n\n check(u) && check(v) && check(w)\nend\n\nusing Test\nfunction runtests()\n @test main(; unknown_storage = :sparse, assembly = :edgewise) &&\n main(; unknown_storage = :dense, assembly = :edgewise) &&\n main(; unknown_storage = :sparse, assembly = :cellwise) &&\n main(; unknown_storage = :dense, assembly = :cellwise)\nend\n\nend","category":"page"},{"location":"module_examples/Example424_AbstractQuantitiesInit/","page":"424: Initialization of Abstract quantities","title":"424: Initialization of Abstract quantities","text":"","category":"page"},{"location":"module_examples/Example424_AbstractQuantitiesInit/","page":"424: Initialization of Abstract quantities","title":"424: Initialization of Abstract quantities","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example121_PoissonPointCharge1D/#121:-1D-Poisson-with-point-charge","page":"121: 1D Poisson with point charge","title":"121: 1D Poisson with point charge","text":"","category":"section"},{"location":"module_examples/Example121_PoissonPointCharge1D/","page":"121: 1D Poisson with point charge","title":"121: 1D Poisson with point charge","text":"(source code)","category":"page"},{"location":"module_examples/Example121_PoissonPointCharge1D/","page":"121: 1D Poisson with point charge","title":"121: 1D Poisson with point charge","text":"Solve a Poisson equation","category":"page"},{"location":"module_examples/Example121_PoissonPointCharge1D/","page":"121: 1D Poisson with point charge","title":"121: 1D Poisson with point charge","text":"- Delta u = 0","category":"page"},{"location":"module_examples/Example121_PoissonPointCharge1D/","page":"121: 1D Poisson with point charge","title":"121: 1D Poisson with point charge","text":"in Omega=(-11) with a point charge Q at x=0.","category":"page"},{"location":"module_examples/Example121_PoissonPointCharge1D/","page":"121: 1D Poisson with point charge","title":"121: 1D Poisson with point charge","text":"module Example121_PoissonPointCharge1D\n\nusing Printf\n\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\n\nfunction main(; nref = 0, Plotter = nothing, verbose = false, unknown_storage = :sparse,\n brea = false, assembly = :edgewise)\n\n # Create grid in (-1,1) refined around 0\n hmax = 0.2 / 2.0^nref\n hmin = 0.05 / 2.0^nref\n X1 = geomspace(-1.0, 0.0, hmax, hmin)\n X2 = geomspace(0.0, 1.0, hmin, hmax)\n X = glue(X1, X2)\n grid = VoronoiFVM.Grid(X)\n\n # Edit default region numbers:\n # additional boundary region 3 at 0.0\n bfacemask!(grid, [0.0], [0.0], 3)\n # Material 1 left of 0\n cellmask!(grid, [-1.0], [0.0], 1)\n # Material 2 right of 0\n cellmask!(grid, [0.0], [1.0], 2)\n\n Q::Float64 = 0.0\n\n function flux!(f, u, edge)\n f[1] = u[1, 1] - u[1, 2]\n end\n function storage!(f, u, node)\n f[1] = u[1]\n end\n\n # Define boundary reaction defining charge\n # Note that the term is written on the left hand side, therefore the - sign\n function breaction!(f, u, node)\n if node.region == 3\n f[1] = -Q\n end\n end\n\n # Create physics\n physics = VoronoiFVM.Physics(; flux = flux!,\n storage = storage!,\n breaction = breaction!)\n\n # Create system\n sys = VoronoiFVM.System(grid, physics; unknown_storage = :dense, assembly = assembly)\n\n # put potential into both regions\n enable_species!(sys, 1, [1, 2])\n\n # Set boundary conditions\n\n boundary_dirichlet!(sys, 1, 1, 1.0)\n boundary_dirichlet!(sys, 1, 2, 0.0)\n\n # Create a solution array\n U = unknowns(sys)\n U .= 0\n\n # Create solver control info\n control = VoronoiFVM.NewtonControl()\n control.verbose = verbose\n\n vis = GridVisualizer(; Plotter = Plotter)\n # Solve and plot for several values of charge\n for q in [0.1, 0.2, 0.4, 0.8, 1.6]\n if brea\n # Charge in reaction term\n Q = q\n else\n # Charge as boundary condition\n sys.boundary_values[1, 3] = q\n end\n U = solve(sys; inival = U, control)\n\n # Plot data\n\n scalarplot!(vis, grid, U[1, :]; title = @sprintf(\"Q=%.2f\", q), clear = true,\n show = true)\n end\n return sum(U)\nend\n\nusing Test\nfunction runtests()\n testval = 20.254591679579015\n @test main(; assembly = :edgewise) ≈ testval &&\n main(; assembly = :cellwise) ≈ testval\nend\nend","category":"page"},{"location":"module_examples/Example121_PoissonPointCharge1D/","page":"121: 1D Poisson with point charge","title":"121: 1D Poisson with point charge","text":"","category":"page"},{"location":"module_examples/Example121_PoissonPointCharge1D/","page":"121: 1D Poisson with point charge","title":"121: 1D Poisson with point charge","text":"This page was generated using Literate.jl.","category":"page"},{"location":"module_examples/Example160_UnipolarDriftDiffusion1D/#160:-Unipolar-degenerate-drift-diffusion","page":"160: Unipolar degenerate drift-diffusion","title":"160: Unipolar degenerate drift-diffusion","text":"","category":"section"},{"location":"module_examples/Example160_UnipolarDriftDiffusion1D/","page":"160: Unipolar degenerate drift-diffusion","title":"160: Unipolar degenerate drift-diffusion","text":"(source code)","category":"page"},{"location":"module_examples/Example160_UnipolarDriftDiffusion1D/","page":"160: Unipolar degenerate drift-diffusion","title":"160: Unipolar degenerate drift-diffusion","text":"See: C. Cancès, C. Chainais-Hillairet, J. Fuhrmann, and B. Gaudeul, \"A numerical-analysis-focused comparison of several finite volume schemes for a unipolar degenerate drift-diffusion model\" IMA Journal of Numerical Analysis, vol. 41, no. 1, pp. 271–314, 2021.","category":"page"},{"location":"module_examples/Example160_UnipolarDriftDiffusion1D/","page":"160: Unipolar degenerate drift-diffusion","title":"160: Unipolar degenerate drift-diffusion","text":"Available from https://doi.org/10.1093/imanum/draa002, the preprint is on arxiv1907.11126.","category":"page"},{"location":"module_examples/Example160_UnipolarDriftDiffusion1D/","page":"160: Unipolar degenerate drift-diffusion","title":"160: Unipolar degenerate drift-diffusion","text":"The problem consists of a Poisson equation for the electrostatic potential phi:","category":"page"},{"location":"module_examples/Example160_UnipolarDriftDiffusion1D/","page":"160: Unipolar degenerate drift-diffusion","title":"160: Unipolar degenerate drift-diffusion","text":"-nabla varepsilon nabla phi = z(2c-1)","category":"page"},{"location":"module_examples/Example160_UnipolarDriftDiffusion1D/","page":"160: Unipolar degenerate drift-diffusion","title":"160: Unipolar degenerate drift-diffusion","text":"and a degenerate drift-diffusion equation of the transport of a charged species c:","category":"page"},{"location":"module_examples/Example160_UnipolarDriftDiffusion1D/","page":"160: Unipolar degenerate drift-diffusion","title":"160: Unipolar degenerate drift-diffusion","text":"partial_t u - nablacdot left(nabla c + c nabla (phi - log (1-c) )right)","category":"page"},{"location":"module_examples/Example160_UnipolarDriftDiffusion1D/","page":"160: Unipolar degenerate drift-diffusion","title":"160: Unipolar degenerate drift-diffusion","text":"In particular, the paper, among others, investigates the \"sedan\" flux discretization which is able to handle the degeneracy coming from the log (1-c) term. The earliest reference to this scheme we found in the source code of the SEDAN III semiconductor device simulator.","category":"page"},{"location":"module_examples/Example160_UnipolarDriftDiffusion1D/","page":"160: Unipolar degenerate drift-diffusion","title":"160: Unipolar degenerate drift-diffusion","text":"module Example160_UnipolarDriftDiffusion1D\n\nusing Printf\n\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\nusing LinearSolve\n\nmutable struct Data\n eps::Float64\n z::Float64\n ic::Int32\n iphi::Int32\n V::Float64\n Data() = new()\nend\n\nfunction classflux!(f, u, edge, data)\n ic = data.ic\n iphi = data.iphi\n f[iphi] = data.eps * (u[iphi, 1] - u[iphi, 2])\n bp, bm = fbernoulli_pm(u[iphi, 1] - u[iphi, 2])\n f[ic] = bm * u[ic, 1] - bp * u[ic, 2]\nend\n\nfunction storage!(f, u, node, data)\n ic = data.ic\n iphi = data.iphi\n f[iphi] = 0\n f[ic] = u[ic]\nend\n\nfunction reaction!(f, u, node, data)\n ic = data.ic\n iphi = data.iphi\n f[iphi] = data.z * (1 - 2 * u[ic])\n f[ic] = 0\nend\nconst eps_reg=1.0e-10\nfunction sedanflux!(f, u, edge, data)\n ic = data.ic\n iphi = data.iphi\n f[iphi] = data.eps * (u[iphi, 1] - u[iphi, 2])\n mu1 = -log1p(-u[ic, 1]+eps_reg)\n mu2 = -log1p(-u[ic, 2]+eps_reg)\n bp, bm = fbernoulli_pm(data.z * 2 * (u[iphi, 1] - u[iphi, 2]) + (mu1 - mu2))\n f[ic] = bm * u[ic, 1] - bp * u[ic, 2]\nend\n\nfunction bcondition!(f, u, bnode, data)\n V = ramp(bnode.time; dt = (0, 1.0e-2), du = (0, data.V))\n boundary_dirichlet!(f, u, bnode; species = data.iphi, region = 1, value = V)\n boundary_dirichlet!(f, u, bnode; species = data.iphi, region = 2, value = 0)\n boundary_dirichlet!(f, u, bnode; species = data.ic, region = 2, value = 0.5)\nend\n\nfunction main(;\n n = 20,\n Plotter = nothing,\n dlcap = false,\n verbose = false,\n phimax = 1,\n dphi = 1.0e-1,\n unknown_storage = :sparse,\n assembly = :edgewise,)\n h = 1.0 / convert(Float64, n)\n grid = VoronoiFVM.Grid(collect(0:h:1))\n\n data = Data()\n data.eps = 1.0e-3\n data.z = -1\n data.iphi = 1\n data.ic = 2\n data.V = 5\n ic = data.ic\n iphi = data.iphi\n\n physics = VoronoiFVM.Physics(;\n data = data,\n flux = sedanflux!,\n reaction = reaction!,\n breaction = bcondition!,\n storage = storage!,)\n\n sys = VoronoiFVM.System(grid,\n physics;\n unknown_storage = unknown_storage,\n species = [1, 2],\n assembly = assembly,)\n\n inival = unknowns(sys)\n @views inival[iphi, :] .= 0\n @views inival[ic, :] .= 0.5\n\n if !dlcap\n # Create solver control info for constant time step size\n tstep = 1.0e-5\n control = VoronoiFVM.NewtonControl()\n control.verbose = false\n control.Δt_min = tstep\n control.Δt = tstep\n control.Δt_grow = 1.1\n control.Δt_max = 0.1\n control.Δu_opt = 0.1\n control.damp_initial = 0.5\n\n tsol = solve(sys;\n method_linear = UMFPACKFactorization(),\n inival,\n times = [0.0, 10],\n control = control,)\n\n vis = GridVisualizer(; Plotter = Plotter, layout = (1, 1), fast = true)\n for log10t = -4:0.025:0\n time = 10^(log10t)\n sol = tsol(time)\n scalarplot!(vis[1, 1],\n grid,\n sol[iphi, :];\n label = \"ϕ\",\n title = @sprintf(\"time=%.3g\", time),\n flimits = (0, 5),\n color = :green,)\n scalarplot!(vis[1, 1],\n grid,\n sol[ic, :];\n label = \"c\",\n flimits = (0, 5),\n clear = false,\n color = :red,)\n reveal(vis)\n end\n return sum(tsol[end])\n\n else # Calculate double layer capacitance\n U = unknowns(sys)\n control = VoronoiFVM.NewtonControl()\n control.damp_initial = 1.0e-5\n delta = 1.0e-4\n @views inival[iphi, :] .= 0\n @views inival[ic, :] .= 0.5\n sys.boundary_values[iphi, 1] = 0\n\n delta = 1.0e-4\n vplus = zeros(0)\n cdlplus = zeros(0)\n vminus = zeros(0)\n cdlminus = zeros(0)\n cdl = 0.0\n vis = GridVisualizer(; Plotter = Plotter, layout = (2, 1), fast = true)\n for dir in [1, -1]\n phi = 0.0\n while phi < phimax\n data.V = dir * phi\n U = solve(sys; inival = U, control, time = 1.0)\n Q = integrate(sys, physics.reaction, U)\n data.V = dir * phi + delta\n U = solve(sys; inival = U, control, time = 1.0)\n Qdelta = integrate(sys, physics.reaction, U)\n cdl = (Qdelta[iphi] - Q[iphi]) / delta\n\n if Plotter != nothing\n scalarplot!(vis[1, 1],\n grid,\n U[iphi, :];\n label = \"ϕ\",\n title = @sprintf(\"Δϕ=%.3g\", phi),\n flimits = (-5, 5),\n clear = true,\n color = :green,)\n scalarplot!(vis[1, 1],\n grid,\n U[ic, :];\n label = \"c\",\n flimits = (0, 5),\n clear = false,\n color = :red,)\n end\n if dir == 1\n push!(vplus, dir * phi)\n push!(cdlplus, cdl)\n else\n push!(vminus, dir * phi)\n push!(cdlminus, cdl)\n end\n\n if Plotter != nothing\n scalarplot!(vis[2, 1], [0, 1.0e-1], [0, 0.05]; color = :white, clear = true)\n end\n v = vcat(reverse(vminus), vplus)\n c = vcat(reverse(cdlminus), cdlplus)\n if length(v) >= 2\n scalarplot!(vis[2, 1],\n v,\n c;\n color = :green,\n clear = false,\n title = \"C_dl\",)\n end\n\n phi += dphi\n reveal(vis)\n end\n end\n\n return cdl\n end\nend\n\nusing Test\nfunction runtests()\n\n\n evolval = 18.721369939565655\n dlcapval = 0.025657355479449806\n rtol = 1.0e-5\n @test isapprox(main(; unknown_storage = :sparse, dlcap = false, assembly = :edgewise),\n evolval;\n rtol = rtol,)\n @test isapprox(main(; unknown_storage = :sparse, dlcap = true, assembly = :edgewise),\n dlcapval;\n rtol = rtol,)\n @test isapprox(main(; unknown_storage = :dense, dlcap = false, assembly = :edgewise),\n evolval;\n rtol = rtol,)\n @test isapprox(main(; unknown_storage = :dense, dlcap = true, assembly = :edgewise),\n dlcapval;\n rtol = rtol,)\n @test isapprox(main(; unknown_storage = :sparse, dlcap = false, assembly = :cellwise),\n evolval;\n rtol = rtol,)\n @test isapprox(main(; unknown_storage = :sparse, dlcap = true, assembly = :cellwise),\n dlcapval;\n rtol = rtol,)\n @test isapprox(main(; unknown_storage = :dense, dlcap = false, assembly = :cellwise),\n evolval;\n rtol = rtol,)\n @test isapprox(main(; unknown_storage = :dense, dlcap = true, assembly = :cellwise),\n dlcapval;\n rtol = rtol,)\nend\nend","category":"page"},{"location":"module_examples/Example160_UnipolarDriftDiffusion1D/","page":"160: Unipolar degenerate drift-diffusion","title":"160: Unipolar degenerate drift-diffusion","text":"","category":"page"},{"location":"module_examples/Example160_UnipolarDriftDiffusion1D/","page":"160: Unipolar degenerate drift-diffusion","title":"160: Unipolar degenerate drift-diffusion","text":"This page was generated using Literate.jl.","category":"page"}] +} diff --git a/v1.19.1/siteinfo.js b/v1.19.1/siteinfo.js new file mode 100644 index 000000000..f3b3c6a57 --- /dev/null +++ b/v1.19.1/siteinfo.js @@ -0,0 +1 @@ +var DOCUMENTER_CURRENT_VERSION = "v1.19.1"; diff --git a/v1.19.1/solutions/index.html b/v1.19.1/solutions/index.html new file mode 100644 index 000000000..b78b07145 --- /dev/null +++ b/v1.19.1/solutions/index.html @@ -0,0 +1,20 @@ + +Solution objects · VoronoiFVM.jl

    Solution objects

    Dense solution arrays

    VoronoiFVM._addMethod
    _add(U::Array{Tv, 2}, idof, val) -> Any
    +

    Add residual value into global degree of freedom

    source

    Sparse solution arrays

    VoronoiFVM.SparseSolutionArrayType
    struct SparseSolutionArray{Tv, Ti} <: AbstractArray{Tv, 2}

    Struct holding solution information for SparseSystem. Solution is stored in a sparse matrix structure.

    This class plays well with the abstract array interface.

    Fields:

    • node_dof::SparseArrays.SparseMatrixCSC{Tv, Ti} where {Tv, Ti}: Sparse matrix holding actual data.
    source
    Base.copyMethod
    copy(this)
    +

    Create a copy of sparse solution array

    source
    Base.similarMethod
    similar(this)
    +

    Create a similar uninitialized sparse solution array

    source
    VoronoiFVM._addMethod
    _add(U::VoronoiFVM.SparseSolutionArray, idof, val) -> Any
    +

    Add residual value into global degree of freedom

    source
    VoronoiFVM.dofMethod
    dof(a, i, j)
    +

    Get number of degree of freedom. Return 0 if species is not defined in node.

    source

    Transient solution

    VoronoiFVM.TransientSolutionType
    mutable struct TransientSolution{T, N, A, B} <: VoronoiFVM.AbstractTransientSolution{T, N, A, B}

    Transient solution structure

    Fields

    • u::Any: Vector of solutions
    • t::Any: Vector of times
    • history::TransientSolverHistory: History

    Interface

    Object of this type adhere to the AbstractDiffEqArray interface. For indexing and interpolation, see https://diffeq.sciml.ai/stable/basics/solution/.

    In particular, a TransientSolution sol can be accessed as follows:

    • sol[i] contains the solution for timestep i
    • sol[ispec,:,i] contains the solution for component ispec at timestep i
    • sol(t) returns a (linearly) interpolated solution value for t.
    • sol.t[i] is the corresponding time for timestep i
    • sol[ispec,ix,i] refers to solution of component ispec at node ix at moment i
    source
    VoronoiFVM.TransientSolutionMethod
    TransientSolution(t0,inival;
    +                  in_memory=true,
    +                  keep_open=true,
    +                  fname=tempname(pwd())*".jld2"

    Constructor of transient solution with initial value and initial time.

    • in_memory: if true (default), data are kept in main memory, otherwise on disk (via JLD2)
    • keep_open: if true, disk file is not closed during the existence of the object
    • fname: file name for the disk file
    source
    VoronoiFVM.VectorOfDiskArraysMethod
    VectorOfDiskArrays(firstobj:AbstractArray;
    +                   keep_open=true,
    +                   fname= fname=tempname(pwd())*".jld2")

    Constructor of vector of arrays stored on disk (via JLD2).

    • keep_open: if true, disk file is not closed during the existence of the object
    • fname: file name for the disk file

    The disk file is automatically removed if the object is garbage collected.

    source
    diff --git a/v1.19.1/solver/index.html b/v1.19.1/solver/index.html new file mode 100644 index 000000000..d442ce4ac --- /dev/null +++ b/v1.19.1/solver/index.html @@ -0,0 +1,18 @@ + +Solvers · VoronoiFVM.jl

    Solvers

    The package comes with a built-in solve method which solves stationary problems, homotopy embedding problems and transient problems via the implicit Euler method. In particular, the transient solver allows to use nonlinear storage terms.

    Alternatively, OrdinaryDiffEq.jl based solvers can be used for transient problems.

    Built-in solver

    This solver and its default parameters are tuned for robustness, possibly at the expense of solution speed. Careful tuning of the parameters, or – in the case of transient problems – the choice of a OrdinaryDiffEq.jl based solver can significantly improve the performance.

    Overview:

    Solve method

    CommonSolve.solveMethod
    solve(system; kwargs...)

    Built-in solution method for VoronoiFVM.System.

    Keyword arguments:

    • General for all solvers

      • inival (default: 0) : Array created via unknowns or number giving the initial value.
      • control (default: nothing): Pass instance of SolverControl
      • All elements of SolverControl can be used as kwargs. Eventually overwrites values given via control
      • params: Parameters (Parameter handling is experimental and may change)
    • Stationary solver: Invoked if neither times nor embed, nor tstep are given as keyword argument.

      • time (default: 0.0): Set time value.

      Returns a DenseSolutionArray or SparseSolutionArray

    • Embedding (homotopy) solver: Invoked if embed kwarg is given. Use homotopy embedding + damped Newton's method to solve stationary problem or to solve series of parameter dependent problems. Parameter step control is performed according to solver control data. kwargs and default values are:

      • embed (default: nothing ): vector of parameter values to be reached exactly

      In addition, all kwargs of the implicit Euler solver (besides times) are handled. Returns a transient solution object sol containing the stored solution(s), see TransientSolution.

    • Implicit Euler transient solver: Invoked if times kwarg is given. Use implicit Euler method + damped Newton's method to solve time dependent problem. Time step control is performed according to solver control data. kwargs and default values are:

      • times (default: nothing ): vector of time values to be reached exactly
      • pre (default: (sol,t)->nothing ): callback invoked before each time step
      • post (default: (sol,oldsol, t, Δt)->nothing ): callback invoked after each time step
      • sample (default: (sol,t)->nothing ): callback invoked after timestep for all times in times[2:end].
      • delta (default: (system, u,v,t, Δt)->norm(sys,u-v,Inf) ): Value used to control the time step size Δu

      If control.handle_error is true, if time step solution throws an error, stepsize is lowered, and step solution is called again with a smaller time value. If control.Δt<control.Δt_min, solution is aborted with error. Returns a transient solution object sol containing the stored solution, see TransientSolution.

    • Implicit Euler timestep solver. Invoked if tstep kwarg is given. Solve one time step of the implicit Euler method.

      • time (default: 0): Set time value.
      • tstep: time step

      Returns a DenseSolutionArray or SparseSolutionArray

    source

    Solver control

    VoronoiFVM.SolverControlType
    SolverControl
    +SolverControl(;kwargs...)
    +SolverControl(linear_solver_strategy, sys; kwargs...)

    Solver control parameter for time stepping, embedding, Newton method and linear solver control. All field names can be used as keyword arguments for solve(system::VoronoiFVM.AbstractSystem; kwargs...)

    Newton's method solves $F(u)=0$ by the iterative procedure $u_{i+1}=u_{i} - d_i F'(u_i)^{-1}F(u_i)$ starting with some initial value $u_0$, where $d_i$ is a damping parameter.

    For linear solver strategies, see VoronoiFVM.LinearSolverStrategy.

    • verbose::Union{Bool, String}: Verbosity control. A collection of output categories is given in a string composed of the following letters:
      • a: allocation warnings
      • d: deprecation warnings
      • e: time/parameter evolution log
      • n: newton solver log
      • l: linear solver log
      Alternatively, a Bool value can be given, resulting in
      • true: "neda"
      • false: "da"
      Switch off all output including deprecation warnings via verbose="". In the output, corresponding messages are marked e.g. via '[n]', [a] etc. (besides of '[l]')
    • abstol::Float64: Tolerance (in terms of norm of Newton update): terminate if $\Delta u_i=||u_{i+1}-u_i||_\infty <$ abstol.
    • reltol::Float64: Tolerance (relative to the size of the first update): terminate if $\Delta u_i/\Delta u_1<$ reltol.
    • maxiters::Int64: Maximum number of newton iterations.
    • tol_round::Float64: Tolerance for roundoff error detection: terminate if $|\;||u_{i+1}||_1 - ||u_{i}||_1\;|/ ||u_{i}||_1<$ tol_round occurred max_round times in a row.
    • tol_mono::Float64: Tolerance for monotonicity test: terminate with error if $\Delta u_i/\Delta u_{i-1}>$ 1/tol_mono.
    • damp_initial::Float64: Initial damping parameter $d_0$. To handle convergence problems, set this to a value less than 1.
    • damp_growth::Float64: Damping parameter growth factor: $d_{i+1}=\min(d_i\cdot$ max_growth $,1)$. It should be larger than 1.
    • max_round::Int64: Maximum number of consecutive iterations within roundoff error tolerance The default effectively disables this criterion.
    • unorm::Function: Calculation of Newton update norm
    • rnorm::Function: Functional for roundoff error calculation
    • method_linear::Union{Nothing, LinearSolve.SciMLLinearSolveAlgorithm}: Solver method for linear systems (see LinearSolve.jl). If given nothing, as default are chosen (for Float64 calculations):

      • 1D: KLUFactorization()
      • 2D: SparspakFactorization()
      • 3D: UMFPACKFactorization()

      SparspakFactorization() is the default choice for general number types. Users should experiment with what works best for their problem.

      For easy access to this functionality, see see also VoronoiFVM.LinearSolverStrategy.

    • reltol_linear::Float64: Relative tolerance of iterative linear solver.
    • abstol_linear::Float64: Absolute tolerance of iterative linear solver.
    • maxiters_linear::Int64: Maximum number of iterations of linear solver
    • precon_linear::Union{Nothing, Function, ExtendableSparse.AbstractFactorization, LinearSolve.SciMLLinearSolveAlgorithm, Type}: Constructor for preconditioner for linear systems. This should work as a function precon_linear(A) which takes an AbstractSparseMatrixCSC (e.g. an ExtendableSparseMatrix) and returns a preconditioner object in the sense of LinearSolve.jl, i.e. which has an ldiv!(u,A,v) method. Useful examples:

      • ExtendableSparse.ILUZero
      • ExtendableSparse.Jacobi

      For easy access to this functionality, see see also VoronoiFVM.LinearSolverStrategy.

    • keepcurrent_linear::Bool: Update preconditioner in each Newton step ?
    • Δp::Float64: Initial parameter step for embedding.
    • Δp_max::Float64: Maximal parameter step size.
    • Δp_min::Float64: Minimal parameter step size.
    • Δp_grow::Float64: Maximal parameter step size growth.
    • Δp_decrease::Float64: Parameter step decrease factor upon rejection
    • Δt::Float64: Initial time step size.
    • Δt_max::Float64: Maximal time step size.
    • Δt_min::Float64: Minimal time step size.
    • Δt_grow::Float64: Maximal time step size growth.
    • Δt_decrease::Float64: Time step decrease factor upon rejection
    • Δu_opt::Float64: Optimal size of update for time stepping and embedding. The algorithm tries to keep the difference in norm between "old" and "new" solutions approximately at this value.
    • Δu_max_factor::Float64: Control maximum sice of update Δu for time stepping and embeding relative to Δu_opt. Time steps with Δu > Δu_max_factor*Δu_opt will be rejected.
    • force_first_step::Bool: Force first timestep.
    • num_final_steps::Int64: Number of final steps to adjust at end of time interval in order to prevent breakdown of step size.
    • handle_exceptions::Bool: Handle exceptions during transient solver and parameter embedding. If true, exceptions in Newton solves are caught, the embedding resp. time step is lowered, and solution is retried. Moreover, if embedding or time stepping fails (e.g. due to reaching minimal step size), a warning is issued, and a solution is returned with all steps calculated so far.

      Otherwise (by default) errors are thrown.

    • store_all::Bool: Store all steps of transient/embedding problem:
    • in_memory::Bool: Store transient/embedding solution in memory
    • log::Any: Record history
    • edge_cutoff::Float64: Edge parameter cutoff for rectangular triangles.
    • pre::Function: Function pre(sol,t) called before time/embedding step
    • post::Function: Function post(sol,oldsol,t,Δt) called after successful time/embedding step
    • sample::Function: Function sample(sol,t) to be called for each t in times[2:end]
    • delta::Function: Time step error estimator. A function Δu=delta(system,u,uold,t,Δt) to calculate Δu.
    • tol_absolute::Union{Nothing, Float64}

    • tol_relative::Union{Nothing, Float64}

    • damp::Union{Nothing, Float64}

    • damp_grow::Union{Nothing, Float64}

    • max_iterations::Union{Nothing, Int64}

    • tol_linear::Union{Nothing, Float64}

    • max_lureuse::Union{Nothing, Int64}

    • mynorm::Union{Nothing, Function}

    • myrnorm::Union{Nothing, Function}

    source

    Linear solver strategies

    VoronoiFVM.LinearSolverStrategyType
    VoronoiFVM.LinearSolverStrategy

    An linear solver strategy provides the possibility to construct SolverControl objects as follows:

        SolverControl(strategy,sys;kwargs...)

    , e.g.

        SolverControl(GMRESIteration(UMFPackFactorization(), EquationBlock()),sys; kwargs...)

    A linear solver strategy combines a Krylov method with a preconditioner which by default is calculated from the linearization of the initial value of the Newton iteration. For coupled systems, a blocking strategy can be chosen. The EquationBlock strategy calculates preconditioners or LU factorization separately for each species equation and combines them to a block Jacobi preconditioner. The PointBlock strategy treats the linear system as consisting of nspecies x nspecies blocks.

    Which is the best strategy, depends on the space dimension. The following is a rule of thumb for choosing strategies

    • For 1D problems use direct solvers
    • For 2D stationary problems, use direct solvers, for transient problems consider iterative solvers which can take advantage of the diagonal dominance of the implicit timestep problem
    • For 3D problems avoid direct solvers

    Currently available strategies are:

    Notable LU Factorizations/direct solvers are:

    Notable incomplete factorizations/preconditioners

    Blocking strategies are:

    source
    VoronoiFVM.CGIterationType
    CGIteration(;factorization=UMFPACKFactorization())
    +CGIteration(factorization)

    CG Iteration from Krylov.jl via LinearSolve.jl.

    source
    VoronoiFVM.BICGstabIterationType
    BICGstabIteration(;factorization=UMFPACKFactorization())
    +BICGstabIteration(factorization)

    BICGstab Iteration from Krylov.jl via LinearSolve.jl.

    source
    VoronoiFVM.GMRESIterationType
    GMRESIteration(;factorization=ILUZeroFactorization(), memory=20, restart=true)
    +GMRESIteration(factorization; memory=20, restart=true)

    GMRES Iteration from Krylov.jl via LinearSolve.jl.

    source

    Block preconditioning

    This feature is under development as of 1.6.

    VoronoiFVM.EquationBlockType
    EquationBlock()

    Equation-wise blocking. Can be combined with any preconditioner resp. factorization including direct solvers.

    source
    VoronoiFVM.PointBlockType
    PointBlock()

    Point-wise blocking. Currently only together with ILUZeroFactorization. This requires a system with unknown_storage=:dense.

    source

    History handling

    If log is set to true in solve, the history of newton iterations and time/embedding steps is recorded and. For the respective previous solution step it can be obtained via history(system).

    VoronoiFVM.NewtonSolverHistoryType
    mutable struct NewtonSolverHistory <: AbstractVector{Float64}

    History information for one Newton solve of a nonlinear system. As an abstract vector it provides the history of the update norms. See summary and details for other ways to extract information.

    • nlu::Int64: number of Jacobi matrix factorizations

    • nlin::Int64: number of linear iteration steps / factorization solves

    • time::Float64: Elapsed time for solution

    • tasm::Float64: Elapsed time for assembly

    • tlinsolve::Float64: Elapsed time for linear solve

    • updatenorm::Any: History of norms of $||u_{i+1}-u_i||$

    • l1normdiff::Any: History of norms of $|\;||u_{i+1}||_1 - ||u_{i}||_1\;|/ ||u_{i}||_1$

    source
    VoronoiFVM.TransientSolverHistoryType
    mutable struct TransientSolverHistory <: AbstractVector{NewtonSolverHistory}

    History information for transient solution/parameter embedding

    As an abstract vector it provides the histories of each implicit Euler/embedding step. See summary and details for other ways to extract information.

    • histories::Any: Histories of each implicit Euler Newton iteration

    • times::Any: Time values

    • updates::Any: Update norms used for step control

    source
    Base.summaryMethod
    summary(h::NewtonSolverHistory)

    Return named tuple summarizing history.

    source
    Base.summaryMethod
    summary(h::TransientSolverHistory)

    Return named tuple summarizing history.

    source
    VoronoiFVM.detailsFunction
    details(h::NewtonSolverHistory)

    Return array of named tuples with info on each iteration step

    source
    details(h::TransientSolverHistory)

    Return array of details of each solver step

    source
    VoronoiFVM.history_detailsFunction
    history_details(tsol)

    Return details of solver history from last solve call, if log was set to true. See details.

    source
    history_details(sys)

    Return details of solver history from last solve call, if log was set to true. See details.

    source
    VoronoiFVM.history_summaryFunction
    history_summary(tsol)

    Return summary of solver history from last solve call, if log was set to true.

    source
    history_summary(sys)

    Return summary of solver history from last solve call, if log was set to true.

    source

    Matrix extraction

    For testing and teaching purposes, one can obtain residual and linearization at a given vector of unknowns

    VoronoiFVM.evaluate_residual_and_jacobianFunction
    evaluate_residual_and_jacobian(system,u;
    +                               t=0.0, tstep=Inf,embed=0.0)

    Evaluate residual and jacobian at solution value u. Returns a solution vector containing a copy of residual, and an ExendableSparseMatrix containing a copy of the linearization at u.

    source

    OrdinaryDiffEq.jl transient solver

    For transient problems, as an alternative to the built-in implicit Euler method, (stiff) ODE solvers from OrdinaryDiffEq.jl can be used.

    The interface just provides two methods: creation of an ODEProblem from a VoronoiFVM.System and a reshape method which turns the output of the ode solver into a TransientSolution.

    The basic usage pattern is as follows: use OrdinaryDiffEq.jl and replace the call to the built-in solver

    sol=solve(sys; times=(t0,t1), inival=inival)

    by

    problem = ODEProblem(sys,inival,(t0,t1))
    +odesol = solve(problem, solver)
    +sol=reshape(odesol,sys)

    Here, solver is some ODE/DAE solver from OrdinaryDiffEq.jl. It is preferable to choose methods able to handle stiff problems. Moreover, often, discretized PDE systems (e.g. containing elliptic equations) are differential agebraic equation (DAE) systems which should be solved by DAE solvers. Some choices to start with are Rosenbrock methods like Rosenbrock23 and multistep methods like QNDF and FBDF.

    If the DifferentialEquations.jl package is loaded, the solver parameter can be omitted, and some default is chosen.

    The solution odesol returned by solve conforms to the ArrayInterface but "forgot" the VoronoiFVM species structure. Using

    Accessing odesol(t) will return an interpolated solution vector giving the value of the solution at moment t. Using reshape(::AbstractVector, ::VoronoiFVM.AbstractSystem) on (odesol(t),system) it can be turned into into a sparse or dense array reflecting the species structure of system. The order of the interpolation depends on the ODE solver.

    Using reshape(::AbstractDiffEqArray,::VoronoiFVM.AbstractSystem) on (odesol, system) returns a TransientSolution knowing the species structure.

    Base.reshapeMethod
    reshape(ode_solution, system, times=nothing)

    Create a TransientSolution from the output of the ode solver which reflects the species structure of the system ignored by the ODE solver. Howvever the interpolation behind reshaped_sol(t) will be linear and ignores the possibility of higher order interpolations with ode_sol.

    If times is specified, the (possibly higher ordee) interpolated solution at the given moments of time will be returned.

    source
    SciMLBase.ODEFunctionMethod
     ODEFunction(system,inival=unknowns(system,inival=0),t0=0)

    Create an ODEPFunction in mass matrix form to be handeled by ODE solvers from DifferentialEquations.jl.

    Parameters:

    • system: A VoronoiFVM.System
    • jacval (optional): Initial value. Default is a zero vector. Consider to pass a stationary solution at time tjac.
    • tjac (optional): tjac, Default: 0

    The jacval and tjac are passed for a first evaluation of the Jacobian, allowing to detect the sparsity pattern which is passed to the solver.

    source

    Legacy API

    During the development of the code, a number of API variants have been developed which are supported for backward compatibility.

    VoronoiFVM.SolverStrategiesModule
    SolverStrategies
    Only available in 1.5

    Please replace this functionality by the new strategy API in 1.6 as follows:

    direct_umfpack() = DirectSolver(UMFPACKFactorization())                            
    +gmres_umfpack() = GMRESIteration(UMFPACKFactorization())                           
    +gmres_eqnblock_umfpack() = GMRESIteration(UMFPACKFactorization(), EquationBlock()) 
    +gmres_iluzero() = GMRESIteration(ILUZeroPreconditioner())                          
    +gmres_eqnblock_iluzero() = GMRESIteration(ILUZeroPreconditioner(), EquationBlock())
    +gmres_pointblock_iluzero() = GMRESIteration(ILUZeroPreconditioner(), PointBlock()) 
    source
    diff --git a/v1.19.1/system/index.html b/v1.19.1/system/index.html new file mode 100644 index 000000000..e3b56a439 --- /dev/null +++ b/v1.19.1/system/index.html @@ -0,0 +1,38 @@ + +System · VoronoiFVM.jl

    System

    The computational grid required is assumed to correspond to a domain $\Omega=\cup_{r=1}^{n_\Omega} \Omega_r$

    Grids for VoronoiFVM are managed by the packages ExtendableGrids.jl and SimplexGridFactory.jl

    with boundary $\partial\Omega=\Gamma=\cup_{b=1}^{n_\Gamma} \Gamma_b$.

    The subdomains $\Omega_r$ are called "regions" and the boundary subdomains $\Gamma_b$ are called "boundary regions".

    On this complex of domains "lives" a number of species which are either attached to a number of regions or to a number of boundary regions.

    All these data, the matrix for the linear system and other things are hold together by a struct VoronoiFVM.System. This type is not exported to avoid name clashes.

    System constructors

    VoronoiFVM.SystemMethod
    System(grid; kwargs...)

    Create structure of type VoronoiFVM.System{Tv,Ti, Tm, TSpecMat<:AbstractMatrix, TSolArray<:AbstractMatrix} holding data for finite volume system solution.

    Parameters:

    • grid::ExtendableGrid: 1, 2 or 3D computational grid

    Keyword arguments:

    • species: vector of integer species indices. Added to all grid regions, avoiding the need to call enable_species! for this default case. If it is kept empty, species have be added to the system after creation via enable_species!.
    • unknown_storage: string or symbol. Information on species distribution is kept in sparse or dense matrices matrices and, correspondingly, the solution array is of type SparseSolutionArray or matrix, respectively. In the case of sparse unknown storage, the system matrix handles exactly those degrees of freedom which correspond to unknowns. However, handling of the sparse matrix structures for the bookkeeping of the unknowns creates overhead.
      • :dense : solution vector is an nspecies x nnodes dense matrix
      • :sparse : solution vector is an nspecies x nnodes sparse matrix
    • matrixindextype: Integer type. Index type for sparse matrices created in the system.
    • is_linear: whether the system is linear or not. If it is linear, only one Newton step is used to solve it.
    • assembly: either :cellwise (default) or :edgewise. Determine, how the assembly loop is organized. :cellwise means that the outer loop goes over grid cells (triangles, tetrahedra), and contributions to edge fluxes and node reactions are calculated for each cell. As a consequence, e.g. im 2D for all interior edges, flux functions are callled twice, once for each adjacent cell. Especially in 3D, this becomes a significant overhead. With :edgewise, geometry factors of these edges are pre-assembled, and the outer assembly loops go over all grid edges resp. nodes, still with separate calls if neigboring cells belong to different regions.
    Note

    It is planned to make :edgewise the default in a later version.

    Physics keyword arguments:

    • flux: Function. Flux between neighboring control volumes: flux(f,u,edge) or flux(f,u,edge,data) should return in f[i] the flux of species i along the edge joining circumcenters of neighboring control volumes. For species i,u[i,1] and u[i,2] contain the unknown values at the corresponding ends of the edge.

    • storage: Function. Storage term (term under time derivative): storage(f,u,node) or storage(f,u,node,data) It should return in f[i] the storage term for the i-th equation. u[i] contains the value of the i-th unknown.

    • reaction: Function. Reaction term: reaction(f,u,node) or reaction(f,u,node,data) It should return in f[i] the reaction term for the i-th equation. u[i] contains the value of the i-th unknown.

    • edgereaction: Function. Edge reeaction term: edgereaction(f,u,edge) or edgereaction(f,u,edge,data) It should return in f[i] the reaction term for the i-th equation. For species i,u[i,1] and u[i,2] contain the unknown values at the corresponding ends of the edge.

    • source: Function. Source term: source(f,node) or source(f,node,data). It should return the in f[i] the value of the source term for the i-th equation.

    • bflux: Function. Flux between neighboring control volumes on the boundary

    • breaction Function. Boundary reaction term: breaction(f,u,node) or breaction(f,u,node,data) Similar to reaction, but restricted to the inner or outer boundaries.

    • bcondition Function. Alias for breaction.

    • bsource: Function. Boundary source term: bsource(f,node) or bsource(f,node,data). It should return in f[i] the value of the source term for the i-th equation.

    • bstorage: Function. Boundary storage term: bstorage(f,u,node) or bstorage(f,u,node,data) Similar to storage, but restricted to the inner or outer boundaries.

    • generic_operator: Function. Generic operator generic_operator(f,u,sys). This operator acts on the full solution u of a system. Sparsity is detected automatically unless generic_operator_sparsity is given.

    • generic_operator_sparsity: Function defining the sparsity structure of the generic operator. This should return the sparsity pattern of the generic_operator.

    • nparams: number of parameters the system is depending on, and with respect to which the derivatives need to be obtained

    • data: User data (parameters). This allows to pass various parameters to the callback functions. If data is given, all callback functions should accept a last data argument. Otherwise, no data are passed explicitly, and constitutive callbacks can take parameters from the closure where the function is defined.

    • matrixtype: :default, :sparse, :tridiagonal, :banded

    source

    Adding species by species numbers

    VoronoiFVM.enable_species!Method
    enable_species!(system,ispec,regions)

    Add species ispec to a list of bulk regions. Species numbers for bulk and boundary species have to be distinct. Once a species has been added, it cannot be removed.

    source
    VoronoiFVM.enable_species!Method
    enable_species!(system; kwargs...)

    Keyword arguments:

    • species: Integer or vector of integers. Species to be added to the system.
    • regions: Vector of integers. Regions, where these species shall be added.If nothing, they are added to all species.

    Once a species has been added, it cannot be removed.

    source
    VoronoiFVM.enable_boundary_species!Function
    enable_boundary_species!(system,ispec,regions)

    Add species ispec to a list of boundary regions. Species numbers for bulk and boundary species have to be distinct. Once a species has been added, it cannot be removed.

    source

    Handling boundary conditions

    Boundary conditions are handled in the bcondition callback passed to the system constructor. For being called in this callback, the following functions are available

    VoronoiFVM.boundary_dirichlet!Method
     boundary_dirichlet!(y,u,bnode, args...; kwargs...)

    Keyword argument version:

    • species: species number. Default: 1
    • region: boundary region number. By default, all boundary regions.
    • value: value
    source
    VoronoiFVM.boundary_neumann!Method
     boundary_neumann!(y,u,bnode, args...; kwargs...)

    Keyword argument version:

    • species: species number. Default: 1
    • region: boundary region number. By default, all boundary regions.
    • value: value
    source
    VoronoiFVM.boundary_robin!Method
     boundary_robin!(y,u,bnode, args...; kwargs...)

    Keyword argument version:

    • species: species number. Default: 1
    • region: boundary region number. By default, all boundary regions.
    • factor: factor
    • value: value
    source
    VoronoiFVM.rampFunction
       ramp(t; kwargs...)

    Ramp function for specifying time dependent boundary conditions

    Keyword arguments:

    • dt: Tuple: start and end time of ramp. Default: (0,0.1)
    • du: Tuple: values at start and end time. Default: (0,0)
    source

    Outflow boundary conditions

    These are characterized by the boutflow physics callback and and the outflowboundaries keyword argument in the system resp. physics constructor. See also the corresponding notebook

    VoronoiFVM.hasoutflownodeFunction
    hasoutflownode(edge)

    Check if one node of the edge is situated on a boundary region listed in outflowboundaries, see [struct Physics].

    source
    VoronoiFVM.isoutflownodeFunction
    isoutflownode(edge,inode)

    Check if inode (1 or 2) is an outflow node.

    source
    isoutflownode(edge,inode,irefgion)

    Check if inode (1 or 2) is an outflow node on boundary region iregion.

    source

    Allocation warnings

    The code checks for allocations in the assembly loop. Care has been taken to ensure that allocations in the assembly loop don't emerge from VoronoiFVM.jl code.

    If allocations occur in the assembly loop, they happen in the physics callbacks. The corresponding warnings can bee switched off by passing a verbosity strings without 'a' to the solver. If no data are allocated in the physics callbacks, these allocations are probably due to type instabilities in physics callbacks, see the the discussion here. Type instabilities can be debugged via the @time macro applied to expressions in a physics callback.

    The following cases provide some ideas where to look for reasons of the problem and possible remedies:

    Case 1: a parameter changes its value, and Julia is not sure about the type.

    eps=1.0
    +
    +flux(f,u,edge)
    +    f[1]=eps*(u[1,1]-[1,2])
    +end
    +... solve etc ...
    +eps=2.0

    Remedy: use a type annotation eps::Float64=... to signalize your intent to Julia. This behaviour is explained in the Julia documentation.

    Case 2: variables in the closure have the same name as a variable introduced in a callback.

    flux(f,u,edge)
    +    x=(u[1,1]-[1,2])
    +    f[1]=x
    +end
    +
    +... create etc ...
    +
    +x=solve(...)

    Remedy: rename e.g. x=solve() to sol=solve()

    Various tools

    VoronoiFVM.num_speciesFunction
    num_species(edge::VoronoiFVM.AbstractEdge) -> Any
    +

    Return number of species for edge

    source
    num_species(system)
    +

    Number of species in system

    source
    num_species(a)
    +

    Number of species (size of first dimension) of solution array.

    source
    VoronoiFVM.unknownsMethod
    unknowns(system; inival, inifunc)
    +

    Create a solution vector for system. If inival is not specified, the entries of the returned vector are undefined.

    source
    unknowns(impedance_system)
    +

    Create a vector of unknowns of the impedance system

    source
    VoronoiFVM.unknownsMethod
    unknowns(Tu, system; inival, inifunc)
    +

    Create a solution vector for system with elements of type Tu. If inival is not specified, the entries of the returned vector are undefined.

    source
    Base.mapFunction
    map(inifunc, sys)
    +

    Create a solution vector for system using the callback inifunc which has the same signature as a source term.

    source
    map(inival, sys)
    +

    Create a solution vector for system using a constant initial value

    source
    Base.map!Function
    map!(inifunc, U, system)
    +

    Map inifunc onto solution array U

    source
    Base.reshapeMethod
    reshape(v, system)
    +

    Reshape vector to fit as solution to system.

    source

    Types

    VoronoiFVM.AbstractSystemType
    abstract type AbstractSystem{Tv<:Number, Tc<:Number, Ti<:Integer, Tm<:Integer}

    Abstract type for finite volume system structure.

    source
    VoronoiFVM.SystemType
    mutable struct System{Tv, Tc, Ti, Tm, TSpecMat<:(AbstractMatrix), TSolArray<:(AbstractMatrix)} <: VoronoiFVM.AbstractSystem{Tv, Tc, Ti, Tm}

    Structure holding data for finite volume system.

    source

    Legacy API

    VoronoiFVM.boundary_dirichlet!Method
    boundary_dirichlet!(system, ispec, ibc, v)
    +

    Set Dirichlet boundary condition for species ispec at boundary ibc:

    $u_{ispec}=v$ on $\Gamma_{ibc}$

    Info

    Starting with version 0.14, it is preferable to define boundary conditions within the bcondition physics callback

    source
    VoronoiFVM.boundary_dirichlet!Method
      boundary_dirichlet!(system; kwargs...)

    Keyword argument version:

    • species: species number
    • region: region number
    • value: value
    Info

    Starting with version 0.14, it is preferable to define boundary conditions within the bcondition physics callback

    source
    VoronoiFVM.boundary_neumann!Method
    boundary_neumann!(system, ispec, ibc, v)
    +

    Set Neumann boundary condition for species ispec at boundary ibc:

    $\mathrm{flux}_{ispec}\cdot \vec n=v$ on $\Gamma_{ibc}$

    Info

    Starting with version 0.14, it is preferable to define boundary conditions within the bcondition physics callback

    source
    VoronoiFVM.boundary_neumann!Method
      boundary_neumann!(system; kwargs...)

    Keyword argument version:

    • species: species number
    • region: region number
    • value: value
    Info

    Starting with version 0.14, it is preferable to define boundary conditions within the bcondition physics callback

    source
    VoronoiFVM.boundary_robin!Method
    boundary_robin!(system, ispec, ibc, α, v)
    +

    Set Robin boundary condition for species ispec at boundary ibc:

    $\mathrm{flux}_{ispec}\cdot \vec n + \alpha u_{ispec}=v$ on $\Gamma_{ibc}$

    Info

    Starting with version 0.14, it is preferable to define boundary conditions within the bcondition physics callback

    source
    VoronoiFVM.boundary_robin!Method
      boundary_robin!(system; kwargs...)

    Keyword argument version:

    • species: species number
    • region: region number
    • factor: factor
    • value: value
    Info

    Starting with version 0.14, it is preferable to define boundary conditions within the bcondition physics callback

    source
    VoronoiFVM.viewKFunction
    viewK(
    +    edge::VoronoiFVM.AbstractEdge,
    +    u
    +) -> VoronoiFVM.VectorUnknowns
    +

    Solution view on first edge node

    source
    VoronoiFVM.viewLFunction
    viewL(
    +    edge::VoronoiFVM.AbstractEdge,
    +    u
    +) -> VoronoiFVM.VectorUnknowns
    +

    Solution view on second edge node

    source
    diff --git a/v1.19.1/trivoro.png b/v1.19.1/trivoro.png new file mode 100644 index 0000000000000000000000000000000000000000..18289b9d5aaa3409ed24ec84ddd2173d798941ed GIT binary patch literal 114659 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 literal 0 HcmV?d00001 diff --git a/v1.19.1/vor.png b/v1.19.1/vor.png new file mode 100644 index 0000000000000000000000000000000000000000..9bba1c0729b0910b520a51970c6fb1cb9c8204c5 GIT binary patch literal 24601 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

    FF9smd3$bf!&n|JUwA^+E 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 c2*KX3Yv6#=k0Uw<5%}A0e!#5E;M1& literal 0 HcmV?d00001 diff --git a/versions.js b/versions.js index d5c9be2c4..628fcd6e8 100644 --- a/versions.js +++ b/versions.js @@ -36,5 +36,5 @@ var DOC_VERSIONS = [ "v0.5", "dev", ]; -var DOCUMENTER_NEWEST = "v1.19.0"; +var DOCUMENTER_NEWEST = "v1.19.1"; var DOCUMENTER_STABLE = "stable";