From adad6a8005f36bd6c15e24b6ebda2fc1b1a1e8a3 Mon Sep 17 00:00:00 2001 From: "Documenter.jl" Date: Fri, 29 Nov 2024 15:32:10 +0000 Subject: [PATCH] build based on e208890 --- dev/.documenter-siteinfo.json | 2 +- dev/allindex/index.html | 2 +- dev/changes/index.html | 2 +- dev/devel/index.html | 2 +- dev/extensions/index.html | 2 +- dev/grid/index.html | 2 +- dev/index.html | 2 +- dev/internal/index.html | 2 +- dev/method/index.html | 2 +- dev/misc/index.html | 2 +- .../Example001_Solvers/index.html | 2 +- .../Example002_EdgeReaction/index.html | 2 +- .../Example003_Solvers/index.html | 2 +- .../Example101_Laplace1D/index.html | 2 +- .../index.html | 2 +- .../index.html | 2 +- .../Example105_NonlinearPoisson1D/index.html | 2 +- .../Example106_NonlinearDiffusion1D/index.html | 2 +- .../Example107_NonlinearStorage1D/index.html | 2 +- .../Example108_OrdinaryDiffEq1D/index.html | 2 +- .../index.html | 2 +- .../index.html | 2 +- .../Example120_ThreeRegions1D/index.html | 2 +- .../Example121_PoissonPointCharge1D/index.html | 2 +- .../Example125_TestFunctions1D/index.html | 2 +- .../Example150_Impedance1D/index.html | 2 +- .../Example151_Impedance1D/index.html | 2 +- .../index.html | 2 +- .../Example201_Laplace2D/index.html | 2 +- .../Example203_CoordinateSystems/index.html | 2 +- .../Example204_HagenPoiseuille/index.html | 2 +- .../Example205_StagnationPoint/index.html | 2 +- .../Example206_JouleHeat/index.html | 2 +- .../Example207_NonlinearPoisson2D/index.html | 2 +- .../index.html | 2 +- .../index.html | 2 +- .../index.html | 2 +- .../Example221_EquationBlockPrecon/index.html | 2 +- .../Example225_TestFunctions2D/index.html | 2 +- .../Example226_BoundaryIntegral/index.html | 2 +- .../Example230_BoundaryFlux/index.html | 2 +- .../index.html | 2 +- .../Example301_Laplace3D/index.html | 2 +- .../index.html | 2 +- .../Example405_GenericOperator/index.html | 2 +- .../Example406_WeirdReaction/index.html | 2 +- .../Example410_ManySpecies/index.html | 2 +- .../index.html | 2 +- .../index.html | 2 +- .../Example422_InterfaceQuantities/index.html | 2 +- .../index.html | 2 +- .../index.html | 2 +- .../Example440_ParallelState/index.html | 2 +- .../Example510_Mixture/index.html | 2 +- dev/notebooks/index.html | 2 +- dev/physics/index.html | 2 +- .../api-update/index.html | 4 ++-- .../bernoulli/index.html | 2 +- .../flux-reconstruction/index.html | 4 ++-- .../heterogeneous-catalysis/index.html | 18 +++++++++--------- .../interfaces1d/index.html | 2 +- .../nonlinear-solvers/index.html | 8 ++++---- .../ode-brusselator/index.html | 8 ++++---- .../ode-diffusion1d/index.html | 8 ++++---- .../ode-nlstorage1d/index.html | 10 +++++----- .../ode-wave1d/index.html | 8 ++++---- .../outflow/index.html | 2 +- .../problemcase/index.html | 4 ++-- dev/post/index.html | 2 +- dev/quantities/index.html | 2 +- dev/runexamples/index.html | 2 +- dev/search_index.js | 2 +- dev/solutions/index.html | 2 +- dev/solver/index.html | 2 +- dev/system/index.html | 2 +- 75 files changed, 102 insertions(+), 102 deletions(-) diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index cf57862e2..5a2fa966b 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.11.1","generation_timestamp":"2024-11-29T14:45:30","documenter_version":"1.8.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.11.1","generation_timestamp":"2024-11-29T15:31:37","documenter_version":"1.8.0"}} \ No newline at end of file diff --git a/dev/allindex/index.html b/dev/allindex/index.html index 3b8bdcefb..93e9d11c8 100644 --- a/dev/allindex/index.html +++ b/dev/allindex/index.html @@ -1,2 +1,2 @@ -Index · VoronoiFVM.jl

Index

Exported

VoronoiFVMModule
VoronoiFVM

VoronoiFVM.jl

Build status DOI Zulip Chat code style: runic

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.

JuliaCon 2024 Lightning Talk: abstract, video

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

Some projects and packages using VoronoiFVM.jl

Citation

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

source

Types and Constructors

Constants

Methods

+Index · VoronoiFVM.jl

Index

Exported

VoronoiFVMModule
VoronoiFVM

VoronoiFVM.jl

Build status DOI Zulip Chat code style: runic

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.

JuliaCon 2024 Lightning Talk: abstract, video

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

Some projects and packages using VoronoiFVM.jl

Citation

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

source

Types and Constructors

Constants

Methods

diff --git a/dev/changes/index.html b/dev/changes/index.html index 77c8c4a7a..9be1336ba 100644 --- a/dev/changes/index.html +++ b/dev/changes/index.html @@ -10,4 +10,4 @@ for ispec=1:nspec y[ispec]=u[ispec,1]-u[ispec,2] end - end

v0.8.5 Sep 1 2020

v0.8.4 July 25 2020

v0.8.3 June 25 2020

v0.8.2 May 15 2020

v0.8.1 May 2 2020

v0.8 Apr 28, 2020

v0.7 Feb 28 2020

v0.6.5 Jan 25 2020

v0.6.4 2020-01-20

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

v0.6.0, Dec 15 2019

v0.5.6 Dec 5 2019

v0.5.5 Dec 4 2019

v0.5.4 Dec 3 2019

v0.5.3 Dec 1 2019

v0.5.2 Nov 19, 2019

v0.5.1 Nov 13, 2019

V0.5, November 10, 2019

V0.4.2, November 6, 2019

V0.4, July 12, 2019

V0.3, April 9 2019

V0.2, Feb 20, 2019

V0.1, Dec. 2018

+ end

v0.8.5 Sep 1 2020

v0.8.4 July 25 2020

v0.8.3 June 25 2020

v0.8.2 May 15 2020

v0.8.1 May 2 2020

v0.8 Apr 28, 2020

v0.7 Feb 28 2020

v0.6.5 Jan 25 2020

v0.6.4 2020-01-20

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

v0.6.0, Dec 15 2019

v0.5.6 Dec 5 2019

v0.5.5 Dec 4 2019

v0.5.4 Dec 3 2019

v0.5.3 Dec 1 2019

v0.5.2 Nov 19, 2019

v0.5.1 Nov 13, 2019

V0.5, November 10, 2019

V0.4.2, November 6, 2019

V0.4, July 12, 2019

V0.3, April 9 2019

V0.2, Feb 20, 2019

V0.1, Dec. 2018

diff --git a/dev/devel/index.html b/dev/devel/index.html index 3108ecf96..fda1cded9 100644 --- a/dev/devel/index.html +++ b/dev/devel/index.html @@ -1,2 +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 environment 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 temporary modules, and @test macros can be used in the notebooks.
+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 environment 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 temporary modules, and @test macros can be used in the notebooks.
diff --git a/dev/extensions/index.html b/dev/extensions/index.html index c330d717c..a21547af9 100644 --- a/dev/extensions/index.html +++ b/dev/extensions/index.html @@ -1,4 +1,4 @@ ExtendableFEMBase Extension · VoronoiFVM.jl

ExtendableFEMBase Extension

The extension for ExtendableFEMBase extends the functions below for solution types of ExtendableFEM. The name of the extension originates from the solution type FEVectorBlock that is defined in ExtendableFEMBase.

+

Compute VoronoiFVM.bfacevelocities for a finite element flow field computed by ExtendableFEM.

source diff --git a/dev/grid/index.html b/dev/grid/index.html index a78adc0dc..0e107e60b 100644 --- a/dev/grid/index.html +++ b/dev/grid/index.html @@ -1,2 +1,2 @@ -Grid · VoronoiFVM.jl
+Grid · VoronoiFVM.jl
diff --git a/dev/index.html b/dev/index.html index abef0c80d..05bb3248f 100644 --- a/dev/index.html +++ b/dev/index.html @@ -1,2 +1,2 @@ -Home · VoronoiFVM.jl

VoronoiFVM.jl

Build status DOI Zulip Chat code style: runic

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.

JuliaCon 2024 Lightning Talk: abstract, video

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

Some projects and packages using VoronoiFVM.jl

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 updating CITATION.bib if you have published work which could be added to this list.

[1]
D. Brust, M. Wullenkord, H. G. Gómez, J. Albero and C. Sattler. Experimental Investigation of Photo-Thermal Catalytic Reactor for the Reverse Water Gas Shift Reaction under Concentrated Irradiation. Journal of Environmental Chemical Engineering, 113372 (2024).
[2]
[3]
D. Brust, K. Hopf, J. Fuhrmann, A. Cheilytko, M. Wullenkord and C. Sattler. Transport of Heat and Mass for Reactive Gas Mixtures in Porous Media: Modeling and Application (SSRN, 2024).
[4]
M. U. Qureshi, S. Matera, D. Runge, C. Merdon, J. Fuhrmann, J.-U. Repke and G. Brösigke. Reduced Order Cfd Modeling Approach Based on the Asymptotic Expansion-An Application for Heterogeneous Catalytic Systems (SSRN, 2024).
[5]
T. Belin, P. Lafitte-Godillon, V. Lescarret, J. Fuhrmann and C. Mascia. Entropy solutions of a diffusion equation with discontinuous hysteresis and their finite volume approximation (HAL, 2024).
[6]
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.
[7]
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).
[8]
S. Scholz and L. Berger. Hestia.jl: A Julia Library for Heat Conduction Modeling with Boundary Actuation. Simul. Notes Eur. 33, 27–30 (2023).
[9]
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).
[10]
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.
[11]
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).
[12]
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).
[13]
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).
[14]
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).
[15]
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.
[16]
J. Jambrich. Consistent non-equilibrium thermodynamic modeling of hydrogen fuel cells. Master's thesis, Univerzita Karlova, Matematicko-fyzikálnı́ fakulta (2022).
[17]
S. B. Chinnery. TCAD-Informed Surrogate Models of Semiconductor Devices. Master's thesis, Massachusetts Institute of Technology (2022).
[18]
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).
[19]
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).
[20]
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).
[21]
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).
[22]
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.
+Home · VoronoiFVM.jl

VoronoiFVM.jl

Build status DOI Zulip Chat code style: runic

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.

JuliaCon 2024 Lightning Talk: abstract, video

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

Some projects and packages using VoronoiFVM.jl

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 updating CITATION.bib if you have published work which could be added to this list.

[1]
D. Brust, M. Wullenkord, H. G. Gómez, J. Albero and C. Sattler. Experimental Investigation of Photo-Thermal Catalytic Reactor for the Reverse Water Gas Shift Reaction under Concentrated Irradiation. Journal of Environmental Chemical Engineering, 113372 (2024).
[2]
[3]
D. Brust, K. Hopf, J. Fuhrmann, A. Cheilytko, M. Wullenkord and C. Sattler. Transport of Heat and Mass for Reactive Gas Mixtures in Porous Media: Modeling and Application (SSRN, 2024).
[4]
M. U. Qureshi, S. Matera, D. Runge, C. Merdon, J. Fuhrmann, J.-U. Repke and G. Brösigke. Reduced Order Cfd Modeling Approach Based on the Asymptotic Expansion-An Application for Heterogeneous Catalytic Systems (SSRN, 2024).
[5]
T. Belin, P. Lafitte-Godillon, V. Lescarret, J. Fuhrmann and C. Mascia. Entropy solutions of a diffusion equation with discontinuous hysteresis and their finite volume approximation (HAL, 2024).
[6]
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.
[7]
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).
[8]
S. Scholz and L. Berger. Hestia.jl: A Julia Library for Heat Conduction Modeling with Boundary Actuation. Simul. Notes Eur. 33, 27–30 (2023).
[9]
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).
[10]
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.
[11]
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).
[12]
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).
[13]
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).
[14]
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).
[15]
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.
[16]
J. Jambrich. Consistent non-equilibrium thermodynamic modeling of hydrogen fuel cells. Master's thesis, Univerzita Karlova, Matematicko-fyzikálnı́ fakulta (2022).
[17]
S. B. Chinnery. TCAD-Informed Surrogate Models of Semiconductor Devices. Master's thesis, Massachusetts Institute of Technology (2022).
[18]
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).
[19]
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).
[20]
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).
[21]
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).
[22]
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/dev/internal/index.html b/dev/internal/index.html index be54ab0f8..34849dbc5 100644 --- a/dev/internal/index.html +++ b/dev/internal/index.html @@ -66,4 +66,4 @@

This is an internal function similar to integrate(::Type{<:Cartesian2D},...), but computes instead $\int_{\sigma} r \, \mathbf{v} \cdot \mathbf{n} \,\mathrm{ds} \lvert x_K - x_L \rvert / \left ( \lvert\sigma\rvert r(\mathrm{mid}(\sigma)) \right )$ where $r(\mathrm{mid}(\sigma))$ is the $r$-coordinate of the mid-point of $\sigma$.

source
VoronoiFVM.doolittle_ludecomp!Function
doolittle_ludecomp!(LU)
 

Non-pivoting inplace LU factorization using Doolittle's method. Adapted from https://en.wikipedia.org/wiki/LUdecomposition#MATLABcode_example.

source
VoronoiFVM.doolittle_lusolve!Function
doolittle_lusolve!(LU, b)
 

Non-pivoting inplace upper and lower triangular solve of matrix factorized with doolittle_ludecomp!. Adapted from https://en.wikipedia.org/wiki/LUdecomposition#MATLABcode_example.

source
VoronoiFVM.bernoulli_hornerFunction
bernoulli_horner(x)
-

Calculation of Bernoulli function via Horner scheme based on Taylor coefficients around 0.

source
VoronoiFVM._print_errorFunction

Print error when catching exceptions

source
+

Calculation of Bernoulli function via Horner scheme based on Taylor coefficients around 0.

source
VoronoiFVM._print_errorFunction

Print error when catching exceptions

source
diff --git a/dev/method/index.html b/dev/method/index.html index d3ee2a45b..a699d84d5 100644 --- a/dev/method/index.html +++ b/dev/method/index.html @@ -18,4 +18,4 @@ \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:

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:

History and literature

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

Software API and implementation

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

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.

+\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:

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:

History and literature

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

Software API and implementation

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

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/dev/misc/index.html b/dev/misc/index.html index 0d811940e..33d3299e2 100644 --- a/dev/misc/index.html +++ b/dev/misc/index.html @@ -2,4 +2,4 @@ Miscellaneous · VoronoiFVM.jl

Miscellaneous

Useful helpers

ForwardDiff.valueFunction
value(x)

Return the value of a dual number (for debugging in callback functions). Re-exported from ForwardDiff.

source

Form factor calculatione

Additional grid methods

VoronoiFVM.GridFunction
Grid=ExtendableGrids.simplexgrid

Re-Export of ExtendableGrids.simplexgrid

Compat

Deprecated as of v1.20

source

Exception types

+

Set coordinate system in grid to spherical symmetry.

source

Exception types

VoronoiFVM.AssemblyErrorType
struct AssemblyError <: Exception

Exception thrown if error occurred during assembly (e.g. domain error)

source
VoronoiFVM.ConvergenceErrorType
struct ConvergenceError <: Exception

Exception thrown if Newton's method convergence fails.

source
VoronoiFVM.EmbeddingErrorType
struct EmbeddingError <: Exception

Exception thrown if embedding fails

source
VoronoiFVM.LinearSolverErrorType
struct LinearSolverError <: Exception

Exception thrown if error occurred during factorization.

source
diff --git a/dev/module_examples/Example001_Solvers/index.html b/dev/module_examples/Example001_Solvers/index.html index ff2f5d4a4..313671b32 100644 --- a/dev/module_examples/Example001_Solvers/index.html +++ b/dev/module_examples/Example001_Solvers/index.html @@ -172,4 +172,4 @@ end return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example002_EdgeReaction/index.html b/dev/module_examples/Example002_EdgeReaction/index.html index 32cbd0237..55db30541 100644 --- a/dev/module_examples/Example002_EdgeReaction/index.html +++ b/dev/module_examples/Example002_EdgeReaction/index.html @@ -199,4 +199,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example003_Solvers/index.html b/dev/module_examples/Example003_Solvers/index.html index 816defbcc..ea98b7c57 100644 --- a/dev/module_examples/Example003_Solvers/index.html +++ b/dev/module_examples/Example003_Solvers/index.html @@ -179,4 +179,4 @@ end return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example101_Laplace1D/index.html b/dev/module_examples/Example101_Laplace1D/index.html index 1b8cd148f..bc89c60bd 100644 --- a/dev/module_examples/Example101_Laplace1D/index.html +++ b/dev/module_examples/Example101_Laplace1D/index.html @@ -59,4 +59,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example102_StationaryConvectionDiffusion1D/index.html b/dev/module_examples/Example102_StationaryConvectionDiffusion1D/index.html index 74d3855d7..13c740a3c 100644 --- a/dev/module_examples/Example102_StationaryConvectionDiffusion1D/index.html +++ b/dev/module_examples/Example102_StationaryConvectionDiffusion1D/index.html @@ -100,4 +100,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example103_ConvectionDiffusion1D/index.html b/dev/module_examples/Example103_ConvectionDiffusion1D/index.html index 797285805..e73f631ae 100644 --- a/dev/module_examples/Example103_ConvectionDiffusion1D/index.html +++ b/dev/module_examples/Example103_ConvectionDiffusion1D/index.html @@ -79,4 +79,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example105_NonlinearPoisson1D/index.html b/dev/module_examples/Example105_NonlinearPoisson1D/index.html index e203558ab..e5b7c50f9 100644 --- a/dev/module_examples/Example105_NonlinearPoisson1D/index.html +++ b/dev/module_examples/Example105_NonlinearPoisson1D/index.html @@ -83,4 +83,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example106_NonlinearDiffusion1D/index.html b/dev/module_examples/Example106_NonlinearDiffusion1D/index.html index 00232fffc..1ccda978f 100644 --- a/dev/module_examples/Example106_NonlinearDiffusion1D/index.html +++ b/dev/module_examples/Example106_NonlinearDiffusion1D/index.html @@ -100,4 +100,4 @@ nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example107_NonlinearStorage1D/index.html b/dev/module_examples/Example107_NonlinearStorage1D/index.html index b1f94be46..7c75edd0a 100644 --- a/dev/module_examples/Example107_NonlinearStorage1D/index.html +++ b/dev/module_examples/Example107_NonlinearStorage1D/index.html @@ -103,4 +103,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example108_OrdinaryDiffEq1D/index.html b/dev/module_examples/Example108_OrdinaryDiffEq1D/index.html index a16de98d0..3d9c366ad 100644 --- a/dev/module_examples/Example108_OrdinaryDiffEq1D/index.html +++ b/dev/module_examples/Example108_OrdinaryDiffEq1D/index.html @@ -96,4 +96,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example110_ReactionDiffusion1D_TwoSpecies/index.html b/dev/module_examples/Example110_ReactionDiffusion1D_TwoSpecies/index.html index 694b174a9..d66b99da4 100644 --- a/dev/module_examples/Example110_ReactionDiffusion1D_TwoSpecies/index.html +++ b/dev/module_examples/Example110_ReactionDiffusion1D_TwoSpecies/index.html @@ -82,4 +82,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example115_HeterogeneousCatalysis1D/index.html b/dev/module_examples/Example115_HeterogeneousCatalysis1D/index.html index a25682920..5e8a15c21 100644 --- a/dev/module_examples/Example115_HeterogeneousCatalysis1D/index.html +++ b/dev/module_examples/Example115_HeterogeneousCatalysis1D/index.html @@ -173,4 +173,4 @@ @test isapprox(main(; diffeq = true, unknown_storage = :dense, assembly = :cellwise), testvaldiffeq; rtol = 1.0e-12) return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example120_ThreeRegions1D/index.html b/dev/module_examples/Example120_ThreeRegions1D/index.html index fe5701ab2..caa69eb17 100644 --- a/dev/module_examples/Example120_ThreeRegions1D/index.html +++ b/dev/module_examples/Example120_ThreeRegions1D/index.html @@ -198,4 +198,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example121_PoissonPointCharge1D/index.html b/dev/module_examples/Example121_PoissonPointCharge1D/index.html index 4efb2718b..19bd08d1a 100644 --- a/dev/module_examples/Example121_PoissonPointCharge1D/index.html +++ b/dev/module_examples/Example121_PoissonPointCharge1D/index.html @@ -103,4 +103,4 @@ main(; assembly = :cellwise) ≈ testval return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example125_TestFunctions1D/index.html b/dev/module_examples/Example125_TestFunctions1D/index.html index 2ada7fb46..a1042fe5d 100644 --- a/dev/module_examples/Example125_TestFunctions1D/index.html +++ b/dev/module_examples/Example125_TestFunctions1D/index.html @@ -70,4 +70,4 @@ @test main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example150_Impedance1D/index.html b/dev/module_examples/Example150_Impedance1D/index.html index fd4c39205..554661701 100644 --- a/dev/module_examples/Example150_Impedance1D/index.html +++ b/dev/module_examples/Example150_Impedance1D/index.html @@ -99,4 +99,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example151_Impedance1D/index.html b/dev/module_examples/Example151_Impedance1D/index.html index 7c5007ddc..549eaedd7 100644 --- a/dev/module_examples/Example151_Impedance1D/index.html +++ b/dev/module_examples/Example151_Impedance1D/index.html @@ -125,4 +125,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example160_UnipolarDriftDiffusion1D/index.html b/dev/module_examples/Example160_UnipolarDriftDiffusion1D/index.html index 3885635f5..32fafaae9 100644 --- a/dev/module_examples/Example160_UnipolarDriftDiffusion1D/index.html +++ b/dev/module_examples/Example160_UnipolarDriftDiffusion1D/index.html @@ -280,4 +280,4 @@ ) return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example201_Laplace2D/index.html b/dev/module_examples/Example201_Laplace2D/index.html index f49280c9a..8f72411d4 100644 --- a/dev/module_examples/Example201_Laplace2D/index.html +++ b/dev/module_examples/Example201_Laplace2D/index.html @@ -45,4 +45,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example203_CoordinateSystems/index.html b/dev/module_examples/Example203_CoordinateSystems/index.html index ddfe3d1dd..063bd9515 100644 --- a/dev/module_examples/Example203_CoordinateSystems/index.html +++ b/dev/module_examples/Example203_CoordinateSystems/index.html @@ -246,4 +246,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example204_HagenPoiseuille/index.html b/dev/module_examples/Example204_HagenPoiseuille/index.html index a01a15aa5..ea745b609 100644 --- a/dev/module_examples/Example204_HagenPoiseuille/index.html +++ b/dev/module_examples/Example204_HagenPoiseuille/index.html @@ -70,4 +70,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example205_StagnationPoint/index.html b/dev/module_examples/Example205_StagnationPoint/index.html index 5a07b1b05..a92665275 100644 --- a/dev/module_examples/Example205_StagnationPoint/index.html +++ b/dev/module_examples/Example205_StagnationPoint/index.html @@ -91,4 +91,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example206_JouleHeat/index.html b/dev/module_examples/Example206_JouleHeat/index.html index 0be04f4d1..9fc14ffe7 100644 --- a/dev/module_examples/Example206_JouleHeat/index.html +++ b/dev/module_examples/Example206_JouleHeat/index.html @@ -110,4 +110,4 @@ main(; assembly = :cellwise) ≈ testval return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example207_NonlinearPoisson2D/index.html b/dev/module_examples/Example207_NonlinearPoisson2D/index.html index e4586cf3d..6e4fa8158 100644 --- a/dev/module_examples/Example207_NonlinearPoisson2D/index.html +++ b/dev/module_examples/Example207_NonlinearPoisson2D/index.html @@ -91,4 +91,4 @@ ) ≈ testval return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example210_NonlinearPoisson2D_Reaction/index.html b/dev/module_examples/Example210_NonlinearPoisson2D_Reaction/index.html index b0667b5be..11d322905 100644 --- a/dev/module_examples/Example210_NonlinearPoisson2D_Reaction/index.html +++ b/dev/module_examples/Example210_NonlinearPoisson2D_Reaction/index.html @@ -89,4 +89,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example215_NonlinearPoisson2D_BoundaryReaction/index.html b/dev/module_examples/Example215_NonlinearPoisson2D_BoundaryReaction/index.html index 88bfe8e97..5ca3d0f33 100644 --- a/dev/module_examples/Example215_NonlinearPoisson2D_BoundaryReaction/index.html +++ b/dev/module_examples/Example215_NonlinearPoisson2D_BoundaryReaction/index.html @@ -91,4 +91,4 @@ main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example220_NonlinearPoisson2D_BoundarySpecies/index.html b/dev/module_examples/Example220_NonlinearPoisson2D_BoundarySpecies/index.html index 0bd8d721b..782e73f52 100644 --- a/dev/module_examples/Example220_NonlinearPoisson2D_BoundarySpecies/index.html +++ b/dev/module_examples/Example220_NonlinearPoisson2D_BoundarySpecies/index.html @@ -94,4 +94,4 @@ main(; unknown_storage = :dense) ≈ 0.0020781361856598 return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example221_EquationBlockPrecon/index.html b/dev/module_examples/Example221_EquationBlockPrecon/index.html index c40275926..db5ef74ea 100644 --- a/dev/module_examples/Example221_EquationBlockPrecon/index.html +++ b/dev/module_examples/Example221_EquationBlockPrecon/index.html @@ -133,4 +133,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example225_TestFunctions2D/index.html b/dev/module_examples/Example225_TestFunctions2D/index.html index 05d114570..37d929314 100644 --- a/dev/module_examples/Example225_TestFunctions2D/index.html +++ b/dev/module_examples/Example225_TestFunctions2D/index.html @@ -179,4 +179,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example226_BoundaryIntegral/index.html b/dev/module_examples/Example226_BoundaryIntegral/index.html index df2005060..3ad565daf 100644 --- a/dev/module_examples/Example226_BoundaryIntegral/index.html +++ b/dev/module_examples/Example226_BoundaryIntegral/index.html @@ -79,4 +79,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example230_BoundaryFlux/index.html b/dev/module_examples/Example230_BoundaryFlux/index.html index cf7bda393..15e895044 100644 --- a/dev/module_examples/Example230_BoundaryFlux/index.html +++ b/dev/module_examples/Example230_BoundaryFlux/index.html @@ -172,4 +172,4 @@ return nothing end -end # module

This page was generated using Literate.jl.

+end # module

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example240_FiniteElementVelocities/index.html b/dev/module_examples/Example240_FiniteElementVelocities/index.html index 0d9a977e5..855342736 100644 --- a/dev/module_examples/Example240_FiniteElementVelocities/index.html +++ b/dev/module_examples/Example240_FiniteElementVelocities/index.html @@ -265,4 +265,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example301_Laplace3D/index.html b/dev/module_examples/Example301_Laplace3D/index.html index 1de175d27..fe73a3e75 100644 --- a/dev/module_examples/Example301_Laplace3D/index.html +++ b/dev/module_examples/Example301_Laplace3D/index.html @@ -42,4 +42,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example311_HeatEquation_BoundaryDiffusion/index.html b/dev/module_examples/Example311_HeatEquation_BoundaryDiffusion/index.html index d2a35fba5..06eb08071 100644 --- a/dev/module_examples/Example311_HeatEquation_BoundaryDiffusion/index.html +++ b/dev/module_examples/Example311_HeatEquation_BoundaryDiffusion/index.html @@ -112,4 +112,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example405_GenericOperator/index.html b/dev/module_examples/Example405_GenericOperator/index.html index f190dad1d..f815468c1 100644 --- a/dev/module_examples/Example405_GenericOperator/index.html +++ b/dev/module_examples/Example405_GenericOperator/index.html @@ -74,4 +74,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example406_WeirdReaction/index.html b/dev/module_examples/Example406_WeirdReaction/index.html index 94cc84172..02f362a78 100644 --- a/dev/module_examples/Example406_WeirdReaction/index.html +++ b/dev/module_examples/Example406_WeirdReaction/index.html @@ -171,4 +171,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example410_ManySpecies/index.html b/dev/module_examples/Example410_ManySpecies/index.html index 7df8688ca..a99279aa3 100644 --- a/dev/module_examples/Example410_ManySpecies/index.html +++ b/dev/module_examples/Example410_ManySpecies/index.html @@ -37,4 +37,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example420_DiscontinuousQuantities/index.html b/dev/module_examples/Example420_DiscontinuousQuantities/index.html index 39defa37a..f094394ec 100644 --- a/dev/module_examples/Example420_DiscontinuousQuantities/index.html +++ b/dev/module_examples/Example420_DiscontinuousQuantities/index.html @@ -127,4 +127,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example421_AbstractQuantities_TestFunctions/index.html b/dev/module_examples/Example421_AbstractQuantities_TestFunctions/index.html index 29ad311b1..971a76b44 100644 --- a/dev/module_examples/Example421_AbstractQuantities_TestFunctions/index.html +++ b/dev/module_examples/Example421_AbstractQuantities_TestFunctions/index.html @@ -166,4 +166,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example422_InterfaceQuantities/index.html b/dev/module_examples/Example422_InterfaceQuantities/index.html index 6d996bf15..2715a6ebc 100644 --- a/dev/module_examples/Example422_InterfaceQuantities/index.html +++ b/dev/module_examples/Example422_InterfaceQuantities/index.html @@ -215,4 +215,4 @@ return nothing end -end # module

This page was generated using Literate.jl.

+end # module

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example424_AbstractQuantitiesInit/index.html b/dev/module_examples/Example424_AbstractQuantitiesInit/index.html index 1465a1424..8a482c3a5 100644 --- a/dev/module_examples/Example424_AbstractQuantitiesInit/index.html +++ b/dev/module_examples/Example424_AbstractQuantitiesInit/index.html @@ -70,4 +70,4 @@ main(; unknown_storage = :dense, assembly = :cellwise) end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example430_ParameterDerivativesStationary/index.html b/dev/module_examples/Example430_ParameterDerivativesStationary/index.html index 4cbeef26a..35ba16a2f 100644 --- a/dev/module_examples/Example430_ParameterDerivativesStationary/index.html +++ b/dev/module_examples/Example430_ParameterDerivativesStationary/index.html @@ -236,4 +236,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example440_ParallelState/index.html b/dev/module_examples/Example440_ParallelState/index.html index 58525d742..95163f48d 100644 --- a/dev/module_examples/Example440_ParallelState/index.html +++ b/dev/module_examples/Example440_ParallelState/index.html @@ -51,4 +51,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/module_examples/Example510_Mixture/index.html b/dev/module_examples/Example510_Mixture/index.html index b4d770a49..e151a5cf0 100644 --- a/dev/module_examples/Example510_Mixture/index.html +++ b/dev/module_examples/Example510_Mixture/index.html @@ -211,4 +211,4 @@ return nothing end -end

This page was generated using Literate.jl.

+end

This page was generated using Literate.jl.

diff --git a/dev/notebooks/index.html b/dev/notebooks/index.html index ebfa7246d..6040e6734 100644 --- a/dev/notebooks/index.html +++ b/dev/notebooks/index.html @@ -1,2 +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.

+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/dev/physics/index.html b/dev/physics/index.html index 089782d63..3c8a401c3 100644 --- a/dev/physics/index.html +++ b/dev/physics/index.html @@ -30,4 +30,4 @@

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.

While x/expm1(x) appears to be sufficiently accurate for all x!=0, it's derivative calculated via ForwardDiff.jl is not, so we use the polynomial approximation in the interval (-bernoulli_small_threshold, bernoulli_small_threshold).

Also, see the discussion in #117.

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.

For the general approach to the implementation, see the discussion in fbernoulli.

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.

source
+

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.

source diff --git a/dev/plutostatichtml_examples/api-update/index.html b/dev/plutostatichtml_examples/api-update/index.html index 8233a6701..0b7426cba 100644 --- a/dev/plutostatichtml_examples/api-update/index.html +++ b/dev/plutostatichtml_examples/api-update/index.html @@ -373,7 +373,7 @@

v0.14

time: \(\;\) method: \(\;\) t: \(\;\) method: \(\;\) t: method:

t= method:

t=

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
+

Number of quantities defined for system

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/dev/runexamples/index.html b/dev/runexamples/index.html index b2c16b2d3..964893545 100644 --- a/dev/runexamples/index.html +++ b/dev/runexamples/index.html @@ -15,4 +15,4 @@ dostuff(u)=a*u @allocated map!(dostuff,v,u) end -ttype_annotated(10)
0
+ttype_annotated(10)
0
diff --git a/dev/search_index.js b/dev/search_index.js index b85b8a349..eac2a11c6 100644 --- a/dev/search_index.js +++ b/dev/search_index.js @@ -1,3 +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(;\n dim = 1, nref = 0, Plotter = nothing, plot_grid = false, verbose = false,\n unknown_storage = :sparse, assembly = :edgewise, strategy = nothing\n )\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 = simplexgrid(X)\n Γ_in = 1\n Γ_out = 2\n elseif dim == 2\n grid = simplexgrid(X, Y)\n Γ_in = 4\n Γ_out = 2\n else\n grid = simplexgrid(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, data)\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 return nothing\n end\n\n function source(f, node, data)\n if node.region == 1\n f[1] = 1.0e-4 * (3.0 - node[1])\n end\n return nothing\n end\n\n flux = function (f, u, edge, data)\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 return nothing\n end\n\n storage = function (f, u, node, data)\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 return nothing\n end\n\n sys = VoronoiFVM.System(\n grid; flux, reaction, storage, source,\n unknown_storage, assembly, is_linear = true\n )\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)\n U = solve(sys; control)\n @info num_dof(U)\n\n p = GridVisualizer(;\n Plotter = Plotter, layout = (3, 1),\n limits = (0, 1.0e-3),\n xlimits = (0, 3)\n )\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 = :darkred)\n scalarplot!(p[2, 1], subgrid2, U2; title = \"spec2\", color = :green)\n scalarplot!(p[3, 1], subgrid3, U3; title = \"spec3\", color = :navyblue)\n reveal(p)\n return U\nend\n\nfunction runtests()\n strategy = BICGstabIteration(AMGCL_AMGPreconditioner())\n @test sum(main(; dim = 1, strategy, unknown_storage = :dense)[2, :]) ≈ 0.014101758266210086\n @test sum(main(; dim = 1, strategy, unknown_storage = :sparse)[2, :]) ≈ 0.014101758266210086\n @test sum(main(; dim = 2, strategy, unknown_storage = :dense)[2, :]) ≈ 0.12691582439590407\n @test sum(main(; dim = 2, strategy, unknown_storage = :sparse)[2, :]) ≈ 0.12691582439590407\n @test sum(main(; dim = 3, strategy, unknown_storage = :dense)[2, :]) ≈ 1.1422561017685693\n @test sum(main(; dim = 3, strategy, unknown_storage = :sparse)[2, :]) ≈ 1.1422561017685693\n return nothing\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 called 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 neighboring 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: :sparse, :tridiagonal, :banded, :auto. Default: :sparse. :auto leads to automatic choice for dense solution storage depending on space dimension and number of species.\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). Uses a lock to ensure parallel access.\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/#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. 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\ndata\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.data","page":"System","title":"VoronoiFVM.data","text":"data(system)\n\n\nRetrieve user data record.\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)} <: VoronoiFVM.AbstractSystem{Tv, Tc, Ti, Tm}\n\nStructure holding data for finite volume system.\n\nSubtype of AbstractSystem.\n\nType parameters:\n\nTSpecMat: Type of matrix storing species information (Matrix or SparseMatrixCSC)\n\nFor the other type parameters, see AbstractSystem.\n\ngrid::ExtendableGrid: Grid\n\nphysics::VoronoiFVM.Physics: Physics data\n\nboundary_values::Matrix: Array of boundary condition values\n\nboundary_factors::Matrix: Array of boundary condition factors\n\nregion_species::AbstractMatrix: Matrix containing species numbers for inner regions\n\nbregion_species::AbstractMatrix: Matrix containing species numbers for boundary regions\n\nnode_dof::AbstractMatrix: Matrix containing degree of freedom numbers for each node\n\nmatrixtype::Symbol: - :multidiagonal (currently disabled)\n:sparse\n:banded\n:tridiagonal\n\nspecies_homogeneous::Bool: Flag which says if the number of unknowns per node is constant\n\nnum_quantities::Any: Number of quantities defined on system\n\nnum_parameters::Any: Number of parameter the system depends on.\n\nassembly_data::Union{Nothing, VoronoiFVM.AbstractAssemblyData{Tc, Ti}} where {Tc, Ti}: Precomputed form factors for bulk assembly\n\nboundary_assembly_data::VoronoiFVM.AbstractAssemblyData: Precomputed form factors for boundary assembly\n\nassembly_type::Symbol: :edgewise or :cellwise\n\nis_linear::Bool: Is the system linear ?\n\noutflownoderegions::Union{Nothing, SparseArrays.SparseMatrixCSC{Bool, Int64}}: Outflow nodes with their region numbers.\n\ngeneric_matrix::SparseArrays.SparseMatrixCSC: Sparse matrix for generic operator handling\n\ngeneric_matrix_colors::Vector: Sparse matrix colors for generic operator handling\n\nis_complete::Bool: Has the system been completed (species information compiled)?\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{Tv}, ispec, ibc, v) where {Tv}\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!-Union{Tuple{Tv}, Tuple{VoronoiFVM.AbstractSystem{Tv}, Any, Any, Any}} where Tv","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 n = 20, m = 2.0, Plotter = nothing, verbose = false,\n unknown_storage = :sparse, tend = 0.01, tstep = 0.0001, assembly = :edgewise\n )\n\n # Create a one-dimensional discretization\n h = 1.0 / convert(Float64, n / 2)\n X = collect(-1:h:1)\n grid = simplexgrid(X)\n\n # Flux function which describes the flux\n # between neighboring control volumes\n function flux!(f, u, edge, data)\n f[1] = u[1, 1] - u[1, 2]\n return nothing\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, data)\n f[1] = (ϵ + u[1])^(1.0 / m)\n return nothing\n end\n\n # Create a physics structure\n physics = VoronoiFVM.Physics(;\n flux = flux!,\n storage = storage!\n )\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 in 1:length(tsol)\n time = tsol.t[i]\n scalarplot!(\n p[1, 1], grid, tsol[1, :, i]; title = @sprintf(\"t=%.3g\", time),\n color = :red, label = \"numerical\"\n )\n scalarplot!(\n p[1, 1], grid, map(x -> barenblatt(x, time, m)^m, grid); clear = false,\n color = :green, label = \"exact\"\n )\n reveal(p)\n sleep(1.0e-2)\n end\n end\n return sum(tsol.u[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\n\n return nothing\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":"module_examples/Example440_ParallelState/#440:-Parallel-solves","page":"440: Parallel solves","title":"440: Parallel solves","text":"","category":"section"},{"location":"module_examples/Example440_ParallelState/","page":"440: Parallel solves","title":"440: Parallel solves","text":"(source code)","category":"page"},{"location":"module_examples/Example440_ParallelState/","page":"440: Parallel solves","title":"440: Parallel solves","text":"Demonstrate how to solve one system with different data in parallel using the SystemState (new in v2.0).","category":"page"},{"location":"module_examples/Example440_ParallelState/","page":"440: Parallel solves","title":"440: Parallel solves","text":"module Example440_ParallelState\n\n\nusing VoronoiFVM, ExtendableGrids\nusing GridVisualize\nusing ChunkSplitters\n\nfunction flux(y, u, node, data)\n y[1] = u[1, 1]^2 - u[1, 2]^2\n return nothing\nend\n\nfunction bcondition(y, u, node, data)\n boundary_dirichlet!(y, u, node, species = 1, region = 1, value = 0.1)\n boundary_neumann!(y, u, node, species = 1, region = 2, value = data.influx)\n return nothing\nend\n\nfunction main(; nref = 5, Plotter = nothing)\n grid = simplexgrid(range(0, 1, length = 10 * 2^nref + 1))\n sys = VoronoiFVM.System(grid; flux, bcondition, species = [1], data = (influx = 0.0,))\n\n # Initial state. First solution creates the matrix\n state0 = VoronoiFVM.SystemState(sys)\n sol = solve!(state0; inival = 0.1)\n\n # Prepare parameter and result data\n influxes = range(0.0, 10.0, length = 100)\n masses = similar(influxes)\n\n # Split the index range in as many chunks as threads\n Threads.@threads for indexes in chunks(1:length(influxes); n = Threads.nthreads())\n # Create a new state sharing the system - one for each chunk\n state = similar(state0)\n # Solve for all data values in chunk\n for iflux in indexes\n data = (influx = influxes[iflux],)\n sol = solve!(state; data, inival = 0.1, verbose = \"\")\n masses[iflux] = integrate(sys, sol)[1, 1]\n end\n end\n scalarplot(influxes, masses; Plotter, xlabel = \"influx\", ylabel = \"mass\")\n return sum(masses)\nend\n\nusing Test\nfunction runtests()\n testval = 140.79872772042577\n @test main() ≈ testval\n return nothing\nend\n\nend","category":"page"},{"location":"module_examples/Example440_ParallelState/","page":"440: Parallel solves","title":"440: Parallel solves","text":"","category":"page"},{"location":"module_examples/Example440_ParallelState/","page":"440: Parallel solves","title":"440: Parallel solves","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/#ForwardDiff.value","page":"Miscellaneous","title":"ForwardDiff.value","text":"value(x)\n\nReturn the value of a dual number (for debugging in callback functions). Re-exported from ForwardDiff.\n\n\n\n\n\n","category":"function"},{"location":"misc/#Form-factor-calculatione","page":"Miscellaneous","title":"Form factor calculatione","text":"","category":"section"},{"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\ncompat: Compat\nDeprecated as of v1.20\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.coordinates-Tuple{ExtendableGrid}","page":"Miscellaneous","title":"VoronoiFVM.coordinates","text":"coordinates(grid::ExtendableGrid)\n\nReturn coordinate array of grid. \n\ncompat: Compat\nDeprecated as of v1.20\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 = simplexgrid(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(;\n flux = function (f, u, edge, data)\n f[1] = eps * (u[1, 1] - u[1, 2])\n return nothing\n end,\n bflux = function (f, u, edge, data)\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 return nothing\n end,\n breaction = function (f, u, node, data)\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 return nothing\n end,\n bsource = function (f, bnode, data)\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 return nothing\n end, bstorage = function (f, u, node, data)\n if node.region == breg\n f[2] = u[2]\n end\n return nothing\n end, storage = function (f, u, node, data)\n f[1] = u[1]\n return nothing\n end\n )\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 return nothing\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.keepcurrent_linear = false\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 return 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)\n return nothing\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 n = 20, m = 2, Plotter = nothing, verbose = false,\n unknown_storage = :sparse, tend = 0.01, tstep = 0.0001, assembly = :edgewise\n )\n\n # Create a one-dimensional discretization\n h = 1.0 / convert(Float64, n / 2)\n X = collect(-1:h:1)\n grid = simplexgrid(X)\n\n # Flux function which describes the flux\n # between neighboring control volumes\n function flux!(f, u, edge, data)\n f[1] = u[1, 1]^m - u[1, 2]^m\n return nothing\n end\n\n # Storage term\n function storage!(f, u, node, data)\n f[1] = u[1]\n return nothing\n end\n\n # Create a physics structure\n physics = VoronoiFVM.Physics(;\n flux = flux!,\n storage = storage!\n )\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 in 1:length(tsol)\n time = tsol.t[i]\n scalarplot!(\n p[1, 1], grid, tsol[1, :, i]; title = @sprintf(\"t=%.3g\", time),\n color = :red, label = \"numerical\",\n markershape = :circle, markevery = 1\n )\n scalarplot!(\n p[1, 1], grid, map(x -> barenblatt(x, time, m), grid); clear = false,\n color = :green,\n label = \"exact\", markershape = :none\n )\n reveal(p)\n sleep(1.0e-2)\n end\n return sum(tsol.u[end])\nend\n\nusing Test\nfunction runtests()\n testval = 46.66666666647518\n return @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\n nothing\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: geomspace, simplexgrid\nusing GridVisualize\nusing OrdinaryDiffEqSDIRK\n\nfunction main(;\n nref = 0, Plotter = nothing, verbose = false,\n unknown_storage = :sparse, assembly = :edgewise,\n time_embedding = :none,\n L = 1.0, R = 1.0, D = 1.0, C = 1.0,\n ω0 = 1.0e-3, ω1 = 5.0e1\n )","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 = 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 = simplexgrid(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 return nothing\n end\n\n storage = function (f, u, node, data)\n f[1] = data.C * u[1]\n return nothing\n end\n\n reaction = function (f, u, node, data)\n f[1] = data.R * u[1]\n return nothing\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 return nothing\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(\n 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\n )","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 tend = 1.0\n if time_embedding == :builtin\n tsol = solve(sys; inival = 0.0, params = [1.0], times = (0.0, tend), force_first_step = true)\n steadystate = tsol.u[end]\n elseif time_embedding == :ordinarydiffeq\n inival = unknowns(sys, inival = 0)\n problem = ODEProblem(sys, inival, (0, tend); params = [1.0])\n odesol = solve(problem, ImplicitEuler())\n tsol = reshape(odesol, sys)\n steadystate = tsol.u[end]\n elseif time_embedding == :none\n steadystate = solve(sys; inival = 0.0, params = [1.0])\n else\n error(\"time_embedding must be one of :builtin, :ordinarydiffeq, :none\")\n end\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 return 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 return 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 vis = GridVisualizer(; Plotter = Plotter)\n scalarplot!(\n vis, real(allIxL), imag(allIxL); label = \"exact\", color = :red,\n linestyle = :dot\n )\n scalarplot!(\n vis, real(allIL), imag(allIL); label = \"calc\", show = true, clear = false,\n color = :blue, linestyle = :solid\n )\n\n return sum(allIL)\nend\n\nusing Test\nfunction runtests()\n testval = 57.92710286186797 + 23.163945443946027im\n for unknown_storage in (:sparse, :dense)\n for assembly in (:edgewise, :cellwise)\n for time_embedding in (:none, :builtin, :ordinarydiffeq)\n @test main(; unknown_storage, assembly, time_embedding) ≈ testval\n end\n end\n end\n return nothing\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/Example003_Solvers/#003:-New-linear-solver-API","page":"003: New linear solver API","title":"003: New linear solver API","text":"","category":"section"},{"location":"module_examples/Example003_Solvers/","page":"003: New linear solver API","title":"003: New linear solver API","text":"(source code)","category":"page"},{"location":"module_examples/Example003_Solvers/","page":"003: New linear solver API","title":"003: New linear solver API","text":"module Example003_Solvers\n\n# under development\n\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\nusing LinearSolve\nusing ExtendableSparse\nusing ExtendableSparse: ILUZeroPreconBuilder, JacobiPreconBuilder, SmoothedAggregationPreconBuilder\nusing SparseArrays\nusing AMGCLWrap\nusing AlgebraicMultigrid\nusing LinearAlgebra\n\n\nusing Test\n\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 = simplexgrid(X, Y)\n nn = num_nodes(grid)\n\n eps = 1.0e-2\n\n function reaction(f, u, node, data)\n f[1] = u[1]^2\n return nothing\n end\n\n function flux(f, u, edge, data)\n f[1] = eps * (u[1, 1]^2 - u[1, 2]^2)\n return nothing\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.0 * (x1^2 + x2^2))\n return nothing\n end\n\n function storage(f, u, node, data)\n f[1] = u[1]\n return nothing\n end\n\n function bcondition(f, u, node, data)\n boundary_dirichlet!(\n f,\n u,\n node;\n species = 1,\n region = 2,\n value = ramp(node.time; dt = (0, 0.1), du = (0, 1))\n )\n boundary_dirichlet!(\n f,\n u,\n node;\n species = 1,\n region = 4,\n value = ramp(node.time; dt = (0, 0.1), du = (0, 1))\n )\n return nothing\n end\n\n sys = VoronoiFVM.System(\n grid; reaction, flux, source, storage, bcondition, assembly,\n species = [1]\n )\n @info \"UMFPACK:\"\n umf_sol = solve(sys; inival = 0.5, method_linear = LinearSolve.UMFPACKFactorization(), kwargs...)\n\n @info \"KLU:\"\n sol = solve(sys; inival = 0.5, method_linear = LinearSolve.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 = LinearSolve.SparspakFactorization(), kwargs...)\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n\n @info \"Krylov-ilu0:\"\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(precs = ILUZeroPreconBuilder()),\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n\n @info \"Krylov-block1\"\n precs = BlockPreconBuilder(; precs = ILUZeroPreconBuilder(), partitioning = A -> [1:(size(A, 1) ÷ 2), (size(A, 1) ÷ 2 + 1):size(A, 1)])\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(; precs),\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n\n @info \"Krylov-block2\"\n precs = BlockPreconBuilder(; precs = ILUZeroPreconBuilder(), partitioning = A -> [1:2:size(A, 1), 2:2:size(A, 1)])\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(; precs),\n log = true,\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n\n @info \"Krylov - delayed factorization:\"\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(; precs = LinearSolvePreconBuilder(SparspakFactorization())),\n keepcurrent_linear = false,\n log = true,\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n @test summary(history(sol)).factorizations == 1\n\n @info \"Krylov - jacobi:\"\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(; precs = JacobiPreconBuilder()),\n keepcurrent_linear = true, log = true,\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n @test summary(history(sol)).factorizations > 1\n\n @info \"Krylov - SA_AMG:\"\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(; precs = SmoothedAggregationPreconBuilder()),\n keepcurrent_linear = true,\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n\n @info \"Krylov - AMGCL_AMG:\"\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(; precs = AMGPreconBuilder()),\n keepcurrent_linear = true,\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n\n @info \"Krylov - AMGnCL_RLX:\"\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(; precs = RLXPreconBuilder()),\n keepcurrent_linear = true,\n kwargs...\n )\n return @test norm(sol - umf_sol, Inf) < 1.0e-7\nend\n\nfunction runtests()\n @testset \"edgewise\" begin\n main(; assembly = :edgewise)\n end\n @testset \"cellwise\" begin\n main(; assembly = :cellwise)\n end\n return nothing\nend\nend","category":"page"},{"location":"module_examples/Example003_Solvers/","page":"003: New linear solver API","title":"003: New linear solver API","text":"","category":"page"},{"location":"module_examples/Example003_Solvers/","page":"003: New linear solver API","title":"003: New linear solver API","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\nimport Metis\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 = simplexgrid(X, Y)\n\n grid = partition(grid, PlainMetisPartitioning(npart = 10))\n @show grid\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 return nothing\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 return nothing\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 return nothing\n end\n\n function storage!(f, u, node, data)\n f[1] = u[1]\n f[2] = u[2]\n return nothing\n end\n\n physics = VoronoiFVM.Physics(;\n data = data,\n flux = flux!,\n storage = storage!,\n reaction = reaction!,\n source = source!\n )\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 testval = 0\n p = GridVisualizer(; Plotter = Plotter, layout = (2, 1))\n @time while time < 1\n time = time + tstep\n U = solve(sys; inival, control, tstep)\n inival .= U\n testval = sum(U)\n tstep *= 1.0\n istep = istep + 1\n scalarplot!(p[1, 1], grid, U[1, :]; clear = true, limits = (0, 0.5))\n scalarplot!(p[2, 1], grid, U[2, :]; show = true, limits = (0, 0.5))\n end\n return testval\nend\n\nusing Test\nfunction runtests()\n testval = 16.01812472041518\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\n return nothing\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, data::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, data::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 nofunc\n\n\n\n\n\n","category":"type"},{"location":"internal/#VoronoiFVM.ResEvaluator-Union{Tuple{Tv}, Tuple{Any, 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 nofunc\n\n\n\n\n\n","category":"type"},{"location":"internal/#VoronoiFVM.ResJacEvaluator-Union{Tuple{Tv}, Tuple{Any, Any, Symbol, Vector{Tv}, Any, Int64}} where Tv","page":"Internal API","title":"VoronoiFVM.ResJacEvaluator","text":"ResJacEvaluator(physics, data, 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/#Manipulating-systems","page":"Internal API","title":"Manipulating systems","text":"","category":"section"},{"location":"internal/","page":"Internal API","title":"Internal API","text":"VoronoiFVM.update_grid_cellwise!\nVoronoiFVM.update_grid_edgewise!\nVoronoiFVM.sysmutatelock\nVoronoiFVM._complete!","category":"page"},{"location":"internal/#VoronoiFVM.update_grid_cellwise!","page":"Internal API","title":"VoronoiFVM.update_grid_cellwise!","text":"update_grid_cellwise!(system)\n\nUpdate cellwise assembly data for new grid\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.update_grid_edgewise!","page":"Internal API","title":"VoronoiFVM.update_grid_edgewise!","text":"update_grid_edgewise!(system)\n\nUpdate edgewise assembly data for new grid\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.sysmutatelock","page":"Internal API","title":"VoronoiFVM.sysmutatelock","text":"const sysmutatelock\n\nReentrant lock to safeguard mutating methods _complete! and update_grid!.\n\n\n\n\n\n","category":"constant"},{"location":"internal/#VoronoiFVM._complete!","page":"Internal API","title":"VoronoiFVM._complete!","text":"_complete!(system)\n\nUpdate grid and compile species information for system. Uses a lock to ensure parallel access.\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\npcolor_partitions::Vector\npartition_cells::Vector\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\npcolor_partitions::Vector\npartition_nodes::Vector\npartition_edges::Vector\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\nVoronoiFVM.increase_num_species!\nVoronoiFVM.addzrows\nVoronoiFVM.dofs","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/#VoronoiFVM.increase_num_species!","page":"Internal API","title":"VoronoiFVM.increase_num_species!","text":" increase_num_species!(system,maxspec)\n\nIncrease number of species in system to maxspec by adding new rows to all relevant matrices.\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.addzrows","page":"Internal API","title":"VoronoiFVM.addzrows","text":"addzrows(matrix,maxrow)\n\nReturn matrix with number of rows increased to maxrow, and set the new elements to zero.\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.dofs","page":"Internal API","title":"VoronoiFVM.dofs","text":"dofs(a)\n\n\nVector of degrees of freedom in solution array.\n\n\n\n\n\ndofs(a)\n\n\nVector of degrees of freedom in sparse solution array.\n\n\n\n\n\n","category":"function"},{"location":"internal/#Geometry-data","page":"Internal API","title":"Geometry data","text":"","category":"section"},{"location":"internal/","page":"Internal API","title":"Internal API","text":"VoronoiFVM.AbstractGeometryItem\nVoronoiFVM.AbstractNode\nVoronoiFVM.AbstractNodeData\nVoronoiFVM.AbstractEdge\nVoronoiFVM.AbstractEdgeData\nVoronoiFVM.outflownode!\nVoronoiFVM.NodeUnknowns\nVoronoiFVM.NodeRHS","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} <: 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} <: 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/#VoronoiFVM.outflownode!","page":"Internal API","title":"VoronoiFVM.outflownode!","text":"outflownode!(edge)\n\nSet edge.outflownode entry.\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.NodeUnknowns","page":"Internal API","title":"VoronoiFVM.NodeUnknowns","text":"struct NodeUnknowns{Tv, Tc, Tp, Ti} <: VoronoiFVM.AbstractNodeData{Tv}\n\nUnknown data on node. \n\n\n\n\n\n","category":"type"},{"location":"internal/#VoronoiFVM.NodeRHS","page":"Internal API","title":"VoronoiFVM.NodeRHS","text":"struct NodeRHS{Tv, Tc, Tp, Ti} <: VoronoiFVM.AbstractNodeData{Tv}\n\nRHS data on 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.factorizationstrategy\nVoronoiFVM.solve_step!\nVoronoiFVM.solve_transient!\nVoronoiFVM.eval_and_assemble\nVoronoiFVM._eval_and_assemble_generic_operator\nVoronoiFVM._addnz\nVoronoiFVM._add","category":"page"},{"location":"internal/#VoronoiFVM.factorizationstrategy","page":"Internal API","title":"VoronoiFVM.factorizationstrategy","text":"factorizationstrategy(preconditioner, blockstratrgy, system)\n\nCreate a factorizations strategy from preconditioner and block information\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.solve_step!","page":"Internal API","title":"VoronoiFVM.solve_step!","text":"solve_step!(\n state,\n solution,\n oldsol,\n control,\n time,\n tstep,\n embedparam,\n params\n)\n\n\nSolve time step problem. This is the core routine for implicit Euler and stationary solve.\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.solve_transient!","page":"Internal API","title":"VoronoiFVM.solve_transient!","text":" solve_transient(inival, system, times; kwargs...)\n\nSolve transient or embedding problem.\n\n\n\n\n\n","category":"function"},{"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 matrix,\n dudp,\n time,\n tstep,\n λ,\n data,\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._eval_and_assemble_generic_operator","page":"Internal API","title":"VoronoiFVM._eval_and_assemble_generic_operator","text":"Evaluate and assemble jacobian for generic operator part.\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_addnz(matrix, i, j, v, fac, part)\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::VoronoiFVM.DenseSolutionArray, idof, val) -> Any\n\n\nAdd residual value into global degree of freedom\n\n(Internal method)\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(internal)\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!(state, 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, state, t)\n\n\nInterpret the discrete problem as an ODE/DAE problem. Provide the rhs function for SciMLBase.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, state, t)\n\n\nInterpret the discrete problem as an ODE/DAE problem. Provide the jacobi matrix calculation function for SciMLBase.ODEFunction\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.mass_matrix","page":"Internal API","title":"VoronoiFVM.mass_matrix","text":"mass_matrix(state)\n\n\nCalculate the mass matrix for use with SciMLBase.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!(state, 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":"internal/#Misc-tools","page":"Internal API","title":"Misc tools","text":"","category":"section"},{"location":"internal/","page":"Internal API","title":"Internal API","text":"VoronoiFVM.solutionarray\nVoronoiFVM.integrate(::Type{<:Cartesian2D}, coordl, coordr, hnormal, velofunc; kwargs...)\nVoronoiFVM.integrate(::Type{<:Cylindrical2D}, coordl, coordr, hnormal, velofunc; kwargs...)\nVoronoiFVM.doolittle_ludecomp!\nVoronoiFVM.doolittle_lusolve!\nVoronoiFVM.bernoulli_horner\nVoronoiFVM._print_error","category":"page"},{"location":"internal/#VoronoiFVM.solutionarray","page":"Internal API","title":"VoronoiFVM.solutionarray","text":"solutionarray(a::Matrix)\n\n\n\n\n\nsolutionarray(a::SparseMatrixCSC)\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.integrate-Tuple{Type{<:Cartesian2D}, Vararg{Any, 4}}","page":"Internal API","title":"VoronoiFVM.integrate","text":"integrate(, coordl, coordr, hnormal, velofunc; kwargs...)\n\n\nThis is an internal function to integrate velofunc along the edge sigma=overlinemathttcoordlmathttcoordr between the x_K and x_L where mathtthnormal=x_K-x_L using Simpson's Rule. To be precise, compute for a cartesian coordinate system: int_sigma mathbfv cdot mathbfn mathrmds lvert x_K - x_L rvert lvertsigmarvert.\n\n\n\n\n\n","category":"method"},{"location":"internal/#VoronoiFVM.integrate-Tuple{Type{<:Cylindrical2D}, Vararg{Any, 4}}","page":"Internal API","title":"VoronoiFVM.integrate","text":"integrate(, coordl, coordr, hnormal, velofunc; kwargs...)\n\n\nThis is an internal function similar to integrate(::Type{<:Cartesian2D},...), but computes instead int_sigma r mathbfv cdot mathbfn mathrmds lvert x_K - x_L rvert left ( lvertsigmarvert r(mathrmmid(sigma)) right ) where r(mathrmmid(sigma)) is the r-coordinate of the mid-point of sigma.\n\n\n\n\n\n","category":"method"},{"location":"internal/#VoronoiFVM.doolittle_ludecomp!","page":"Internal API","title":"VoronoiFVM.doolittle_ludecomp!","text":"doolittle_ludecomp!(LU)\n\n\nNon-pivoting inplace LU factorization using Doolittle's method. Adapted from https://en.wikipedia.org/wiki/LUdecomposition#MATLABcode_example.\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.doolittle_lusolve!","page":"Internal API","title":"VoronoiFVM.doolittle_lusolve!","text":"doolittle_lusolve!(LU, b)\n\n\nNon-pivoting inplace upper and lower triangular solve of matrix factorized with doolittle_ludecomp!. Adapted from https://en.wikipedia.org/wiki/LUdecomposition#MATLABcode_example.\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.bernoulli_horner","page":"Internal API","title":"VoronoiFVM.bernoulli_horner","text":"bernoulli_horner(x)\n\n\nCalculation of Bernoulli function via Horner scheme based on Taylor coefficients around 0.\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM._print_error","page":"Internal API","title":"VoronoiFVM._print_error","text":"Print error when catching exceptions\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 = simplexgrid(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, data)\n f[1] = ϵ * (u[1, 1] - u[1, 2])\n return nothing\n end\n\n # Source term\n function source!(f, node, data)\n if node[1] <= 0.5\n f[1] = 1\n else\n f[1] = -1\n end\n return nothing\n end\n\n # Reaction term\n function reaction!(f, u, node, data)\n f[1] = exp(u[1]) - exp(-u[1])\n return nothing\n end\n\n # Create a physics structure\n physics = VoronoiFVM.Physics(;\n flux = flux!,\n source = source!,\n reaction = reaction!\n )\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\n return nothing\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\nusing OrdinaryDiffEqRosenbrock\nusing SciMLBase: NoInit\n\nfunction reaction(f, u, node, data)\n k = data.k\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 return nothing\nend\n\nfunction source(f, node, data)\n if node.region == 1\n f[1] = 1.0e-4 * (3.0 - node[1])\n end\n return nothing\nend\n\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.\nfunction correctionflux(f, u, edge, data)\n eps = data.eps\n for i in 1:3\n f[i] = eps[i] * (u[i, 1] - u[i, 2])\n end\n return nothing\nend\n\nfunction correctionstorage(f, u, node, data)\n f .= u\n return nothing\nend\n\n# This is the \"old\" way:\n# Write into result only where\n# the corresponding species has been enabled\nfunction pickyflux(f, u, edge, data)\n eps = data.eps\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 return nothing\nend\n\nfunction pickystorage(f, u, node, data)\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 return nothing\nend\n\n\nfunction main(;\n n = 30, Plotter = nothing, plot_grid = false, verbose = false,\n unknown_storage = :sparse, tend = 10,\n diffeq = false,\n rely_on_corrections = false, assembly = :edgewise\n )\n\n X = range(0, 3, length = n)\n grid = simplexgrid(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 data = (eps = [1, 1, 1], k = [1, 1, 1])\n\n flux = rely_on_corrections ? correctionflux : pickyflux\n storage = rely_on_corrections ? correctionstorage : pickystorage\n\n sys = VoronoiFVM.System(\n grid; data,\n flux, reaction, storage, source,\n unknown_storage, assembly\n )\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 function plot_timestep(U, time)\n U1 = view(U[1, :], subgrid1)\n U2 = view(U[2, :], subgrid2)\n U3 = view(U[3, :], subgrid3)\n\n scalarplot!(\n p[1, 1], subgrid1, U1; label = \"spec1\", color = :darkred,\n xlimits = (0, 3), flimits = (0, 1.0e-3),\n title = @sprintf(\"three regions t=%.3g\", time)\n )\n scalarplot!(\n p[1, 1], subgrid2, U2; label = \"spec2\", color = :green,\n clear = false\n )\n scalarplot!(\n p[1, 1], subgrid3, U3; label = \"spec3\", color = :navyblue,\n clear = false, show = true\n )\n return if ismakie(Plotter)\n sleep(0.02)\n end\n end\n\n if diffeq\n inival = unknowns(sys, inival = 0)\n problem = ODEProblem(sys, inival, (0, tend))\n # use fixed timesteps just for the purpose of CI\n odesol = solve(problem, Rosenbrock23(), initializealg = NoInit(), dt = 1.0e-2, adaptive = false)\n tsol = reshape(odesol, sys)\n else\n tsol = solve(\n sys; inival = 0, times = (0, tend),\n verbose, Δu_opt = 1.0e-5,\n method_linear = KLUFactorization()\n )\n end\n\n testval = 0.0\n for i in 2:length(tsol.t)\n ui = view(tsol, 2, :, i)\n Δt = tsol.t[i] - tsol.t[i - 1]\n testval += sum(view(ui, subgrid2)) * Δt\n end\n\n if !isnothing(Plotter)\n for i in 2:length(tsol.t)\n plot_timestep(tsol.u[i], tsol.t[i])\n end\n end\n return testval\nend\n\nusing Test\n\nfunction runtests()\n testval = 0.06922262169719146\n testvaldiffeq = 0.06889809741891571\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\n\n\n @test main(; diffeq = true, unknown_storage = :sparse, rely_on_corrections = false, assembly = :edgewise) ≈ testvaldiffeq\n @test main(; diffeq = true, unknown_storage = :dense, rely_on_corrections = false, assembly = :edgewise) ≈ testvaldiffeq\n @test main(; diffeq = true, unknown_storage = :sparse, rely_on_corrections = true, assembly = :edgewise) ≈ testvaldiffeq\n @test main(; diffeq = true, unknown_storage = :dense, rely_on_corrections = true, assembly = :edgewise) ≈ testvaldiffeq\n @test main(; diffeq = true, unknown_storage = :sparse, rely_on_corrections = false, assembly = :cellwise) ≈ testvaldiffeq\n @test main(; diffeq = true, unknown_storage = :dense, rely_on_corrections = false, assembly = :cellwise) ≈ testvaldiffeq\n @test main(; diffeq = true, unknown_storage = :sparse, rely_on_corrections = true, assembly = :cellwise) ≈ testvaldiffeq\n @test main(; diffeq = true, unknown_storage = :dense, rely_on_corrections = true, assembly = :cellwise) ≈ testvaldiffeq\n return nothing\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/","page":"Changelog","title":"Changelog","text":"using Markdown\nMarkdown.parse(read(\"../../CHANGELOG.md\",String))","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 callbacks 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":"plutostatichtml_examples/bernoulli/","page":"Bernoulli function test","title":"Bernoulli function test","text":"\n\n\n\n
begin\n    import Pkg as _Pkg\n    haskey(ENV, \"PLUTO_PROJECT\") && _Pkg.activate(ENV[\"PLUTO_PROJECT\"])\n    using CairoMakie\n    using Revise\n    using VoronoiFVM\n    using ForwardDiff: derivative\nend
\n\n\n\n

Bernoulli function test

We test the implementation of the Bernoulli function in VoronoiFVM against the evaluation with BigFloat. This allows to optimize thresholds for switching between evaluation expressions.

\n\n\n

Reference with BigFLoat

\n\n
function B_Big(x)\n    bx = BigFloat(x)\n    return Float64(bx / expm1(bx))\nend
\n
B_Big (generic function with 1 method)
\n\n
function DB_Big(x)\n    bx = BigFloat(x)\n    bone = one(BigFloat)\n    bex = exp(bx)\n    b = -(x * bex - bex + bone) / ((bex - bone) * (bex - bone))\n    return Float64(b)\nend
\n
DB_Big (generic function with 1 method)
\n\n\n

Implementation using expm1

\n\n
B(x) = x / expm1(x)
\n
B (generic function with 1 method)
\n\n","category":"page"},{"location":"plutostatichtml_examples/bernoulli/#Approximation-for-small-x","page":"Bernoulli function test","title":"Approximation for small x","text":"","category":"section"},{"location":"plutostatichtml_examples/bernoulli/","page":"Bernoulli function test","title":"Bernoulli function test","text":"
\n

For small values of x, a Taylor approximation implemented using a Horner scheme is utilized, as the exponential expression runs into errors in the vicinity of zero and fails to evaluate at zero.. As as long as its error is large than that of the Taylor approximation calculated with the Taylor scheme, we should use the later one.

\n\n
B(0.0)
\n
NaN
\n\n
fbernoulli(0.0)
\n
1.0
\n\n
B(nextfloat(0.0))
\n
1.0
\n\n
fbernoulli(nextfloat(0.0))
\n
1.0
\n\n
derivative(B, 0.0)
\n
NaN
\n\n
derivative(fbernoulli, 0.0)
\n
-0.5
\n\n
derivative(B, nextfloat(0.0))
\n
NaN
\n\n
derivative(fbernoulli, nextfloat(0.0))
\n
-0.5
\n\n
smallX = collect(-0.5:(1.0e-4 + 1.0e-8):0.5);
\n\n\n\n\n\n\n\n\n","category":"page"},{"location":"plutostatichtml_examples/bernoulli/#Error-comparison-for-VoronoiFVM-implementation","page":"Bernoulli function test","title":"Error comparison for VoronoiFVM implementation","text":"","category":"section"},{"location":"plutostatichtml_examples/bernoulli/","page":"Bernoulli function test","title":"Bernoulli function test","text":"
\n
\n\n
largeX = -100:1.00001e-3:100;
\n\n\n\n\n\n\n\n\n\n

Derivative error

\n\n\n\n\n\n
\n
\n

Built with Julia 1.11.1 and

\nCairoMakie 0.12.16
\nForwardDiff 0.10.38
\nPkg 1.11.0
\nRevise 3.5.18
\nVoronoiFVM 1.25.1\n
\n\n","category":"page"},{"location":"plutostatichtml_examples/bernoulli/","page":"Bernoulli function test","title":"Bernoulli function test","text":"EditURL = \"https://github.com/WIAS-PDELib/VoronoiFVM.jl/blob/master/nothing\"","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, ExtendableGrids\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, data)\n f[1] = u[1, 1] - u[1, 2]\n return nothing\n end\n\n function bcond!(f, u, node, data)\n boundary_dirichlet!(f, u, node; region = 1, value = 0)\n boundary_dirichlet!(f, u, node; region = 2, value = 1)\n return nothing\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 = simplexgrid(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\n return nothing\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 n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise,\n dim = 2, tend = 5, dt = 0.2\n )\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, data)\n f .= u\n return nothing\n end\n\n function flux(f, u, edge, data)\n f[1] = u[1, 1] - u[1, 2]\n f[2] = u[2, 1] - u[2, 2]\n return nothing\n end\n\n r(u1, u2) = u1 - 0.1 * u2\n\n function reaction(f, u, node, data)\n f[1] = r(u[1], u[2])\n f[2] = -r(u[1], u[2])\n return nothing\n end\n\n function source(f, node, data)\n f[1] = 1.0\n return nothing\n end\n\n physics = VoronoiFVM.Physics(;\n flux = flux,\n storage = storage,\n reaction = reaction,\n source = source\n )\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(;\n Plotter = Plotter, layout = (1, 2), resolution = (600, 300),\n fignumber = 1\n )\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, data) -> source(f, node, data), 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(;\n Plotter = Plotter, layout = (1, 2), resolution = (600, 300),\n fignumber = 4\n )\n\n for i in 1:length(tsol)\n sol = tsol.u[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 in 2:length(tsol)\n ofr = integrate(system, T, tsol.u[i], tsol.u[i - 1], tsol.t[i] - tsol.t[i - 1])\n push!(outflow_rate, ofr[2])\n end\n\n vis2 = GridVisualizer(;\n Plotter = Plotter, layout = (1, 1), resolution = (600, 300),\n fignumber = 2\n )\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 in 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.u[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 return 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)\n return nothing\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 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 ###########################################################################\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, data)\n f[1] = d1 * (u[1, 1] - u[1, 2])\n return nothing\n end\n\n function reaction!(f, u, node, data)\n f[1] = k1[node.index] * u[1]\n return nothing\n end\n\n function source!(f, node::VoronoiFVM.Node, data)\n f[1] = c1[node.index]\n return nothing\n end\n\n sys_1D = VoronoiFVM.System(\n grid_1D,\n VoronoiFVM.Physics(;\n flux = flux!, reaction = reaction!,\n source = source!\n )\n )","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(;\n Plotter = Plotter, layout = (2, 1), clear = true,\n resolution = (800, 500)\n )\n\n scalarplot!(\n p[1, 1], grid_1D, sol_1D[1, :]; show = true,\n title = \"1D calculation (d1 = $d1, kmax = $kmax, cmax = $cmax)\"\n )\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, data)\n f[ispec_2D] = d2 * (u[ispec_2D, 1] - u[ispec_2D, 2])\n return nothing\n end\n\n function reaction2D!(f, u, node, data)\n f[ispec_2D] = k2 * u[ispec_2D]\n return nothing\n end\n\n function source2D!(f, node, data)\n f[ispec_2D] = c2\n return nothing\n end\n\n #### discretization functions for boundary species at active boundary ####\n function bflux!(f, u, bedge, data)\n if bedge.region == active_boundary\n f[ispec_boundary] = db * (u[ispec_boundary, 1] - u[ispec_boundary, 2])\n end\n return nothing\n end\n\n function breaction!(f, u, bnode, data)\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 return nothing\n end\n\n function bsource!(f, bnode, data)\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 return nothing\n end\n\n sys_2D = VoronoiFVM.System(\n grid_2D,\n VoronoiFVM.Physics(;\n flux = flux2D!, reaction = reaction2D!,\n source = source2D!,\n bflux = bflux!, breaction = breaction!,\n bsource = bsource!\n );\n unknown_storage = unknown_storage, assembly = assembly\n )","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 return nothing\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!(\n 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\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\n return nothing\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":"extensions/#[ExtendableFEMBase](https://github.com/chmerdon/ExtendableFEMBase.jl/)-Extension","page":"ExtendableFEMBase Extension","title":"ExtendableFEMBase Extension","text":"","category":"section"},{"location":"extensions/","page":"ExtendableFEMBase Extension","title":"ExtendableFEMBase Extension","text":"The extension for ExtendableFEMBase extends the functions below for solution types of ExtendableFEM. The name of the extension originates from the solution type FEVectorBlock that is defined in ExtendableFEMBase.","category":"page"},{"location":"extensions/","page":"ExtendableFEMBase Extension","title":"ExtendableFEMBase Extension","text":"edgevelocities(grid::ExtendableGrid, vel::FEVectorBlock; kwargs...)\nbfacevelocities(grid::ExtendableGrid, vel::FEVectorBlock; kwargs...)","category":"page"},{"location":"extensions/#VoronoiFVM.edgevelocities-Tuple{ExtendableGrid, FEVectorBlock}","page":"ExtendableFEMBase Extension","title":"VoronoiFVM.edgevelocities","text":"edgevelocities(grid, vel; kwargs...)\n\n\nCompute VoronoiFVM.edgevelocities for a finite element flow field computed by ExtendableFEM.\n\n\n\n\n\n","category":"method"},{"location":"extensions/#VoronoiFVM.bfacevelocities-Tuple{ExtendableGrid, FEVectorBlock}","page":"ExtendableFEMBase Extension","title":"VoronoiFVM.bfacevelocities","text":"bfacevelocities(grid, vel; kwargs...)\n\n\nCompute VoronoiFVM.bfacevelocities for a finite element flow field computed by ExtendableFEM.\n\n\n\n\n\n","category":"method"},{"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 n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse,\n dim = 2, assembly = :edgewise\n )\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, data)\n f .= u\n return nothing\n end\n\n function flux(f, u, edge, data)\n f[1] = u[1, 1] - u[1, 2]\n return nothing\n end\n\n function breaction(f, u, node, data)\n if node.region == Γ_where_T_equal_1[1]\n f[1] = u[1]^2\n end\n return nothing\n end\n\n physics = VoronoiFVM.Physics(;\n flux = flux,\n storage = storage,\n breaction = breaction\n )\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 return 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)\n return nothing\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\nimport Metis\n\n# Flux function which describes the flux\n# between neighboring control volumes\nfunction g!(f, u, edge, data)\n f[1] = u[1, 1] - u[1, 2]\n return nothing\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 = simplexgrid(X, X)\n grid = partition(grid, PlainMetisPartitioning(npart = 20))\n @show grid\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, vscale = 0.5, rasterpoints = 10)\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\n return nothing\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 OrdinaryDiffEqBDF\n    using OrdinaryDiffEqRosenbrock\n    using OrdinaryDiffEqSDIRK\n    using LinearAlgebra\n    using PlutoUI\n    using ExtendableGrids\n    using DataStructures\n    using GridVisualize, CairoMakie\n    CairoMakie.activate!(type = \"png\")\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 order 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            y[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\n    return nothing\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}}(\n  grid = ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=1, nnodes=151, ncells=150,\n  nbfaces=2),  \n  physics = Physics(data=@NamedTuple{c::Float64, bctype::Symbol}, flux=flux,\n  storage=storage, reaction=reaction, breaction=brea, ),  \n  num_species = 2)
\n\n\n

Perturbation in the center of the domain:

\n\n
begin\n    inival = unknowns(sys, inival = 0)\n    inival[1, :] .= map(x -> cos(κ * π * x) * exp(-x^2 / 0.1), grid)\nend;
\n\n\n
problem = ODEProblem(sys, inival, (0.0, tend));
\n\n\n
tsol = solve(\n    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\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
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.11.1 and

\n\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/WIAS-PDELib/VoronoiFVM.jl/blob/master/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 LinearSolve\nimport Triangulate\nimport Metis\n\nfunction main(;\n nref = 0, Plotter = nothing, verbose = \"and\", unknown_storage = :sparse, assembly = :edgewise,\n ythin = 0.25\n )\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 grid = partition(grid, PlainMetisPartitioning(npart = 20); nodes = true, edges = true)\n @show grid\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, data)\n y[iT] = c * u[iT]\n return nothing\n end\n\n κ(T) = κ0 * exp(α * (T - T0))\n\n function flux!(y, u, edge, data)\n y[iϕ] = κ(y[iT]) * (u[iϕ, 1] - u[iϕ, 2])\n y[iT] = λ * (u[iT, 1] - u[iT, 2])\n return nothing\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, data)\n y[iT] = -κ(y[iT]) * (u[iϕ, 1] - u[iϕ, 2]) * (u[iϕ, 1] - u[iϕ, 2])\n return nothing\n end\n\n function bcondition!(y, u, node, data)\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 return nothing\n end\n\n sys = VoronoiFVM.System(\n grid; bcondition = bcondition!, flux = flux!,\n edgereaction = jouleheat!, storage = storage!,\n species = [iϕ, iT], assembly = assembly\n )\n\n sol = solve(\n sys; verbose,\n method_linear = KrylovJL_BICGSTAB(precs = LinearSolvePreconBuilder(UMFPACKFactorization())),\n keepcurrent_linear = false\n )\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 return norm(sol, Inf)\nend\n\nusing Test\nfunction runtests()\n testval = 24.639120035942938\n @test main(; assembly = :edgewise) ≈ testval &&\n main(; assembly = :cellwise) ≈ testval\n return nothing\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 OrdinaryDiffEqBDF\n    using OrdinaryDiffEqRosenbrock\n    using OrdinaryDiffEqSDIRK\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 conditions 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, data)\n        f[1] = u[1, 1]^m - u[1, 2]^m\n        return nothing\n    end\n\n    storage!(f, u, node, data) = f[1] = u[1]\n\n    sys = VoronoiFVM.System(grid, flux = flux!, storage = storage!, species = 1)\n    return sys, X\nend
\n
create_porous_medium_problem (generic function with 1 method)
\n\n
begin\n    function 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        return sol, sys, err\n    end\n    run_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        state = VoronoiFVM.SystemState(sys)\n        problem = ODEProblem(state, inival, (t0, tend))\n        odesol = solve(problem, solver)\n        sol = reshape(odesol, sys; state)\n        err = norm(sol[1, :, end] - map(x -> barenblatt(x, tend, m), X))\n        return sol, sys, err\n    end\n    for method in diffeqmethods\n        run_diffeq(m = 2, n = 10, solver = 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
t1 = @elapsed sol1, sys1, err1 = run_vfvm(m = m, n = n);history_summary(sol1)
\n
(seconds = 1.56, tasm = 1.41, tlinsolve = 0.137, 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(sol2)
\n
(nd = 165, njac = 82, nf = 248)
\n\n\n\n\n\n

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

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

\n\n
@test err2 < err1
\n
Test Passed
\n
\n

Built with Julia 1.11.1 and

\n\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/WIAS-PDELib/VoronoiFVM.jl/blob/master/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: ILUZeroPreconBuilder\nusing GridVisualize\nusing LinearSolve\nusing ILUZero\n\nfunction main(;\n n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse,\n method_linear = nothing, assembly = :edgewise\n )\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 = simplexgrid(X, Y)\n\n eps = 1.0e-2\n\n physics = VoronoiFVM.Physics(;\n reaction = function (f, u, node, data)\n f[1] = u[1]^2\n return nothing\n end, flux = function (f, u, edge, data)\n f[1] = eps * (u[1, 1]^2 - u[1, 2]^2)\n return nothing\n end, source = function (f, node, data)\n x1 = node[1] - 0.5\n x2 = node[2] - 0.5\n f[1] = exp(-20.0 * (x1^2 + x2^2))\n return nothing\n end, storage = function (f, u, node, data)\n f[1] = u[1]\n return nothing\n end\n )\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 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(;\n unknown_storage = :sparse, method_linear = KrylovJL_CG(precs = ILUZeroPreconBuilder()),\n assembly = :edgewise\n ) ≈ testval &&\n main(;\n unknown_storage = :dense, method_linear = KrylovJL_CG(precs = ILUZeroPreconBuilder()),\n assembly = :edgewise\n ) ≈ testval &&\n main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval &&\n main(;\n unknown_storage = :sparse, method_linear = KrylovJL_CG(precs = ILUZeroPreconBuilder()),\n assembly = :cellwise\n ) ≈ testval &&\n main(;\n unknown_storage = :dense, method_linear = KrylovJL_CG(precs = ILUZeroPreconBuilder()),\n assembly = :cellwise\n ) ≈ testval\n return nothing\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(\n range(0, L; length = 10 * 2^nref),\n range(0, H; length = 10 * 2^nref)\n )\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, data)\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 return nothing\n end\n\n function outflow!(f, u, node, data)\n if node.region == Γ_out\n f[1] = bfvelo[node.ibnode, node.ibface] * u[1]\n end\n return nothing\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 return 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)\n return nothing\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\n return nothing\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\n return nothing\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]\n return nothing\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 = simplexgrid(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\n return nothing\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, data)\n f[1] = u[1, 1] - u[1, 2]\n return nothing\nend\n\nfunction s(f, node, data)\n n = view(node.coord, :, node.index)\n f[1] = n[1] * sin(5.0 * n[2]) * exp(n[3])\n return nothing\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 = simplexgrid(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\n return nothing\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 = simplexgrid(collect(0:h:1))\n\n eps::Vector{Float64} = [1.0, 1.0]\n\n physics = VoronoiFVM.Physics(\n ; reaction = function (f, u, node, data)\n f[1] = u[1] * u[2]\n f[2] = -u[1] * u[2]\n return nothing\n end,\n flux = function (f, u, edge, data)\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 return nothing\n end,\n source = function (f, node, data)\n f[1] = 1.0e-4 * (0.01 + node[1])\n f[2] = 1.0e-4 * (0.01 + 1.0 - node[1])\n return nothing\n end,\n storage = function (f, u, node, data)\n f[1] = u[1]\n f[2] = u[2]\n return nothing\n end\n )\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!(\n p[2, 1], grid, U[2, :]; clear = true, title = \"U2, eps=$(xeps)\",\n reveal = true\n )\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\n\n return nothing\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 = \"png\", 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(;\n        resolution = (500, 200),\n        xlabel = \"t\",\n        ylabel = \"outflow\",\n        legend = :rb,\n        title = \"Breakthrough Curves\"\n    )\n    scalarplot!(p1, sol_n.t, bt_n; label = \"naive grid\", color = :red)\n    scalarplot!(\n        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    )\n    scalarplot!(\n        p1,\n        sol_f.t,\n        bt_f;\n        label = \"grid with fix\",\n        markershape = :circle,\n        color = :green,\n        clear = false\n    )\n    scalarplot!(\n        p1,\n        sol_ϕ.t,\n        bt_ϕ;\n        label = \"modified ϕ\",\n        markershape = :cross,\n        color = :blue,\n        clear = false\n    )\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.00023622450350365277

  • 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    return 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    return 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 in 2:length(sol.t)\n        of[i] = -integrate(sys, tf, sol.u[i], sol.u[i - 1], t[i] - t[i - 1])[ic]\n    end\n    return of\nend
\n
breakthrough (generic function with 1 method)
\n\n\n

Transient solver:

\n\n
function trsolve(\n        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    )\n    function flux(y, u, edge, data)\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        return nothing\n    end\n\n    function stor(y, u, node, data)\n        y[ip] = 0\n        y[ic] = ϕ[node.region] * u[ic]\n        return nothing\n    end\n\n    dim = dim_space(grid)\n    function bc(y, u, bnode, data)\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        return nothing\n    end\n\n    sys = VoronoiFVM.System(\n        grid;\n        check_allocs = true,\n        flux = flux,\n        storage = stor,\n        bcondition = bc,\n        species = [ip, ic],\n    )\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(\n        sys;\n        inival = inival,\n        times = [0, tend],\n        Δt = 1.0e-4,\n        Δt_min = 1.0e-6,\n    )\n\n    bt = breakthrough(sys, tfc, sol)\n    if dim == 1\n        bt = bt * W\n    end\n\n    return 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, data)\n        y[1] = D[edge.region] * (u[1, 1] - u[1, 2])\n        return nothing\n    end\n\n    function rea(y, u, node, data)\n        y[1] = R[node.region] * u[1]\n        return nothing\n    end\n    function bc(y, u, bnode, data)\n        boundary_dirichlet!(y, u, bnode, 1, Γ_in, 1)\n        boundary_dirichlet!(y, u, bnode, 1, Γ_out, 0)\n        return nothing\n    end\n    sys = VoronoiFVM.System(\n        grid;\n        flux = flux,\n        reaction = rea,\n        species = 1,\n        bcondition = bc,\n        check_allocs = true\n    )\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    return 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.11.1 and

\nCairoMakie 0.12.16
\nExtendableGrids 1.9.2
\nGridVisualize 1.7.0
\nPkg 1.11.0
\nPlutoUI 0.7.60
\nRevise 3.5.18
\nTest 1.11.0
\nVoronoiFVM 1.25.1\n
\n\n","category":"page"},{"location":"plutostatichtml_examples/problemcase/","page":"A case for caution","title":"A case for caution","text":"EditURL = \"https://github.com/WIAS-PDELib/VoronoiFVM.jl/blob/master/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 updating CITATION.bib if you have published work which could be added to this list.","category":"page"},{"location":"","page":"Home","title":"Home","text":"D. Brust, M. Wullenkord, H. G. Gómez, J. Albero and C. Sattler. Experimental Investigation of Photo-Thermal Catalytic Reactor for the Reverse Water Gas Shift Reaction under Concentrated Irradiation. Journal of Environmental Chemical Engineering, 113372 (2024).\n\n\n\nD. Abdel. Modeling and simulation of vacancy-assisted charge transport in innovative semiconductor devices. PhD thesis, Freie Universität Berlin (2024).\n\n\n\nD. Brust, K. Hopf, J. Fuhrmann, A. Cheilytko, M. Wullenkord and C. Sattler. Transport of Heat and Mass for Reactive Gas Mixtures in Porous Media: Modeling and Application (SSRN, 2024).\n\n\n\nM. U. Qureshi, S. Matera, D. Runge, C. Merdon, J. Fuhrmann, J.-U. Repke and G. Brösigke. Reduced Order Cfd Modeling Approach Based on the Asymptotic Expansion-An Application for Heterogeneous Catalytic Systems (SSRN, 2024).\n\n\n\nT. Belin, P. Lafitte-Godillon, V. Lescarret, J. Fuhrmann and C. Mascia. Entropy solutions of a diffusion equation with discontinuous hysteresis and their finite volume approximation (HAL, 2024).\n\n\n\nS. 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(\n range(0, L; length = 20 * 2^nref),\n range(0, H; length = 5 * 2^nref)\n )\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, data)\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 return nothing\n end\n\n function outflow!(f, u, node, data)\n if node.region == 2\n f[1] = bfvelo[node.ibnode, node.ibface] * u[1]\n end\n return nothing\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 in 1:length(tsol.t)\n scalarplot!(\n vis[1, 1], grid, tsol[1, :, i]; flimits = (0, cin + 1.0e-5),\n title = @sprintf(\"time=%3f\", tsol.t[i]), show = true\n )\n end\n return tsol\nend\n\nusing Test\nfunction runtests()\n tsol1 = main(; assembly = :edgewise)\n tsol2 = main(; assembly = :cellwise)\n @test all(tsol1.u[end] .≈ 1)\n @test all(tsol1.u[end] .≈ 1)\n return nothing\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 OrdinaryDiffEqRosenbrock\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 n = 20, m = 2, Plotter = nothing, verbose = false,\n unknown_storage = :sparse, tend = 0.01, assembly = :edgewise, solver = Rosenbrock23()\n )\n\n # Create a one-dimensional discretization\n h = 1.0 / convert(Float64, n / 2)\n X = collect(-1:h:1)\n grid = simplexgrid(X)\n\n # Flux function which describes the flux\n # between neighboring control volumes\n function flux!(f, u, edg, data)\n f[1] = u[1, 1]^m - u[1, 2]^m\n return nothing\n end\n\n # Storage term\n function storage!(f, u, node, data)\n f[1] = u[1]\n return nothing\n end\n\n # Create a physics structure\n physics = VoronoiFVM.Physics(;\n flux = flux!,\n storage = storage!\n )\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 in 1:length(tsol)\n time = tsol.t[i]\n scalarplot!(\n p[1, 1], grid, tsol[1, :, i]; title = @sprintf(\"t=%.3g\", time),\n color = :red, label = \"numerical\",\n markershape = :circle, markevery = 1\n )\n scalarplot!(\n p[1, 1], grid, map(x -> barenblatt(x, time, m), grid); clear = false,\n color = :green,\n label = \"exact\", markershape = :none\n )\n reveal(p)\n sleep(1.0e-2)\n end\n return sum(tsol.u[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\n return nothing\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 = \"png\", 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, data)\n        f[1] = u[1]^2\n        return nothing\n    end\n\n    function flux(f, u, edge, data)\n        f[1] = eps * (u[1, 1]^2 - u[1, 2]^2)\n        return nothing\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.0 * (x1^2 + x2^2))\n        return nothing\n    end\n\n    function storage(f, u, node, data)\n        f[1] = u[1]\n        return nothing\n    end\n\n    function bcondition(f, u, node, data)\n        boundary_dirichlet!(\n            f,\n            u,\n            node;\n            species = 1,\n            region = 2,\n            value = ramp(node.time; dt = (0, 0.1), du = (0, 1))\n        )\n        boundary_dirichlet!(\n            f,\n            u,\n            node;\n            species = 1,\n            region = 4,\n            value = ramp(node.time; dt = (0, 0.1), du = (0, 1))\n        )\n        return nothing\n    end\n\n    sys0 = VoronoiFVM.System(\n        0.0:h:1.0,\n        0.0:h:1.0;\n        reaction,\n        flux,\n        source,\n        storage,\n        bcondition,\n        species = [1],\n    )\nend
\n
VoronoiFVM.System{Float64, Float64, Int32, Int64, Matrix{Int32}}(\n  grid = ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=2, nnodes=10201,\n  ncells=20000, nbfaces=400),  \n  physics = Physics(flux=flux, storage=storage, reaction=reaction, source=source,\n  breaction=bcondition, ),  \n  num_species = 1)
\n\n\n

Deprecated call:

\n\n\n

begin inival = unknowns(sys0; inival = 0.1) sol00 = unknowns(sys0) solve!(sol00, inival, sys0) end

\n\n\n

Replace this by:

\n\n
sol0 = solve(sys0; inival = 0.1)
\n
1×10201 VoronoiFVM.DenseSolutionArray{Float64, 2}:\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...)

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.

  • 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:

    • UMFPACKFactorization() for sparse matrices with Float64

    • SparspakFactorization() for sparse matrices with general number types.

    • Defaults from LinearSolve.jl for tridiagonal and banded matrices

    Users should experiment with what works best for their problem.

    For an overeview on possible alternatives, see 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 ? Translates to reuse_precs=!keepcurrent_linear for LinearSolve.

  • Δ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 embedding 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 VoronoiFVM.DenseSolutionArray{Float64, 2}:\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 VoronoiFVM.DenseSolutionArray{Float64, 2}:\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(\n    sys0;\n    inival = 0.1,\n    method_linear = KrylovJL_BICGSTAB(),\n    precon_linear = JacobiPreconditioner,\n    verbose = true,\n)
\n
1×10201 VoronoiFVM.DenseSolutionArray{Float64, 2}:\n 1.02242e-33  0.0319717  0.045243  0.0554731  …  0.045243  0.0319717  1.02242e-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(\n    sys0;\n    inival = 0.1,\n    method_linear = KrylovJL_BICGSTAB(),\n    precon_linear = SparspakFactorization(),\n    verbose = \"nlad\",\n)
\n
1×10201 VoronoiFVM.DenseSolutionArray{Float64, 2}:\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(\n    sys0;\n    inival = 0.5,\n    method_linear = KrylovJL_BICGSTAB(),\n    precon_linear = ILUZeroPreconditioner,\n    verbose = true,\n)
\n
1×10201 VoronoiFVM.DenseSolutionArray{Float64, 2}:\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, data)\n    return 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}}(\n  grid = ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=1, nnodes=1001, ncells=1000,\n  nbfaces=2),  \n  physics = Physics(flux=xflux, storage=default_storage, ),  \n  num_species = 1)
\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, data)\n    f[1] = D1 * (u[1, 1]^2 - u[1, 2]^2)\n    return nothing\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}}(\n  grid = ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=1, nnodes=1001, ncells=1000,\n  nbfaces=2),  \n  physics = Physics(flux=xflux1, storage=default_storage, ),  \n  num_species = 1)
\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, data)\n    for i in 1:(edge.nspec)\n        y[i] = u[i, 1] - u[i, 2]\n    end\n    return nothing\nend
\n
multispecies_flux (generic function with 1 method)
\n\n
function test_reaction(y, u, node, data)\n    y[1] = u[1]\n    y[2] = -u[1]\n    return nothing\nend
\n
test_reaction (generic function with 1 method)
\n\n
begin\n    system1 = VoronoiFVM.System(\n        grid1;\n        flux = multispecies_flux,\n        reaction = test_reaction,\n        species = [1, 2]\n    )\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, data)\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)\n    return nothing\nend;
\n\n\n
system2 = VoronoiFVM.System(\n    grid1;\n    flux = multispecies_flux,\n    reaction = test_reaction,\n    species = [1, 2],\n    bcondition = bcond2,\n    check_allocs = false\n);
\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, data)\n    boundary_dirichlet!(y, u, bnode; region = 4, value = bnode[2])\n    boundary_dirichlet!(y, u, bnode; region = 2, value = -bnode[2])\n    return nothing\nend;
\n\n\n
system3 = VoronoiFVM.System(\n    -1:0.1:1,\n    -1:0.1:1;\n    flux = multispecies_flux,\n    bcondition = bcond3,\n    species = 1\n);
\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, data) = y[1] = -bnode[1]^2 + u[1]^4;
\n\n\n
bc4(f, u, node, data) = boundary_dirichlet!(f, u, node; value = 0);
\n\n\n
system4 = VoronoiFVM.System(\n    -10:0.1:10;\n    species = [1],\n    reaction = reaction4,\n    flux = multispecies_flux,\n    bcondition = bc4\n);
\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.11.1 and

\nCairoMakie 0.12.16
\nExtendableGrids 1.9.2
\nExtendableSparse 1.5.3
\nGridVisualize 1.7.0
\nILUZero 0.2.0
\nLinearAlgebra 1.11.0
\nLinearSolve 2.34.0
\nPkg 1.11.0
\nPlutoUI 0.7.60
\nRevise 3.5.18
\nTest 1.11.0
\nVoronoiFVM 1.25.1\n
\n\n","category":"page"},{"location":"plutostatichtml_examples/api-update/","page":"API Updates","title":"API Updates","text":"EditURL = \"https://github.com/WIAS-PDELib/VoronoiFVM.jl/blob/master/nothing\"","category":"page"},{"location":"solutions/#Solution-objects","page":"Solution objects","title":"Solution objects","text":"","category":"section"},{"location":"solutions/","page":"Solution objects","title":"Solution objects","text":"AbstractSolutionArray","category":"page"},{"location":"solutions/#VoronoiFVM.AbstractSolutionArray","page":"Solution objects","title":"VoronoiFVM.AbstractSolutionArray","text":"abstract type AbstractSolutionArray{T, N} <: AbstractArray{T, N}\n\nAbstract type for stationary solution. Subtype of AbstractArray.\n\n\n\n\n\n","category":"type"},{"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":"mutable struct DenseSolutionArray{T, N} <: AbstractSolutionArray{T, N}\n\nDense storage of solution. Subtype of AbstractSolutionArray\n\nFields:\n\nu::Array\nhistory::Union{Nothing, NewtonSolverHistory}\n\n\n\n\n\n","category":"type"},{"location":"solutions/#VoronoiFVM.DenseSolutionArray-Union{Tuple{Int64, Int64}, Tuple{T}} where T","page":"Solution objects","title":"VoronoiFVM.DenseSolutionArray","text":"DenseSolutionArray constructor.\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.DenseSolutionArray-Union{Tuple{Matrix{T}}, Tuple{T}} where T","page":"Solution objects","title":"VoronoiFVM.DenseSolutionArray","text":"DenseSolutionArray(\n u::Array{T, 2}\n) -> VoronoiFVM.DenseSolutionArray{_A, 2} where _A\n\n\nDenseSolutionArray constructor.\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.DenseSolutionArray-Union{Tuple{UndefInitializer, Int64, Int64}, Tuple{T}} where T","page":"Solution objects","title":"VoronoiFVM.DenseSolutionArray","text":"DenseSolutionArray constructor.\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM._add-Tuple{VoronoiFVM.DenseSolutionArray, Any, Any}","page":"Solution objects","title":"VoronoiFVM._add","text":"_add(U::VoronoiFVM.DenseSolutionArray, idof, val) -> Any\n\n\nAdd residual value into global degree of freedom\n\n(Internal method)\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM._set-Tuple{VoronoiFVM.DenseSolutionArray, Any, Any}","page":"Solution objects","title":"VoronoiFVM._set","text":"_set(U::VoronoiFVM.DenseSolutionArray, idof, val) -> Any\n\n\nSet residual value for global degree of freedom\n\n(Internal method)\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.dof-Tuple{VoronoiFVM.DenseSolutionArray, Any, Any}","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.dofs-Tuple{VoronoiFVM.DenseSolutionArray}","page":"Solution objects","title":"VoronoiFVM.dofs","text":"dofs(a)\n\n\nVector of degrees of freedom in solution array.\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.solutionarray-Union{Tuple{Matrix{T}}, Tuple{T}} where T","page":"Solution objects","title":"VoronoiFVM.solutionarray","text":"solutionarray(a::Matrix)\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.unknown_indices-Tuple{VoronoiFVM.DenseSolutionArray}","page":"Solution objects","title":"VoronoiFVM.unknown_indices","text":"unknown_indices(a)\n\n\nReturn indices for dense 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":"mutable struct SparseSolutionArray{T, N, Ti} <: AbstractSolutionArray{T, N}\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\nu::SparseArrays.SparseMatrixCSC{T, Ti} where {T, Ti}: Sparse matrix holding actual data.\n\nhistory::Union{Nothing, NewtonSolverHistory}\n\n\n\n\n\n","category":"type"},{"location":"solutions/#VoronoiFVM.SparseSolutionArray-Union{Tuple{SparseArrays.SparseMatrixCSC{Tv, Ti}}, Tuple{Ti}, Tuple{Tv}} where {Tv, Ti}","page":"Solution objects","title":"VoronoiFVM.SparseSolutionArray","text":"SparseSolutionArray(\n a::SparseArrays.SparseMatrixCSC{Tv, Ti}\n) -> VoronoiFVM.SparseSolutionArray{_A, 2} where _A\n\n\nSparseSolutionArray constructor\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.SparseSolutionIndices","page":"Solution objects","title":"VoronoiFVM.SparseSolutionIndices","text":"struct SparseSolutionIndices\n\nWrapper structure to access sparse solution indices.\n\n\n\n\n\n","category":"type"},{"location":"solutions/#Base.copy-Union{Tuple{VoronoiFVM.SparseSolutionArray{T, N, Ti}}, Tuple{Ti}, Tuple{N}, Tuple{T}} where {T, N, 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, Int64, Int64}","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, Int64, Int64}","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{T, N, Ti}}, Tuple{Ti}, Tuple{N}, Tuple{T}} where {T, N, 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/#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(internal)\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM._set-Tuple{VoronoiFVM.SparseSolutionArray, Any, Any}","page":"Solution objects","title":"VoronoiFVM._set","text":"_set(U::VoronoiFVM.SparseSolutionArray, idof, val) -> Any\n\n\nSet residual value for global degree of freedom\n\n(internal)\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.dof-Tuple{VoronoiFVM.SparseSolutionArray, Any, Any}","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.dofs-Tuple{VoronoiFVM.SparseSolutionArray}","page":"Solution objects","title":"VoronoiFVM.dofs","text":"dofs(a)\n\n\nVector of degrees of freedom in sparse solution array.\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.solutionarray-Union{Tuple{SparseArrays.SparseMatrixCSC{Tv, Ti}}, Tuple{Ti}, Tuple{Tv}} where {Tv, Ti}","page":"Solution objects","title":"VoronoiFVM.solutionarray","text":"solutionarray(a::SparseMatrixCSC)\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.unknown_indices-Tuple{VoronoiFVM.SparseSolutionArray}","page":"Solution objects","title":"VoronoiFVM.unknown_indices","text":"unknown_indices(a)\n\n\nReturn indices for 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::Union{VoronoiFVM.DiffEqHistory, 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/#Base.append!","page":"Solution objects","title":"Base.append!","text":"append!(tsol::AbstractTransientSolution, t, sol)\n\nAppend time step solution sol as solution at time t to tsol. sol will be directly references in tsol. This method does not copy sol. If during a time steping process it is the same vector, a copy should appended.\n\nDefined in VoronoiFVM.jl.\n\n\n\n\n\n","category":"function"},{"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 = simplexgrid(X, Y)\n\n k = 1.0\n eps::Float64 = 1.0\n physics = VoronoiFVM.Physics(;\n breaction = function (f, u, node, data)\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 return nothing\n end, bstorage = function (f, u, node, data)\n if node.region == 2\n f[3] = u[3]\n end\n return nothing\n end, flux = function (f, u, edge, data)\n f[1] = eps * (u[1, 1] - u[1, 2])\n f[2] = eps * (u[2, 1] - u[2, 2])\n return nothing\n end, source = function (f, node, data)\n x1 = node[1] - 0.5\n x2 = node[2] - 0.5\n f[1] = exp(-20.0 * (x1^2 + x2^2))\n return nothing\n end, storage = function (f, u, node, data)\n f[1] = u[1]\n f[2] = u[2]\n return nothing\n end\n )\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 return 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 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\n return nothing\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":"plutostatichtml_examples/heterogeneous-catalysis/","page":"Coupling with Catalyst.jl","title":"Coupling with Catalyst.jl","text":"\n\n\n\n
begin\n    using SciMLBase: ODEProblem, solve\n    using OrdinaryDiffEqLowOrderRK: DP5\n    using OrdinaryDiffEqRosenbrock: Rosenbrock23\n    using OrdinaryDiffEqTsit5: Tsit5\n    using Catalyst\n    using VoronoiFVM: VoronoiFVM, enable_species!, enable_boundary_species!\n    using VoronoiFVM: ramp, boundary_dirichlet!\n    using ExtendableGrids: simplexgrid\n    using GridVisualize: GridVisualize, GridVisualizer, reveal, scalarplot!, gridplot, available_kwargs\n    if doplots # defined in the Appendix\n        using Plots: Plots, plot, theme\n        using PlotThemes\n        Plots.gr()\n        Plots.theme(:dark)\n        GridVisualize.default_plotter!(Plots)\n    end\n    import PlutoUI\n    using Test\n    PlutoUI.TableOfContents(; depth = 4)\nend
\n\n\n\n

Towards Heterogeneous Catalysis

\n\n\n

How to model and simulate heterogeneous catalysis with Catalyst.jl and VoronoiFVM.jl.

\n\n","category":"page"},{"location":"plutostatichtml_examples/heterogeneous-catalysis/#Mass-action-kinetics","page":"Coupling with Catalyst.jl","title":"Mass action kinetics","text":"","category":"section"},{"location":"plutostatichtml_examples/heterogeneous-catalysis/","page":"Coupling with Catalyst.jl","title":"Coupling with Catalyst.jl","text":"
\n
\n\n\n\n\n\n

Assume \\(j=1 … M\\) reversible reactions of educt (substrate) species to product species

$$\tα_1^j S_1 + α_2^j S_2 + … + α_n^jS_n \\underset{k_j^+}{\\stackrel{k_j^-}{\\longrightleftharpoons}} β_1^jS_1 + β_2^j S_2 + … + β_n^j S_n$$

or equivalently,

$$∑_{i=1}^n α_{i}^j S_i \\underset{k_j^+}{\\stackrel{k_j^-}{\\longrightleftharpoons}} ∑_{i=1}^n β_i^j S_i $$

The rate of these reactions depend on the concentrations \\([S_i]\\) of the species. Within \\(Catalyst.jl\\), due to consistency with the derivation from stochastic approaches, the default \"combinatoric rate law\" is

$$ r_j=k_j^+ ∏_{i=1}^n \\frac{[S_i]^{α_i^j}}{α_i^j!} - k_j^- ∏_{i=1}^n \\frac{[S_i]^{β_i^j}}{β_i^j!} $$

while it appears that in most textbooks, the rate law is

$$ r_j=k_j^+ ∏_{i=1}^n [S_i]^{α_i^j} - k_j^- ∏_{i=1}^n [S_i]^{β_i^j}.$$

See the corresponding remark in the Catalyst.jl docs and the github issue. We will stick to the secobd version which can be achieved by setting combinatoric_ratelaws to false at appropriate places.

Later in the project we will see that instead of the concentrations, we need to work with so called activities.

\n\n\n

The numbers \\(σ_i^j=α_i^j-β_i^j\\) are the net stoichiometric coefficients of the system.

\n\n\n

The rate differential equations then are (TODO: check this)

$$\t∂_t [S_i] + \\sum_{j=1}^M \\sigma_i^jr_j = 0$$

These assume that the reactions take place in a continuously stirred tank reactor (CSTR) which means that we have complete mixing, and species concentrations are spatially constant, and we have just one concentration value for each species at a given point of time.

\n\n\n

Example 1: A \\(\\longrightleftharpoons\\) B

$$\\begin{aligned}\n A& \\underset{k_1^+}{\\stackrel{k_1^-}{\\longrightleftharpoons}} B\\\\\n r_1&= k_1^+ [A] - k_1^- [B]\\\\\n \\partial_t[A] &= -r_1\\\\\n \\partial_t[B] &= r_1\n\\end{aligned} $$

\n\n\n

Solution via plain ODE problem using OrdinaryDiffEq.jl:

\n\n\n

Set the parameters such that the forward reaction is faster then the backward reaction:

\n\n
p1 = (k_p = 1, k_m = 0.1)
\n
(k_p = 1, k_m = 0.1)
\n\n\n

Define an ODE function describing the right hand side of the ODE System:

\n\n
function example1(du, u, p, t)\n    (; k_p, k_m) = p\n    r1 = k_p * u[1] - k_m * u[2]\n    du[1] = -r1\n    return du[2] = r1\nend
\n
example1 (generic function with 1 method)
\n\n\n

Define some initial value:

\n\n
u1_ini = [1.0, 0.0]
\n
2-element Vector{Float64}:\n 1.0\n 0.0
\n\n\n

Create an solve the corresponding ODEProblem

\n\n
prob1 = ODEProblem(example1, u1_ini, (0, 10), p1)
\n
ODEProblem with uType Vector{Float64} and tType Int64. In-place: true\ntimespan: (0, 10)\nu0: 2-element Vector{Float64}:\n 1.0\n 0.0
\n\n
sol1 = solve(prob1, DP5())
\n
timestampvalue1value2
10.01.00.0
20.0009990010.9990020.000998452
30.0109890.9890770.0109229
40.1108890.8956070.104393
50.4188180.6644020.335598
60.8599810.4439120.556088
71.471250.2711230.728877
82.180130.1735570.826443
92.977590.1253020.874698
103.853570.1040430.895957
114.836140.09537550.904625
125.967130.09220440.907796
137.312950.0912110.908789
148.970330.09096420.909036
1510.00.09092690.909073
\n\n
doplots && plot(sol1; size = (600, 200))
\n\n\n\n

Mass conservation: adding the two reaction eqauations results in

$$\t\\partial_t ([A]+[B]) = 0,$$

therefore \\([A]+[B]\\) must be constant:

\n\n
all(s -> isapprox(s, sum(u1_ini)), sum(sol1; dims = 1))
\n
true
\n\n\n

Catalyst.@reaction_network

\n\n\n

Catalyst.jl provides a convenient way to define a reaction network, and the resulting reaction system. So we use this to build the same system:

\n\n
rn1 = @reaction_network rn1 begin\n    @combinatoric_ratelaws false\n    k_p, A --> B\n    k_m, B --> A\nend
\n

$$\\begin{align*}\n\\mathrm{A} &\\xrightleftharpoons[k_{m}]{k_{p}} \\mathrm{B} \n \\end{align*}$$

\n\n\n

The corresponding ODE system is:

\n\n
convert(ODESystem, rn1)
\n

$$\\begin{align}\n\\frac{\\mathrm{d} A\\left( t \\right)}{\\mathrm{d}t} &= k_{m} B\\left( t \\right) - k_{p} A\\left( t \\right) \\\\\n\\frac{\\mathrm{d} B\\left( t \\right)}{\\mathrm{d}t} &= - k_{m} B\\left( t \\right) + k_{p} A\\left( t \\right)\n\\end{align}$$

\n\n\n

Catalyst.jl adds a new method to the ODEProblem constructor which allows to pass a reaction nerwork:

\n\n
prob1n = ODEProblem(rn1, u1_ini, (0, 10.0), Dict(pairs(p1)))
\n
ODEProblem with uType Vector{Float64} and tType Float64. In-place: true\ntimespan: (0.0, 10.0)\nu0: 2-element Vector{Float64}:\n 1.0\n 0.0
\n\n
sol1n = solve(prob1n, Rosenbrock23())
\n
timestampA(t)B(t)
10.01.00.0
20.0001133860.9998870.000113379
30.001247250.9987540.0012464
40.008102660.9919330.00806667
50.01833760.9818460.0181539
60.03991150.9609510.0390486
70.06953730.9330540.066946
80.1171840.8900480.109952
90.1778980.8384120.161588
100.2614260.7727690.227231
...
\n\n
doplots && plot(sol1n; idxs = [rn1.A, rn1.B, rn1.A + rn1.B], size = (600, 200))
\n\n\n\n

Unraveling @reaction_network

Let us try to look behind the macro voodoo - this is necessary to build networks programmatically and is behind the translation from python to Julia in CatmapInterface.jl.

\n\n\n

It is interesting if there is a \"macro - less\" way to define variables, parameters and species.

\n\n
@variables t
\n

$$\\begin{equation}\n\\left[\n\\begin{array}{c}\nt \\\\\n\\end{array}\n\\right]\n\\end{equation}$$

\n\n
@parameters k_p k_m
\n

$$\\begin{equation}\n\\left[\n\\begin{array}{c}\nk_{p} \\\\\nk_{m} \\\\\n\\end{array}\n\\right]\n\\end{equation}$$

\n\n
@species A(t) B(t)
\n

$$\\begin{equation}\n\\left[\n\\begin{array}{c}\nA\\left( t \\right) \\\\\nB\\left( t \\right) \\\\\n\\end{array}\n\\right]\n\\end{equation}$$

\n\n\n

A reaction network can be combined from several reactions:

\n\n
r1p = Reaction(k_p, [A], [B], [1], [1])
\n
k_p, A --> B
\n\n
r1m = Reaction(k_m, [B], [A], [1], [1])
\n
k_m, B --> A
\n\n
rn1x = complete(ReactionSystem([r1p, r1m], t; name = :example1))
\n

$$\\begin{align*}\n\\mathrm{A} &\\xrightleftharpoons[k_{m}]{k_{p}} \\mathrm{B} \n \\end{align*}$$

\n\n\n

Once we are here, the rest remains the same.

\n\n
convert(ODESystem, rn1x)
\n

$$\\begin{align}\n\\frac{\\mathrm{d} A\\left( t \\right)}{\\mathrm{d}t} &= k_{m} B\\left( t \\right) - k_{p} A\\left( t \\right) \\\\\n\\frac{\\mathrm{d} B\\left( t \\right)}{\\mathrm{d}t} &= - k_{m} B\\left( t \\right) + k_{p} A\\left( t \\right)\n\\end{align}$$

\n\n
prob1x = ODEProblem(rn1x, u1_ini, (0, 10.0), Dict(pairs(p1)))
\n
ODEProblem with uType Vector{Float64} and tType Float64. In-place: true\ntimespan: (0.0, 10.0)\nu0: 2-element Vector{Float64}:\n 1.0\n 0.0
\n\n
sol1x = solve(prob1x, Rosenbrock23())
\n
timestampA(t)B(t)
10.01.00.0
20.0001133860.9998870.000113379
30.001247250.9987540.0012464
40.008102660.9919330.00806667
50.01833760.9818460.0181539
60.03991150.9609510.0390486
70.06953730.9330540.066946
80.1171840.8900480.109952
90.1778980.8384120.161588
100.2614260.7727690.227231
...
\n\n
doplots && plot(sol1x; size = (600, 200))
\n\n\n\n

Example 2: A + 2B \\(\\longrightleftharpoons\\) AB_2

\n\n
rn2 = @reaction_network rn2 begin\n    @combinatoric_ratelaws false\n    k_0A, ∅ --> A\n    k_0B, ∅ --> B\n    (k_1p, k_1m), A + 2B <--> AB_2\nend
\n

$$\\begin{align*}\n\\varnothing &\\xrightarrow{k_{0A}} \\mathrm{A} \\\\\n\\varnothing &\\xrightarrow{k_{0B}} \\mathrm{B} \\\\\n\\mathrm{A} + 2 \\mathrm{B} &\\xrightleftharpoons[k_{1m}]{k_{1p}} \\mathrm{AB_{2}} \n \\end{align*}$$

\n\n
convert(ODESystem, rn2)
\n

$$\\begin{align}\n\\frac{\\mathrm{d} A\\left( t \\right)}{\\mathrm{d}t} &= k_{0A} + k_{1m} \\mathrm{AB}_{2}\\left( t \\right) - \\left( B\\left( t \\right) \\right)^{2} k_{1p} A\\left( t \\right) \\\\\n\\frac{\\mathrm{d} B\\left( t \\right)}{\\mathrm{d}t} &= k_{0B} + 2 k_{1m} \\mathrm{AB}_{2}\\left( t \\right) - 2 \\left( B\\left( t \\right) \\right)^{2} k_{1p} A\\left( t \\right) \\\\\n\\frac{\\mathrm{d} \\mathrm{AB}_{2}\\left( t \\right)}{\\mathrm{d}t} &= - k_{1m} \\mathrm{AB}_{2}\\left( t \\right) + \\left( B\\left( t \\right) \\right)^{2} k_{1p} A\\left( t \\right)\n\\end{align}$$

\n\n
p2 = (k_0A = 0.5, k_0B = 1, k_1p = 0.1, k_1m = 1.0e-5)
\n
(k_0A = 0.5, k_0B = 1, k_1p = 0.1, k_1m = 1.0e-5)
\n\n
u2_ini = (A = 0, B = 0, AB_2 = 0)
\n
(A = 0, B = 0, AB_2 = 0)
\n\n
prob2 = ODEProblem(rn2, Dict(pairs(u2_ini)), (0, 20.0), Dict(pairs(p2)))
\n
ODEProblem with uType Vector{Float64} and tType Float64. In-place: true\ntimespan: (0.0, 20.0)\nu0: 3-element Vector{Float64}:\n 0.0\n 0.0\n 0.0
\n\n
sol2 = solve(prob2, Rosenbrock23())
\n
timestampA(t)B(t)AB_2(t)
10.00.00.00.0
20.00015.0e-50.00016.25e-19
30.00110.000550.00111.08006e-14
40.01110.005550.01111.13501e-10
50.0584360.02921790.05843589.95852e-8
60.08245190.04122540.08245095.19335e-7
70.1417660.07087850.1417574.69768e-6
80.1787550.08936510.178731.23077e-5
90.2419570.1209370.2418744.17015e-5
100.2876190.1437260.2874518.40283e-5
...
\n\n
doplots && plot(sol2; legend = :topleft, size = (600, 300))
\n\n\n\n

Example 3: Catalysis for A + 2B \\(\\rightleftharpoons\\) AB_2

\n\n\n

The same reaction as in example 2, but now with a catalyst C.

The reaction between A and B takes places when A and B are bound (adsorbed) to the catalyst. So we have adsorption reactions, reactions at the catalyst, and desorption. The overall of free and bound catalyst needs to be constant over time.

\n\n
rn3 = @reaction_network rn3 begin\n    @combinatoric_ratelaws false\n    k_0A, ∅ --> A\n    k_0B, ∅ --> B\n    (k_1p, k_1m), A + C <--> CA\n    (k_2p, k_2m), B + C <--> CB\n    (k_3p, k_3m), CA + 2CB <--> CAB2 + 2C\n    (k_4p, k_4m), CAB2 <--> AB2 + C\nend
\n

$$\\begin{align*}\n\\varnothing &\\xrightarrow{k_{0A}} \\mathrm{A} \\\\\n\\varnothing &\\xrightarrow{k_{0B}} \\mathrm{B} \\\\\n\\mathrm{A} + \\mathrm{C} &\\xrightleftharpoons[k_{1m}]{k_{1p}} \\mathrm{CA} \\\\\n\\mathrm{B} + \\mathrm{C} &\\xrightleftharpoons[k_{2m}]{k_{2p}} \\mathrm{CB} \\\\\n\\mathrm{CA} + 2 \\mathrm{CB} &\\xrightleftharpoons[k_{3m}]{k_{3p}} \\mathrm{CAB2} + 2 \\mathrm{C} \\\\\n\\mathrm{CAB2} &\\xrightleftharpoons[k_{4m}]{k_{4p}} \\mathrm{AB2} + \\mathrm{C} \n \\end{align*}$$

\n\n
convert(ODESystem, rn3)
\n

$$\\begin{align}\n\\frac{\\mathrm{d} A\\left( t \\right)}{\\mathrm{d}t} &= k_{0A} + k_{1m} \\mathrm{CA}\\left( t \\right) - k_{1p} A\\left( t \\right) C\\left( t \\right) \\\\\n\\frac{\\mathrm{d} B\\left( t \\right)}{\\mathrm{d}t} &= k_{0B} + k_{2m} \\mathrm{CB}\\left( t \\right) - k_{2p} B\\left( t \\right) C\\left( t \\right) \\\\\n\\frac{\\mathrm{d} C\\left( t \\right)}{\\mathrm{d}t} &= k_{1m} \\mathrm{CA}\\left( t \\right) + k_{2m} \\mathrm{CB}\\left( t \\right) + k_{4p} \\mathrm{CAB2}\\left( t \\right) - k_{1p} A\\left( t \\right) C\\left( t \\right) - k_{2p} B\\left( t \\right) C\\left( t \\right) - k_{4m} C\\left( t \\right) \\mathrm{AB2}\\left( t \\right) - 2 \\left( C\\left( t \\right) \\right)^{2} k_{3m} \\mathrm{CAB2}\\left( t \\right) + 2 \\left( \\mathrm{CB}\\left( t \\right) \\right)^{2} k_{3p} \\mathrm{CA}\\left( t \\right) \\\\\n\\frac{\\mathrm{d} \\mathrm{CA}\\left( t \\right)}{\\mathrm{d}t} &= - k_{1m} \\mathrm{CA}\\left( t \\right) + k_{1p} A\\left( t \\right) C\\left( t \\right) + \\left( C\\left( t \\right) \\right)^{2} k_{3m} \\mathrm{CAB2}\\left( t \\right) - \\left( \\mathrm{CB}\\left( t \\right) \\right)^{2} k_{3p} \\mathrm{CA}\\left( t \\right) \\\\\n\\frac{\\mathrm{d} \\mathrm{CB}\\left( t \\right)}{\\mathrm{d}t} &= - k_{2m} \\mathrm{CB}\\left( t \\right) + k_{2p} B\\left( t \\right) C\\left( t \\right) + 2 \\left( C\\left( t \\right) \\right)^{2} k_{3m} \\mathrm{CAB2}\\left( t \\right) - 2 \\left( \\mathrm{CB}\\left( t \\right) \\right)^{2} k_{3p} \\mathrm{CA}\\left( t \\right) \\\\\n\\frac{\\mathrm{d} \\mathrm{CAB2}\\left( t \\right)}{\\mathrm{d}t} &= - k_{4p} \\mathrm{CAB2}\\left( t \\right) + k_{4m} C\\left( t \\right) \\mathrm{AB2}\\left( t \\right) - \\left( C\\left( t \\right) \\right)^{2} k_{3m} \\mathrm{CAB2}\\left( t \\right) + \\left( \\mathrm{CB}\\left( t \\right) \\right)^{2} k_{3p} \\mathrm{CA}\\left( t \\right) \\\\\n\\frac{\\mathrm{d} \\mathrm{AB2}\\left( t \\right)}{\\mathrm{d}t} &= k_{4p} \\mathrm{CAB2}\\left( t \\right) - k_{4m} C\\left( t \\right) \\mathrm{AB2}\\left( t \\right)\n\\end{align}$$

\n\n
p3 = (\n    k_0A = 0.5, k_0B = 1,\n    k_1p = 10, k_1m = 0.1,\n    k_2p = 10, k_2m = 0.1,\n    k_3p = 10, k_3m = 0.1,\n    k_4p = 10, k_4m = 0.1,\n)
\n
(k_0A = 0.5, k_0B = 1, k_1p = 10, k_1m = 0.1, k_2p = 10, k_2m = 0.1, k_3p = 10, k_3m = 0.1, k_4p = 10, k_4m = 0.1)
\n\n
Cini = 40
\n
40
\n\n
u3_ini = (A = 0, B = 0, CA = 0, CB = 0, CAB2 = 0, AB2 = 0, C = Cini)
\n
(A = 0, B = 0, CA = 0, CB = 0, CAB2 = 0, AB2 = 0, C = 40)
\n\n
t3end = 200
\n
200
\n\n
prob3 = ODEProblem(rn3, Dict(pairs(u3_ini)), (0, t3end), Dict(pairs(p3)))
\n
ODEProblem with uType Vector{Float64} and tType Int64. In-place: true\ntimespan: (0, 200)\nu0: 7-element Vector{Float64}:\n  0.0\n  0.0\n 40.0\n  0.0\n  0.0\n  0.0\n  0.0
\n\n
sol3 = solve(prob3, Rosenbrock23())
\n
timestampA(t)B(t)C(t)CA(t)CB(t)CAB2(t)AB2(t)
10.00.00.040.00.00.00.00.0
26.46784e-63.22974e-66.45949e-640.04.17882e-98.35764e-94.74654e-318.99173e-36
37.11463e-53.50726e-57.01452e-540.05.00556e-71.00111e-61.20711e-232.28831e-27
40.0001750168.45196e-50.00016903940.02.9886e-65.9772e-61.52686e-205.17114e-24
50.00033990.000158920.0003178440.01.10301e-52.20603e-51.86355e-181.10544e-21
60.0005593470.0002506380.00050127739.99992.9035e-55.807e-56.46331e-175.76826e-20
70.000853060.0003614740.00072294939.99986.50556e-50.0001301111.1845e-151.55464e-18
80.001210110.0004798250.0009596539.99960.0001252320.0002504641.26499e-142.27535e-17
90.001650390.0006043420.0012086839.99930.0002208530.0004417069.78351e-142.37856e-16
100.002167790.0007252540.0014505139.99890.0003586380.0007172775.66062e-131.79861e-15
...
\n\n
doplots && plot(sol3; legend = :topleft, size = (600, 300))
\n\n\n
ctotal = rn3.C + rn3.CA + rn3.CB + rn3.CAB2
\n

$$\\begin{equation}\n\\mathrm{CAB2}\\left( t \\right) + \\mathrm{CA}\\left( t \\right) + \\mathrm{CB}\\left( t \\right) + C\\left( t \\right)\n\\end{equation}$$

\n\n
sol3[ctotal]
\n
148-element Vector{Float64}:\n 40.0\n 40.0\n 40.0\n 39.99999999999999\n 39.99999999999999\n 39.99999999999999\n 39.99999999999999\n  ⋮\n 39.99999999999902\n 39.99999999999902\n 39.99999999999902\n 39.99999999999902\n 39.99999999999902\n 39.99999999999926
\n\n
@test sol3[ctotal] ≈ fill(Cini, length(sol3))
\n
Test Passed
\n\n","category":"page"},{"location":"plutostatichtml_examples/heterogeneous-catalysis/#Example-4:-Heterogeneous-catalysis","page":"Coupling with Catalyst.jl","title":"Example 4: Heterogeneous catalysis","text":"","category":"section"},{"location":"plutostatichtml_examples/heterogeneous-catalysis/","page":"Coupling with Catalyst.jl","title":"Coupling with Catalyst.jl","text":"
\n

Heterogeneous catalysis assumes that the catalytic reaction takes place at surface. This means that reacting species need to be transported towards or away from the surface, and one has to model coupled transport and surface reaction.

Here we use VoronoiFVM.jl to model transport and Catalyst.jl to create the surface reaction network.

Problem specification

Assume \\(\\Omega=(0,1)\\) where a catalytic reaction takes place at \\(x=0\\). We assume that the educts A, B, and the product AB2 are bulk species transported to the domain. At \\(x=1\\) we set Dirichlet boundary conditions providing A,B and removing AB2.

A, B can adsorb at the catalyst at \\(x=0\\) and react to AB2 while adsorbed. The product desorbs and is released to the bulk. So we have

  • Mass transport in the interior of \\(\\Omega\\):

$$\\begin{aligned}\n\\partial_t c_A + \\nabla \\cdot D_A \\nabla c_A &=0\\\\\n\\partial_t c_B + \\nabla \\cdot D_B \\nabla c_B &=0\\\\\n\\partial_t c_{AB2} + \\nabla \\cdot D_{AB2} \\nabla c_{AB2} &=0\n\\end{aligned}$$

  • Coupled nonlinear robin boundary conditions at \\(x=0\\):

$$\\begin{aligned}\nD_A\\partial_n c_A + r_1 &= 0\\\\\nD_B\\partial_n c_A + r_2 &= 0\\\\\nD_{AB2}\\partial_n c_{AB2} - r_4 &= 0\\\\\n\\end{aligned}$$

  • \\(r_1, r_2\\) and \\(r_4\\) are asorption/desorption reactions:

$$\\begin{aligned}\n r_1&=k_{1p}c_A c_C - k_{1m}c_{CA}\\\\\n r_2&=k_{2p}c_B c_C - k_{2m}c_{CB}\\\\\n r_4&=k_{4p}c_{AB2} - k_{4m}c_{C_C}c_{C_{AB2}}\\\\\n\\end{aligned}$$

\n\n\n
  • The free catalyst sites C and the catalyst coverages CA, CB, CAB2 behave according to:

\n\n\n\n\n\n

$$\\begin{equation}\n\\frac{\\mathrm{d} C\\left( t \\right)}{\\mathrm{d}t} = k_{1m} \\mathrm{CA}\\left( t \\right) + k_{2m} \\mathrm{CB}\\left( t \\right) + k_{4p} \\mathrm{CAB2}\\left( t \\right) - k_{1p} A\\left( t \\right) C\\left( t \\right) - k_{2p} B\\left( t \\right) C\\left( t \\right) - k_{4m} C\\left( t \\right) \\mathrm{AB2}\\left( t \\right) - 2 \\left( C\\left( t \\right) \\right)^{2} k_{3m} \\mathrm{CAB2}\\left( t \\right) + 2 \\left( \\mathrm{CB}\\left( t \\right) \\right)^{2} k_{3p} \\mathrm{CA}\\left( t \\right)\n\\end{equation}$$

\n\n\n

$$\\begin{equation}\n\\frac{\\mathrm{d} \\mathrm{CA}\\left( t \\right)}{\\mathrm{d}t} = - k_{1m} \\mathrm{CA}\\left( t \\right) + k_{1p} A\\left( t \\right) C\\left( t \\right) + \\left( C\\left( t \\right) \\right)^{2} k_{3m} \\mathrm{CAB2}\\left( t \\right) - \\left( \\mathrm{CB}\\left( t \\right) \\right)^{2} k_{3p} \\mathrm{CA}\\left( t \\right)\n\\end{equation}$$

\n\n\n

$$\\begin{equation}\n\\frac{\\mathrm{d} \\mathrm{CB}\\left( t \\right)}{\\mathrm{d}t} = - k_{2m} \\mathrm{CB}\\left( t \\right) + k_{2p} B\\left( t \\right) C\\left( t \\right) + 2 \\left( C\\left( t \\right) \\right)^{2} k_{3m} \\mathrm{CAB2}\\left( t \\right) - 2 \\left( \\mathrm{CB}\\left( t \\right) \\right)^{2} k_{3p} \\mathrm{CA}\\left( t \\right)\n\\end{equation}$$

\n\n\n

$$\\begin{equation}\n\\frac{\\mathrm{d} \\mathrm{CAB2}\\left( t \\right)}{\\mathrm{d}t} = - k_{4p} \\mathrm{CAB2}\\left( t \\right) + k_{4m} C\\left( t \\right) \\mathrm{AB2}\\left( t \\right) - \\left( C\\left( t \\right) \\right)^{2} k_{3m} \\mathrm{CAB2}\\left( t \\right) + \\left( \\mathrm{CB}\\left( t \\right) \\right)^{2} k_{3p} \\mathrm{CA}\\left( t \\right)\n\\end{equation}$$

\n\n\n
  • Dirichlet boundary conditions at \\(x=1\\) :

$$\\begin{aligned}\n\tc_A&=1\\\\\n c_B&=1\\\\\n\tc_{AB2}&=0\n\\end{aligned}$$

\n\n\n

Finally, we set all initial concentrations to zero besides of the catalyst concenration (number of catalyst sites) \\(c_C|_{t=0}=C_0=1\\).

\n\n\n

Implementation

\n\n\n

Surface reaction network

\n\n\n

Define a reaction network under the assumption that the supply of A and B comes from the transport and does not need to be specified.

\n\n
rnv = @reaction_network rnv begin\n    @combinatoric_ratelaws false\n    (k_1p, k_1m), A + C <--> CA\n    (k_2p, k_2m), B + C <--> CB\n    (k_3p, k_3m), CA + 2CB <--> CAB2 + 2C\n    (k_4p, k_4m), CAB2 <--> AB2 + C\nend
\n

$$\\begin{align*}\n\\mathrm{A} + \\mathrm{C} &\\xrightleftharpoons[k_{1m}]{k_{1p}} \\mathrm{CA} \\\\\n\\mathrm{B} + \\mathrm{C} &\\xrightleftharpoons[k_{2m}]{k_{2p}} \\mathrm{CB} \\\\\n\\mathrm{CA} + 2 \\mathrm{CB} &\\xrightleftharpoons[k_{3m}]{k_{3p}} \\mathrm{CAB2} + 2 \\mathrm{C} \\\\\n\\mathrm{CAB2} &\\xrightleftharpoons[k_{4m}]{k_{4p}} \\mathrm{AB2} + \\mathrm{C} \n \\end{align*}$$

\n\n
odesys = convert(ODESystem, rnv)
\n

$$\\begin{align}\n\\frac{\\mathrm{d} A\\left( t \\right)}{\\mathrm{d}t} &= k_{1m} \\mathrm{CA}\\left( t \\right) - k_{1p} A\\left( t \\right) C\\left( t \\right) \\\\\n\\frac{\\mathrm{d} C\\left( t \\right)}{\\mathrm{d}t} &= k_{1m} \\mathrm{CA}\\left( t \\right) + k_{2m} \\mathrm{CB}\\left( t \\right) + k_{4p} \\mathrm{CAB2}\\left( t \\right) - k_{1p} A\\left( t \\right) C\\left( t \\right) - k_{2p} B\\left( t \\right) C\\left( t \\right) - k_{4m} C\\left( t \\right) \\mathrm{AB2}\\left( t \\right) - 2 \\left( C\\left( t \\right) \\right)^{2} k_{3m} \\mathrm{CAB2}\\left( t \\right) + 2 \\left( \\mathrm{CB}\\left( t \\right) \\right)^{2} k_{3p} \\mathrm{CA}\\left( t \\right) \\\\\n\\frac{\\mathrm{d} \\mathrm{CA}\\left( t \\right)}{\\mathrm{d}t} &= - k_{1m} \\mathrm{CA}\\left( t \\right) + k_{1p} A\\left( t \\right) C\\left( t \\right) + \\left( C\\left( t \\right) \\right)^{2} k_{3m} \\mathrm{CAB2}\\left( t \\right) - \\left( \\mathrm{CB}\\left( t \\right) \\right)^{2} k_{3p} \\mathrm{CA}\\left( t \\right) \\\\\n\\frac{\\mathrm{d} B\\left( t \\right)}{\\mathrm{d}t} &= k_{2m} \\mathrm{CB}\\left( t \\right) - k_{2p} B\\left( t \\right) C\\left( t \\right) \\\\\n\\frac{\\mathrm{d} \\mathrm{CB}\\left( t \\right)}{\\mathrm{d}t} &= - k_{2m} \\mathrm{CB}\\left( t \\right) + k_{2p} B\\left( t \\right) C\\left( t \\right) + 2 \\left( C\\left( t \\right) \\right)^{2} k_{3m} \\mathrm{CAB2}\\left( t \\right) - 2 \\left( \\mathrm{CB}\\left( t \\right) \\right)^{2} k_{3p} \\mathrm{CA}\\left( t \\right) \\\\\n\\frac{\\mathrm{d} \\mathrm{CAB2}\\left( t \\right)}{\\mathrm{d}t} &= - k_{4p} \\mathrm{CAB2}\\left( t \\right) + k_{4m} C\\left( t \\right) \\mathrm{AB2}\\left( t \\right) - \\left( C\\left( t \\right) \\right)^{2} k_{3m} \\mathrm{CAB2}\\left( t \\right) + \\left( \\mathrm{CB}\\left( t \\right) \\right)^{2} k_{3p} \\mathrm{CA}\\left( t \\right) \\\\\n\\frac{\\mathrm{d} \\mathrm{AB2}\\left( t \\right)}{\\mathrm{d}t} &= k_{4p} \\mathrm{CAB2}\\left( t \\right) - k_{4m} C\\left( t \\right) \\mathrm{AB2}\\left( t \\right)\n\\end{align}$$

\n\n\n

For coupling with VoronoiFVM we need species numbers which need to correspond to the species in our network:

\n\n
begin\n    smap = speciesmap(rnv)\n    const iA = smap[rnv.A]\n    const iB = smap[rnv.B]\n    const iC = smap[rnv.C]\n    const iCA = smap[rnv.CA]\n    const iCB = smap[rnv.CB]\n    const iCAB2 = smap[rnv.CAB2]\n    const iAB2 = smap[rnv.AB2]\nend;
\n\n\n\n

Grid:

\n\n
grid = simplexgrid(0:0.01:1)
\n
ExtendableGrids.ExtendableGrid{Float64, Int32}\n      dim =       1\n   nnodes =     101\n   ncells =     100\n  nbfaces =       2
\n\n
gridplot(grid, size = (600, 100))
\n\n\n\n

The grid has two boundary regions: region 1 at x=0 and region 2 at x=1.

\n\n\n

Reaction parameters:

\n\n
pcat = (\n    k_1p = 50, k_1m = 0.1,\n    k_2p = 50, k_2m = 0.1,\n    k_3p = 10, k_3m = 0.1,\n    k_4p = 50, k_4m = 0.1,\n)
\n
(k_1p = 50, k_1m = 0.1, k_2p = 50, k_2m = 0.1, k_3p = 10, k_3m = 0.1, k_4p = 50, k_4m = 0.1)
\n\n\n

Parameters for the VoronoiFVM system:

\n\n
params = (\n    D_A = 1.0,\n    D_B = 1.0,\n    D_AB2 = 1.0,\n    pcat = pcat,\n)
\n
(D_A = 1.0, D_B = 1.0, D_AB2 = 1.0, pcat = (k_1p = 50, k_1m = 0.1, k_2p = 50, k_2m = 0.1, k_3p = 10, k_3m = 0.1, k_4p = 50, k_4m = 0.1))
\n\n\n

Initial values for the reaction network (needed only for the definition of the ODE problem)

\n\n
C0 = 1.0
\n
1.0
\n\n
uv_ini = (A = 0, B = 0, CA = 0, CB = 0, CAB2 = 0, AB2 = 0, C = C0)
\n
(A = 0, B = 0, CA = 0, CB = 0, CAB2 = 0, AB2 = 0, C = 1.0)
\n\n
tvend = 200.0
\n
200.0
\n\n
const probv = ODEProblem(rnv, Dict(pairs(uv_ini)), (0, tvend), Dict(pairs(pcat)))
\n
ODEProblem with uType Vector{Float64} and tType Float64. In-place: true\ntimespan: (0.0, 200.0)\nu0: 7-element Vector{Float64}:\n 0.0\n 1.0\n 0.0\n 0.0\n 0.0\n 0.0\n 0.0
\n\n\n

Callback functions for VoronoiFVM

\n\n\n

First, define flux and storage functions for the bulk process:

\n\n
function storage(y, u, node, p)\n    y[iA] = u[iA]\n    y[iB] = u[iB]\n    return y[iAB2] = u[iAB2]\nend
\n
storage (generic function with 1 method)
\n\n
function flux(y, u, edge, p)\n    (; D_A, D_B, D_AB2) = p\n    y[iA] = D_A * (u[iA, 1] - u[iA, 2])\n    y[iB] = D_B * (u[iB, 1] - u[iB, 2])\n    return y[iAB2] = D_A * (u[iAB2, 1] - u[iAB2, 2])\nend
\n
flux (generic function with 1 method)
\n\n\n

Storage term for the surface reaction:

\n\n
function bstorage(y, u, bnode, p)\n    y[iC] = u[iC]\n    y[iCA] = u[iCA]\n    y[iCB] = u[iCB]\n    return y[iCAB2] = u[iCAB2]\nend
\n
bstorage (generic function with 1 method)
\n\n\n

Catalytic reaction. Here we use the right hand side function of the ODE problem generated above. In VoronoiFVM, reaction term are a the left hand side, so we need to multiply by -1.

Note that we need to pass the parameter record as generated for the ODE problem instead of pcat.

\n\n
function catreaction(f, u, bnode, p)\n    probv.f(f, u, probv.p, bnode.time)\n    for i in 1:length(f)\n        f[i] = -f[i]\n    end\n    return\nend
\n
catreaction (generic function with 1 method)
\n\n\n

Define the Dirichlet boundary condition at x=1 (region 2):

\n\n
function bulkbc(f, u, bnode, p)\n    v = ramp(bnode.time; du = (0.0, 1.0), dt = (0.0, 0.01))\n    boundary_dirichlet!(f, u, bnode; species = iA, value = v, region = 2)\n    boundary_dirichlet!(f, u, bnode; species = iB, value = v, region = 2)\n    return boundary_dirichlet!(f, u, bnode; species = iAB2, value = 0, region = 2)\nend
\n
bulkbc (generic function with 1 method)
\n\n\n

Dispatch the boundary conditions

\n\n
function breaction(f, u, bnode, p)\n    return if bnode.region == 1\n        catreaction(f, u, bnode, p)\n    else\n        bulkbc(f, u, bnode, p)\n    end\nend
\n
breaction (generic function with 1 method)
\n\n\n

Coupled transport-reaction system

\n\n\n

Define a VoronoiFVM system from grid, params and the callback functions and enable the bulk and boundary species. unknown_storage = :sparse means that the solution is stored as a nspecies x nnodes sparse matrix in order to take into account that the surface species are non-existent in the bulk. unknown_storage = :dense would store a full matrix and solve dummy equations for the surface species values in the bulk.

\n\n
begin\n    sys = VoronoiFVM.System(\n        grid; data = params,\n        flux, breaction, bstorage, storage,\n        unknown_storage = :sparse\n    )\n    enable_species!(sys, iA, [1])\n    enable_species!(sys, iB, [1])\n    enable_species!(sys, iAB2, [1])\n\n    enable_boundary_species!(sys, iC, [1])\n    enable_boundary_species!(sys, iCA, [1])\n    enable_boundary_species!(sys, iCB, [1])\n    enable_boundary_species!(sys, iCAB2, [1])\nend;
\n\n\n\n

Define an initial value for sys:

\n\n
begin\n    u0 = VoronoiFVM.unknowns(sys; inival = 0)\n    u0[iC, 1] = C0\nend;
\n\n\n\n

Solution

\n\n\n

Solve the time evolution

\n\n
tsol = solve(sys; inival = u0, times = (1.0e-4, tvend));
\n\n\n\n

t:

\n\n
let\n    t_plot = round(10^log_t_plot; sigdigits = 3)\n    vis = GridVisualizer(; size = (600, 300), flimits = (0, 1), title = \"Bulk concentrations: t=$t_plot\", legend = :lt)\n    sol = tsol(t_plot)\n    scalarplot!(vis, grid, sol[iA, :]; color = :red, label = \"A\")\n    scalarplot!(vis, grid, sol[iB, :]; color = :green, label = \"B\", clear = false)\n    scalarplot!(vis, grid, sol[iAB2, :]; color = :blue, label = \"AB2\", clear = false)\n    reveal(vis)\nend
\n\n\n
Ctotalv = tsol[iC, 1, :] + tsol[iCA, 1, :] + tsol[iCB, 1, :] + tsol[iCAB2, 1, :]
\n
248-element Vector{Float64}:\n 1.0\n 1.0\n 0.9999999999999999\n 1.0\n 1.0\n 0.9999999999999999\n 0.9999999999999999\n ⋮\n 1.0000000000000002\n 1.0000000000000002\n 1.0000000000000002\n 1.0000000000000002\n 1.0000000000000002\n 1.0000000000000002
\n\n
@test Ctotalv ≈ ones(length(tsol))
\n
Test Passed
\n\n
let\n    vis = GridVisualizer(;\n        size = (600, 300),\n        xlabel = \"t\",\n        flimits = (0, 1), xlimits = (1.0e-3, tvend),\n        legend = :lt, title = \"Concentrations at x=0\", xscale = :log10\n    )\n    t = tsol.t\n    scalarplot!(vis, t, tsol[iA, 1, :]; color = :darkred, label = \"A\")\n    scalarplot!(vis, t, tsol[iCA, 1, :]; color = :red, label = \"CA\")\n    scalarplot!(vis, t, tsol[iB, 1, :]; color = :darkgreen, label = \"B\")\n    scalarplot!(vis, t, tsol[iCB, 1, :]; color = :green, label = \"CB\")\n    scalarplot!(vis, t, tsol[iAB2, 1, :]; color = :darkblue, label = \"AB2\")\n    scalarplot!(vis, t, tsol[iCAB2, 1, :]; color = :blue, label = \"CAB2\")\n    scalarplot!(vis, t, tsol[iC, 1, :] / C0; color = :orange, label = \"C/C0\")\n    scalarplot!(vis, t, Ctotalv / C0; color = :darkorange, label = \"Ctot/C0\")\n\n    reveal(vis)\nend
\n\n\n","category":"page"},{"location":"plutostatichtml_examples/heterogeneous-catalysis/#Appendix","page":"Coupling with Catalyst.jl","title":"Appendix","text":"","category":"section"},{"location":"plutostatichtml_examples/heterogeneous-catalysis/","page":"Coupling with Catalyst.jl","title":"Coupling with Catalyst.jl","text":"
\n
\n\n
# Some tricks helping to run the notebook during VoronoiFVM.jl CI\nbegin\n    doplots = !haskey(ENV, \"VORONOIFVM_RUNTESTS\")\n    import Pkg as _Pkg\n    haskey(ENV, \"PLUTO_PROJECT\") && _Pkg.activate(ENV[\"PLUTO_PROJECT\"])\n    using Revise\nend
\n\n
\n

Built with Julia 1.11.1 and

\n\n
\n\n","category":"page"},{"location":"plutostatichtml_examples/heterogeneous-catalysis/","page":"Coupling with Catalyst.jl","title":"Coupling with Catalyst.jl","text":"EditURL = \"https://github.com/WIAS-PDELib/VoronoiFVM.jl/blob/master/nothing\"","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\n\nusing ExtendableGrids\nusing GridVisualize\nusing LinearAlgebra\nusing AMGCLWrap\nusing Random\nusing StrideArraysCore: @gc_preserve, StrideArray, StaticInt, PtrArray\nusing LinearSolve, ExtendableSparse\nusing ExtendableSparse: ILUZeroPreconBuilder\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 in 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 in 1:nspec(data)\n for jspec in 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 in 1:nspec(data)\n f[ispec] = du[ispec]\n end\n return\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 in 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 in 1:nspec(data)\n for jspec in 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 in 1:nspec(data)\n f[ispec] = du[ispec]\n end\n return nothing\nend\n\nfunction bcondition(f, u, node, data)\n for species in 1:nspec(data)\n boundary_dirichlet!(\n f, u, node; species, region = data.diribc[1],\n value = species % 2\n )\n boundary_dirichlet!(\n f, u, node; species, region = data.diribc[2],\n value = 1 - species % 2\n )\n end\n return nothing\nend\n\nfunction main(;\n 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 )\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 in 1:nspec\n DBinary[ispec, ispec] = 0\n end\n\n DKnudsen = fill(1.0, nspec)\n\n if dim == 1\n grid = simplexgrid(X)\n diribc = [1, 2]\n elseif dim == 2\n grid = simplexgrid(X, X)\n diribc = [4, 2]\n else\n grid = simplexgrid(X, X, X)\n diribc = [4, 2]\n end\n\n function storage(f, u, node, data)\n f .= u\n return nothing\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 if verbose\n @info \"Strategy: $(strategy)\"\n end\n if !isnothing(strategy) && hasproperty(strategy, :precs)\n if isa(strategy.precs, BlockPreconBuilder)\n strategy.precs.partitioning = A -> partitioning(sys, Equationwise())\n end\n if isa(strategy.precs, ILUZeroPreconBuilder) && strategy.precs.blocksize != 1\n strategy.precs.blocksize = nspec\n end\n end\n control = SolverControl(method_linear = strategy)\n control.maxiters = 500\n if verbose\n @info control.method_linear\n end\n u = solve(sys; verbose, control, log = true)\n if verbose\n @show norm(u)\n end\n return norm(u)\nend\n\nusing Test\nfunction runtests()\n strategies = [\n UMFPACKFactorization(),\n KrylovJL_GMRES(precs = LinearSolvePreconBuilder(UMFPACKFactorization())),\n KrylovJL_GMRES(precs = BlockPreconBuilder(precs = LinearSolvePreconBuilder(UMFPACKFactorization()))),\n KrylovJL_GMRES(precs = BlockPreconBuilder(precs = AMGPreconBuilder())),\n KrylovJL_BICGSTAB(precs = BlockPreconBuilder(precs = AMGPreconBuilder())),\n KrylovJL_GMRES(precs = ILUZeroPreconBuilder()),\n KrylovJL_GMRES(precs = BlockPreconBuilder(precs = ILUZeroPreconBuilder())),\n KrylovJL_GMRES(precs = ILUZeroPreconBuilder(blocksize = 5)),\n ]\n\n val1D = 4.788926530387466\n val2D = 15.883072449873742\n val3D = 52.67819183426213\n\n\n @test main(; dim = 1, assembly = :cellwise) ≈ val1D\n @test main(; dim = 2, assembly = :cellwise) ≈ val2D\n @test main(; dim = 3, assembly = :cellwise) ≈ val3D\n @test main(; dim = 1, flux = :flux_marray, assembly = :cellwise) ≈ val1D\n @test main(; dim = 2, flux = :flux_marray, assembly = :cellwise) ≈ val2D\n @test main(; dim = 3, flux = :flux_marray, assembly = :cellwise) ≈ val3D\n @test all(map(strategy -> main(; dim = 2, flux = :flux_marray, strategy) ≈ val2D, strategies))\n return nothing\nend\n\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 in 1:(N - 1)\n xcoord = glue(xcoord, XX .+ i)\n end\n grid2 = simplexgrid(xcoord)\n for i in 1:N\n cellmask!(grid2, [i - 1], [i], i)\n end\n for i in 1:(N - 1)\n bfacemask!(grid2, [i], [i], i + 2)\n end\n\n params = zeros(2, num_cellregions(grid2))\n for i in 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 in 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 in carrierList\n params2[icc] = 2\n end\n\n for i in 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 in 1:num_cellregions(grid2)\n @assert params[cspec, i] == i\n @assert params[dspec, i] == 10 * i\n end\n\n for i in 1:num_cellregions(grid2)\n params[cspec, i] = -i\n params[dspec, i] = -10 * i\n end\n\n for i in 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, data)\n f[dspec] = u[dspec, 1] - u[dspec, 2]\n f[cspec] = u[cspec, 1] - u[cspec, 2]\n return nothing\n end\n\n d1 = 1\n q1 = 0.2\n\n function breaction(f, u, bnode, data)","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 return nothing\n end\n\n physics!(\n system, VoronoiFVM.Physics(;\n flux = flux,\n breaction = breaction\n )\n )\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!(\n vis, subgrids[i], dvws[i]; flimits = (-0.5, 1.5), clear = false,\n color = :red\n )\n scalarplot!(\n vis, subgrids[i], cvws[i]; flimits = (-0.5, 1.5), clear = false,\n color = :green\n )\n end\n reveal(vis)\n I = integrate(system, system.physics.storage, U)\n return 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\n return nothing\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 n = 10,\n Plotter = nothing,\n verbose = false,\n tend = 1,\n unknown_storage = :sparse,\n autodetect_sparsity = true\n )\n h = 1.0 / convert(Float64, n)\n X = collect(0.0:h:1.0)\n N = length(X)\n\n grid = simplexgrid(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, data)\n f[iA] = D_A * (u[iA, 1] - u[iA, 2])\n f[iB] = D_B * (u[iB, 1] - u[iB, 2])\n return nothing\n end\n\n # Storage term of species A and B\n function storage!(f, u, node, data)\n f[iA] = u[iA]\n f[iB] = u[iB]\n return nothing\n end\n\n # Source term for species a around 0.5\n function source!(f, node, data)\n x1 = node[1] - 0.5\n f[iA] = exp(-100 * x1^2)\n return nothing\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, data)\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 return nothing\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 return nothing\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 return sparsity\n end\n\n if autodetect_sparsity\n physics = VoronoiFVM.Physics(;\n breaction = breaction!,\n generic = generic_operator!,\n flux = flux!,\n storage = storage!,\n source = source!\n )\n else\n physics = VoronoiFVM.Physics(;\n breaction = breaction!,\n generic = generic_operator!,\n generic_sparsity = generic_operator_sparsity,\n flux = flux!,\n storage = storage!,\n source = source!\n )\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!(\n p[1, 1], grid, U[iA, :]; label = \"[A]\",\n title = @sprintf(\n \"max_A=%.5f max_B=%.5f u_C=%.5f\", maximum(U[iA, :]),\n maximum(U[iB, :]), u_C[end]\n ), color = :red\n )\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\n return nothing\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 = \"png\", 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, data) = y[1] = u[1, 1] - u[1, 2];
\n\n\n
function reaction(y, u, node, data)\n    eplus = exp(u[1])\n    eminus = 1 / eplus\n    y[1] = eplus - eminus\n    return nothing\nend
\n
reaction (generic function with 1 method)
\n\n
function bc(y, u, node, data)\n    boundary_dirichlet!(y, u, node; region = 1, value = 100)\n    boundary_dirichlet!(y, u, node; region = 2, value = 0.0)\n    return nothing\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(sol)\nend;
\n\n\n
scalarplot(\n    system,\n    sol;\n    resolution = (500, 200),\n    xlabel = \"x\",\n    ylable = \"y\",\n    title = \"solution\"\n)
\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(sol).

\n\n\n

The history can be plotted:

\n\n
function plothistory(h)\n    return scalarplot(\n        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\"\n    )\nend;
\n\n\n
plothistory(hist)
\n\n\n\n

History can be summarized:

\n\n
summary(hist)
\n
(seconds = 14.6, tasm = 12.0, tlinsolve = 0.832, 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_initial = 0.15, damp_growth = 1.5)\n    hist1 = history(sol1)\nend
\n
28-element NewtonSolverHistory:\n 97.81584868964667\n  9.162760369739365\n  0.9999999998511512\n  0.9999999997401952\n  0.999999999441547\n  0.9999999983981809\n  0.9999999941837444\n  ⋮\n  0.7769510871365355\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.318, tasm = 0.296, tlinsolve = 0.0216, 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, data)\n    boundary_dirichlet!(y, u, node; region = 1, value = 100 * embedparam(node))\n    boundary_dirichlet!(y, u, node; region = 2, value = 0)\n    return nothing\nend;
\n\n\n
system2 = VoronoiFVM.System(\n    X;\n    flux = flux,\n    reaction = function (y, u, node, data)\n        reaction(y, u, node, data)\n\n        y[1] = y[1] * embedparam(node)\n        return nothing\n    end,\n    bcondition = pbc,\n    species = 1,\n);
\n\n\n
begin\n    sol2 = solve(\n        system2;\n        inival = 0,\n        log = true,\n        embed = (0, 1),\n        Δp = 0.1,\n        Δp_grow = 1.2,\n        Δu_opt = 15\n    )\n    history2 = history(sol2)\nend
\n
9-element TransientSolverHistory:\n [0.0]\n [9.989343055885163, 0.98148159332474, 0.8053267053718508, 0.34118506593594755, 0.03588566608807058, 0.00030501510006535184, 2.0656145341786982e-8, 3.5058194770979925e-15]\n [11.34146393762356, 0.9997315206431725, 0.9990808258671514, 0.9966384496135858, 0.9877041414138586, 0.9368309534589466, 0.7284028335581785, 0.3000679588018384, 0.034624159753369424, 0.00039224137194873325, 5.021311921932754e-8, 1.3972756883275171e-15]\n [1.5085724152572166, 0.4205506561491683, 0.10826945171253802, 0.005760884131907183, 1.5240635820497445e-5, 1.0633964457291663e-10]\n [0.32730498752976783, 0.04842614744099976, 0.001078162227433007, 4.82292409015689e-7, 8.958093218077431e-14]\n [0.18959174175719817, 0.01947525749984668, 0.00017208751333215453, 1.2283470243608807e-8, 2.6864715379034067e-15]\n [0.22151709368816527, 0.01350594149309612, 8.295254108143132e-5, 2.8747465562928216e-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.9999999995955116, 0.9999999983507291, 0.9999999940224222, 0.9999999796890737, 0.9999999337470156, 0.9999997898900223, 0.9999993472708903, 0.9999980039123316, 0.9999939712056518  …  0.9888382056759235, 0.9682851805189645, 0.912237194563469, 0.7725313381228643, 0.49505826421414995, 0.16481352791231269, 0.014468420882723345, 0.00010072395527063066, 4.834945474226937e-9, 1.0894463251846006e-15]
\n\n
summary(history2)
\n
(seconds = 0.9, tasm = 0.789, tlinsolve = 0.109, 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.u[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.u[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/#SolverControl","page":"Nonlinear solver control","title":"SolverControl","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...)

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.

  • 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:

    • UMFPACKFactorization() for sparse matrices with Float64

    • SparspakFactorization() for sparse matrices with general number types.

    • Defaults from LinearSolve.jl for tridiagonal and banded matrices

    Users should experiment with what works best for their problem.

    For an overeview on possible alternatives, see 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 ? Translates to reuse_precs=!keepcurrent_linear for LinearSolve.

  • Δ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 embedding 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.11.1 and

\nCairoMakie 0.12.16
\nExtendableGrids 1.9.2
\nGridVisualize 1.7.0
\nLinearAlgebra 1.11.0
\nPkg 1.11.0
\nPlutoUI 0.7.60
\nRevise 3.5.18
\nTest 1.11.0
\nVoronoiFVM 1.25.1\n
\n\n","category":"page"},{"location":"plutostatichtml_examples/nonlinear-solvers/","page":"Nonlinear solver control","title":"Nonlinear solver control","text":"EditURL = \"https://github.com/WIAS-PDELib/VoronoiFVM.jl/blob/master/nothing\"","category":"page"},{"location":"physics/#Physics-and-special-functions","page":"Physics & special functions","title":"Physics & special functions","text":"","category":"section"},{"location":"physics/#Physics-callbacks","page":"Physics & special functions","title":"Physics callbacks","text":"","category":"section"},{"location":"physics/","page":"Physics & special functions","title":"Physics & special functions","text":"VoronoiFVM.AbstractPhysics\nVoronoiFVM.Physics\nVoronoiFVM.Physics(;kwargs...)\nVoronoiFVM.generic_operator_sparsity!\nBase.show(io::IO,physics::VoronoiFVM.AbstractPhysics)\nVoronoiFVM.AbstractData\nBase.show(::IO, ::MIME{Symbol(\"text/plain\")}, ::VoronoiFVM.AbstractData)\nBase.copy!(::VoronoiFVM.AbstractData{Tv}, ::VoronoiFVM.AbstractData{Tu}) where {Tv,Tu}","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,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,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,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,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,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,data)\n\nbreaction::Function: Boundary reaction term: 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,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,data) Similar to storage, but restricted to the inner or outer boundaries.\n\nboutflow::Function: Outflow boundary term 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 boundary 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. Meaningless & deprecated.\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/#VoronoiFVM.generic_operator_sparsity!","page":"Physics & special functions","title":"VoronoiFVM.generic_operator_sparsity!","text":"generic_operator_sparsity!(system, sparsematrix)\n\n\nSet generic operator sparsity, in the case where a generic operator has been defined in physics.\n\n\n\n\n\n","category":"function"},{"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/#VoronoiFVM.AbstractData","page":"Physics & special functions","title":"VoronoiFVM.AbstractData","text":"abstract type AbstractData{Tv}\n\nAbstract type for user data.\n\nIt is possible but not necessary to make user data a subtype of AbstractData and get a prettyprinting show method.\n\n\n\n\n\n","category":"type"},{"location":"physics/#Base.show-Tuple{IO, MIME{Symbol(\"text/plain\")}, VoronoiFVM.AbstractData}","page":"Physics & special functions","title":"Base.show","text":"show(\n io::IO,\n _::MIME{Symbol(\"text/plain\")},\n this::VoronoiFVM.AbstractData\n)\n\n\nPretty print AbstractData\n\n\n\n\n\n","category":"method"},{"location":"physics/#Base.copy!-Union{Tuple{Tu}, Tuple{Tv}, Tuple{VoronoiFVM.AbstractData{Tv}, VoronoiFVM.AbstractData{Tu}}} where {Tv, Tu}","page":"Physics & special functions","title":"Base.copy!","text":"copy!(to::AbstractData, from::AbstractData)\n\nCopy AbstractData.\n\n\n\n\n\n","category":"method"},{"location":"physics/#Handling-boundary-conditions","page":"Physics & special functions","title":"Handling boundary conditions","text":"","category":"section"},{"location":"physics/","page":"Physics & special functions","title":"Physics & special functions","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":"physics/","page":"Physics & special functions","title":"Physics & special functions","text":"boundary_dirichlet!(y,u,bnode::AbstractGeometryItem,ispec,ireg,val)\nboundary_dirichlet!(y,u,bnode::AbstractGeometryItem;kwargs...)\nboundary_neumann!(y,u,bnode::AbstractGeometryItem,ispec,ireg,val)\nboundary_neumann!(y,u,bnode::AbstractGeometryItem;kwargs...)\nboundary_robin!(y,u,bnode::AbstractGeometryItem,ispec,ireg,fac,val)\nboundary_robin!(y,u,bnode::AbstractGeometryItem;kwargs...)\nramp","category":"page"},{"location":"physics/#VoronoiFVM.boundary_dirichlet!-Tuple{Any, Any, AbstractGeometryItem, Any, Any, Any}","page":"Physics & special functions","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":"physics/#VoronoiFVM.boundary_dirichlet!-Tuple{Any, Any, AbstractGeometryItem}","page":"Physics & special functions","title":"VoronoiFVM.boundary_dirichlet!","text":" boundary_dirichlet!(y,u,bnode; 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":"physics/#VoronoiFVM.boundary_neumann!-Tuple{Any, Any, AbstractGeometryItem, Any, Any, Any}","page":"Physics & special functions","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":"physics/#VoronoiFVM.boundary_neumann!-Tuple{Any, Any, AbstractGeometryItem}","page":"Physics & special functions","title":"VoronoiFVM.boundary_neumann!","text":" boundary_neumann!(y,u,bnode; 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":"physics/#VoronoiFVM.boundary_robin!-Tuple{Any, Any, AbstractGeometryItem, Vararg{Any, 4}}","page":"Physics & special functions","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":"physics/#VoronoiFVM.boundary_robin!-Tuple{Any, Any, AbstractGeometryItem}","page":"Physics & special functions","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":"physics/#VoronoiFVM.ramp","page":"Physics & special functions","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":"physics/#Outflow-boundary-conditions","page":"Physics & special functions","title":"Outflow boundary conditions","text":"","category":"section"},{"location":"physics/","page":"Physics & special functions","title":"Physics & special functions","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":"physics/","page":"Physics & special functions","title":"Physics & special functions","text":"hasoutflownode\nisoutflownode\noutflownode\ncalc_divergences","category":"page"},{"location":"physics/#VoronoiFVM.hasoutflownode","page":"Physics & special functions","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":"physics/#VoronoiFVM.isoutflownode","page":"Physics & special functions","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":"physics/#VoronoiFVM.outflownode","page":"Physics & special functions","title":"VoronoiFVM.outflownode","text":"outflownode(edge)\n\nReturn outflow node of edge (1 or 2).\n\n\n\n\n\n","category":"function"},{"location":"physics/#VoronoiFVM.calc_divergences","page":"Physics & special functions","title":"VoronoiFVM.calc_divergences","text":"calc_divergences(sys, evelo, bfvelo)\n\n\nCalculate for each Voronoi cell associated with a node of sys.grid the divergence of the velocity field used to obtain evelo and bfvelo via edgevelocities and bfacevelocities by means of summing all evelos and bfvelos per Voronoi cell.\n\n\n\n\n\n","category":"function"},{"location":"physics/#Coupling-to-flow","page":"Physics & special functions","title":"Coupling to flow","text":"","category":"section"},{"location":"physics/","page":"Physics & special functions","title":"Physics & special functions","text":"edgevelocities\nbfacevelocities\nbfacenodefactors","category":"page"},{"location":"physics/#VoronoiFVM.edgevelocities","page":"Physics & special functions","title":"VoronoiFVM.edgevelocities","text":"edgevelocities(grid, velofunc; kwargs...)\n\n\nProject velocity onto grid edges. That is, we compute the path integrals of the given velofunc along the Voronoi cell edges as provided by integrate.\n\n\n\n\n\nedgevelocities(grid, vel; kwargs...)\n\n\nCompute VoronoiFVM.edgevelocities for a finite element flow field computed by ExtendableFEM.\n\n\n\n\n\n","category":"function"},{"location":"physics/#VoronoiFVM.bfacevelocities","page":"Physics & special functions","title":"VoronoiFVM.bfacevelocities","text":"bfacevelocities(grid, velofunc; kwargs...)\n\n\nSimilar to edgevelocities, but for boundary faces.\n\n\n\n\n\nbfacevelocities(grid, vel; kwargs...)\n\n\nCompute VoronoiFVM.bfacevelocities for a finite element flow field computed by ExtendableFEM.\n\n\n\n\n\n","category":"function"},{"location":"physics/#VoronoiFVM.bfacenodefactors","page":"Physics & special functions","title":"VoronoiFVM.bfacenodefactors","text":"bfacenodefactors(sys)\n\n\nNode form factors per boundary face\n\n\n\n\n\n","category":"function"},{"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\nVoronoiFVM.time\nVoronoiFVM.region\nVoronoiFVM.parameters\nVoronoiFVM.embedparam\nVoronoiFVM.project\nVoronoiFVM.edgelength\nVoronoiFVM.meas","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 (deprecated)\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: Parameters (deprecated)\n\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: Parameters (deprecated)\n\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: Parameters (deprecated)\n\nfac::Float64\n\n\n\n\n\n","category":"type"},{"location":"physics/#VoronoiFVM.time","page":"Physics & special functions","title":"VoronoiFVM.time","text":"time(edge_or_node)\n\nReturn actual simulation time stored in node or edge\n\n\n\n\n\n","category":"function"},{"location":"physics/#VoronoiFVM.region","page":"Physics & special functions","title":"VoronoiFVM.region","text":"region(edgeornode)\n\nReturn region number node or edge is belonging to\n\n\n\n\n\n","category":"function"},{"location":"physics/#VoronoiFVM.parameters","page":"Physics & special functions","title":"VoronoiFVM.parameters","text":"parameters(edge_or_node)\n\nReturn abstract vector of parameters passed via vector of unknowns. This allows differentiation with respect to these parameters.\n\n\n\n\n\n","category":"function"},{"location":"physics/#VoronoiFVM.embedparam","page":"Physics & special functions","title":"VoronoiFVM.embedparam","text":"embedparam(edge_or_node)\n\nReturn embedding parameter stored in node or edge\n\n\n\n\n\n","category":"function"},{"location":"physics/#VoronoiFVM.project","page":"Physics & special functions","title":"VoronoiFVM.project","text":"project(edge, vector)\n\nProject d-vector onto d-dimensional vector, i.e. calculate the dot product of vector with the difference of the edge end coordinates.\n\n\n\n\n\n","category":"function"},{"location":"physics/#VoronoiFVM.edgelength","page":"Physics & special functions","title":"VoronoiFVM.edgelength","text":"edgelength(edge)\n\nReturn length of edge\n\n\n\n\n\n","category":"function"},{"location":"physics/#VoronoiFVM.meas","page":"Physics & special functions","title":"VoronoiFVM.meas","text":"meas(edge::VoronoiFVM.AbstractEdge) -> Any\n\n\nCalculate the length of an edge. \n\n\n\n\n\n","category":"function"},{"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\nWhile x/expm1(x) appears to be sufficiently accurate for all x!=0, it's derivative calculated via ForwardDiff.jl is not, so we use the polynomial approximation in the interval (-bernoulli_small_threshold, bernoulli_small_threshold). \n\nAlso, see the discussion in #117.\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\nFor the general approach to the implementation, see the discussion in fbernoulli.\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\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\nusing OrdinaryDiffEqRosenbrock\nusing SciMLBase: NoInit\n\nfunction main(;\n n = 10, Plotter = nothing, verbose = false, tend = 1,\n unknown_storage = :sparse, assembly = :edgewise,\n diffeq = false\n )\n h = 1.0 / convert(Float64, n)\n X = collect(0.0:h:1.0)\n N = length(X)\n\n grid = simplexgrid(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, data)\n f[iA] = D_A * (u[iA, 1] - u[iA, 2])\n f[iB] = D_B * (u[iB, 1] - u[iB, 2])\n return nothing\n end\n\n # Storage term of species A and B\n function storage!(f, u, node, data)\n f[iA] = u[iA]\n f[iB] = u[iB]\n return nothing\n end\n\n # Source term for species a around 0.5\n function source!(f, node, data)\n x1 = node[1] - 0.5\n f[iA] = exp(-100 * x1^2)\n return nothing\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, data)\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 return nothing\n end\n\n # This is for the term \\partial_t u_C at the boundary\n function bstorage!(f, u, node, data)\n if node.region == 1\n f[iC] = u[iC]\n end\n return nothing\n end\n\n physics = VoronoiFVM.Physics(;\n breaction = breaction!,\n bstorage = bstorage!,\n flux = flux!,\n storage = storage!,\n source = source!\n )\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 if diffeq\n inival = unknowns(sys, inival = 0)\n problem = ODEProblem(sys, inival, (0, tend))\n # use fixed timesteps just for the purpose of CI\n odesol = solve(problem, Rosenbrock23(); initializealg = NoInit(), dt = tstep, adaptive = false)\n tsol = reshape(odesol, sys)\n else\n control = fixed_timesteps!(VoronoiFVM.NewtonControl(), tstep)\n tsol = solve(sys; inival, times = [0, tend], control, verbose = verbose)\n end\n\n p = GridVisualizer(; Plotter = Plotter, layout = (3, 1), fast = true)\n for it in 1:length(tsol)\n time = tsol.t[it]\n scalarplot!(\n p[1, 1], grid, tsol[iA, :, it]; clear = true,\n title = @sprintf(\"[A]: (%.3f,%.3f)\", extrema(tsol[iA, :, it])...)\n )\n scalarplot!(\n p[2, 1], grid, tsol[iB, :, it]; clear = true,\n title = @sprintf(\"[B]: (%.3f,%.3f)\", extrema(tsol[iB, :, it])...)\n )\n scalarplot!(\n p[3, 1], tsol.t[1:it], tsol[iC, 1, 1:it]; title = @sprintf(\"[C]\"),\n clear = true, show = true\n )\n end\n\n return tsol[iC, 1, end]\nend\n\nusing Test\nfunction runtests()\n testval = 0.87544440641274\n testvaldiffeq = 0.8891082547874963\n @test isapprox(main(; unknown_storage = :sparse, assembly = :edgewise), testval; rtol = 1.0e-12)\n @test isapprox(main(; unknown_storage = :dense, assembly = :edgewise), testval; rtol = 1.0e-12)\n @test isapprox(main(; unknown_storage = :sparse, assembly = :cellwise), testval; rtol = 1.0e-12)\n @test isapprox(main(; unknown_storage = :dense, assembly = :cellwise), testval; rtol = 1.0e-12)\n\n @test isapprox(main(; diffeq = true, unknown_storage = :sparse, assembly = :edgewise), testvaldiffeq; rtol = 1.0e-12)\n @test isapprox(main(; diffeq = true, unknown_storage = :dense, assembly = :edgewise), testvaldiffeq; rtol = 1.0e-12)\n @test isapprox(main(; diffeq = true, unknown_storage = :sparse, assembly = :cellwise), testvaldiffeq; rtol = 1.0e-12)\n @test isapprox(main(; diffeq = true, unknown_storage = :dense, assembly = :cellwise), testvaldiffeq; rtol = 1.0e-12)\n return nothing\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 in 1:(N - 1)\n xcoord = glue(xcoord, XX .+ i)\n end\n grid = simplexgrid(xcoord)\n for i in 1:N\n cellmask!(grid, [i - 1], [i], i)\n end\n for i in 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 return nothing\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 return nothing\n end\n\n physics!(\n sysQ, VoronoiFVM.Physics(;\n data = data,\n flux = fluxQ,\n breaction = breactionQ\n )\n )\n\n ##########################################################\n icc = 1 # for system without AbstractQuantities\n\n function flux!(f, u, edge, data) # analogous as for other system\n f[icc] = u[icc, 1] - u[icc, 2]\n return nothing\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(\n grid; flux = flux!, species = icc,\n unknown_storage = unknown_storage\n )\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!(\n 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 )\n scalarplot!(\n vis[1, 1], subgrids[i], cvws[i]; label = \"cont quantity\",\n clear = false, color = :green\n )\n end\n scalarplot!(\n vis[1, 1], grid, U[icc, :]; label = \"without quantity\", clear = false,\n linestyle = :dot, color = :blue\n )\n\n scalarplot!(\n vis[2, 1], biasval, Idspec; clear = false,\n title = @sprintf(\"IV with rate=%.2f\", data.rate),\n label = \"discont quantity\", color = :red\n )\n scalarplot!(\n vis[2, 1], biasval, Icspec; clear = false, title = \"Current\",\n label = \"cont quantity\", color = :green\n )\n scalarplot!(\n vis[2, 1], biasval, Iicc; clear = false, label = \"discont quantity\",\n linestyle = :dot, color = :blue, show = true\n )\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\n return nothing\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 for systems:","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/#Grid-verification","page":"Postprocessing","title":"Grid verification","text":"","category":"section"},{"location":"post/","page":"Postprocessing","title":"Postprocessing","text":"VoronoiFVM.nondelaunay","category":"page"},{"location":"post/#VoronoiFVM.nondelaunay","page":"Postprocessing","title":"VoronoiFVM.nondelaunay","text":"nondelaunay(grid;tol=1.0e-14, verbose=true)\n\nReturn non-Delaunay edges. Returns a vector of tuples: Each tuple consists of (node1, node2, edge factor, region)\n\nIf the vector has length 0, the grid is boundary conforming Delaunay with respect to each cell region. This means that up to tol, all edge form factors are nonnegative.\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 weighted 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":"VoronoiFVM.integrate(system::VoronoiFVM.AbstractSystem, tf::Vector, U::AbstractMatrix; kwargs...)\nVoronoiFVM.integrate(system::VoronoiFVM.AbstractSystem{Tv, Tc, Ti, Tm}, F::Tf, U::AbstractMatrix{Tu}; boundary = false, data = system.physics.data) where {Tu, Tv, Tc, Ti, Tm, Tf}\nVoronoiFVM.integrate(system::VoronoiFVM.AbstractSystem, U::AbstractMatrix; kwargs...)\nVoronoiFVM.integrate(system::VoronoiFVM.AbstractSystem, F, U::AbstractMatrix; boundary, data)\nVoronoiFVM.edgeintegrate","category":"page"},{"location":"post/#VoronoiFVM.integrate-Tuple{VoronoiFVM.AbstractSystem, Vector, 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 matrix.\n\n\n\n\n\nintegrate(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{Tf}, Tuple{Tm}, Tuple{Ti}, Tuple{Tc}, Tuple{Tv}, Tuple{Tu}, Tuple{VoronoiFVM.AbstractSystem{Tv, Tc, Ti, Tm}, Tf, AbstractMatrix{Tu}}} where {Tu, Tv, Tc, Ti, Tm, Tf}","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 matrix.\n\n\n\n\n\n","category":"method"},{"location":"post/#VoronoiFVM.integrate-Tuple{VoronoiFVM.AbstractSystem, AbstractMatrix}","page":"Postprocessing","title":"VoronoiFVM.integrate","text":"integrate(system,U; boundary=false)\n\nIntegrate solution vector region-wise over domain or boundary. The result is an nspec x nregion matrix.\n\n\n\n\n\n","category":"method"},{"location":"post/#VoronoiFVM.integrate-Tuple{VoronoiFVM.AbstractSystem, Any, 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 matrix.\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 matrix.\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, F,U; data)\n\nReconstruction of an edge function 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. Gallouët, R. Herbin, IMA Journal of Numerical Analysis (2006) 26, 326−353, Lemma 2.4 (also: hal.science/hal-00004840 ).\n\nThe return value is a dim x nspec x nnodes vector.\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\nnodeflux(system, U; data)\n\nReconstruction of the edge flux as vector function on the nodes of the triangulation. See nodeflux(system,F,U;data).\n\nThe 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\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{Tu, Tv}\n\nData structure containing DenseSystem used to calculate test functions for boundary flux calculations.\n\nType parameters:\n\nTu: value type of test functions\nTv: Default value type of system\nsystem::VoronoiFVM.AbstractSystem{Tv} where Tv: Original system\n\nstate::VoronoiFVM.SystemState{Tu, Tp, TMatrix, TSolArray} where {Tu, Tp, TMatrix<:AbstractMatrix{Tu}, TSolArray<:AbstractMatrix{Tu}}: Test function system state\n\ncontrol::SolverControl: Solver control\n\n\n\n\n\n","category":"type"},{"location":"post/#VoronoiFVM.TestFunctionFactory-Union{Tuple{VoronoiFVM.AbstractSystem{Tv}}, Tuple{Tv}} where Tv","page":"Postprocessing","title":"VoronoiFVM.TestFunctionFactory","text":"TestFunctionFactory(\n system::VoronoiFVM.AbstractSystem{Tv};\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, data)\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; data)\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; data)\n\n\nCalculate transient part of test function integral.\n\n\n\n\n\n","category":"method"},{"location":"post/#VoronoiFVM.testfunction-Union{Tuple{Tv}, Tuple{TestFunctionFactory{Tv}, Any, Any}} where Tv","page":"Postprocessing","title":"VoronoiFVM.testfunction","text":"testfunction(\n factory::TestFunctionFactory{Tv},\n bc0,\n bc1\n) -> 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.freqdomain_impedance-Tuple{VoronoiFVM.ImpedanceSystem, Vararg{Any, 7}}","page":"Postprocessing","title":"VoronoiFVM.freqdomain_impedance","text":"freqdomain_impedance(\n impedance_system,\n ω,\n U0,\n excited_spec,\n excited_bc,\n excited_bcval,\n dmeas_stdy,\n dmeas_tran\n)\n\n\nCalculate reciprocal value of impedance.\n\nexcitedspec,excitedbc,excited_bcval are ignored.\n\nwarning: Warning\n\n\nThis is deprecated: use impedance.\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 = simplexgrid(collect(0:h:1))\n\n eps::Vector{Float64} = [1, 1.0e-1]\n\n physics = VoronoiFVM.Physics(\n ; reaction = function (f, u, node, data)\n f[1] = 10 * (u[1] - u[2])\n f[2] = 10 * (u[2] - u[1])\n return nothing\n end, flux = function (f, u, edge, data)\n f[1] = eps[1] * (u[1, 1] - u[1, 2])\n f[2] = eps[2] * (u[2, 1] - u[2, 2])\n return nothing\n end, storage = function (f, u, node, data)\n f[1] = u[1]\n f[2] = u[2]\n return nothing\n end\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 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 @test main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval\n @test main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval\n @test main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval\n return nothing\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, data)\n for ispec in 1:nspec\n f[ispec] = u[ispec, 1] - u[ispec, 2]\n end\n return nothing\n end\n physics = VoronoiFVM.Physics(; flux = flux)\n sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly)\n for ispec in 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 return 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\n return nothing\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 return scalarplot!(vis[2, 1], grid, exact; title = \"exact\", show = true)\nend\n\nfunction flux(f, u, edge, data)\n f[1] = u[1, 1] - u[1, 2]\n return nothing\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 = simplexgrid(R)\n circular_symmetric!(grid)\n source(f, node, data) = 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 return 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 )\n h = 0.1 * 2.0^(-nref)\n R = collect(0:h:r2)\n Z = collect(z1:h:z2)\n grid = simplexgrid(R, Z)\n circular_symmetric!(grid)\n source(f, node, data) = 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 return 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 )\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, data) = 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 return 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 = simplexgrid(R)\n circular_symmetric!(grid)\n source(f, node, data) = 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 return 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 )\n h = 0.1 * 2.0^(-nref)\n R = collect(r1:h:r2)\n Z = collect(z1:h:z2)\n grid = simplexgrid(R, Z)\n circular_symmetric!(grid)\n source(f, node, data) = 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 return 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 = simplexgrid(R)\n spherical_symmetric!(grid)\n source(f, node, data) = 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 return 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 )\n h = 0.1 * 2.0^(-nref)\n R = collect(r1:h:r2)\n grid = simplexgrid(R)\n spherical_symmetric!(grid)\n source(f, node, data) = 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 return 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)\n return nothing\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 environment 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 .\nDuring CI tests, they are run as scripts. For this purpose they are wrapped into temporary modules, and @test macros can be 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]\n return nothing\nend\n\nfunction outflow!(f, u, node, data)\n if node.region == 2\n f[1] = data.v[1] * u[1]\n end\n return nothing\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 = simplexgrid(0:h:1)\n\n data = (v = [v], D = D)\n\n sys = VoronoiFVM.System(\n grid,\n VoronoiFVM.Physics(;\n flux = exponential_flux!, data = data,\n breaction = outflow!\n )\n )\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 in 1:length(tsol.t)\n scalarplot!(\n vis[1, 1], grid, tsol[1, :, i]; flimits = (0, 1),\n title = \"t=$(tsol.t[i])\", show = true\n )\n sleep(0.01)\n end\n return tsol\nend\n\nusing Test\nfunction runtests()\n tsol = main()\n @test maximum(tsol) <= 1.0 && maximum(tsol.u[end]) < 1.0e-20\n return nothing\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: geomspace, simplexgrid\nusing GridVisualize\n\nfunction main(;\n 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\n )","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 = 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 = simplexgrid(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 return nothing\n end\n\n storage = function (f, u, node, data)\n f[1] = data.C * u[1]\n return nothing\n end\n\n reaction = function (f, u, node, data)\n f[1] = data.R * u[1]\n return nothing\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(;\n data = data,\n flux = flux,\n storage = storage,\n reaction = reaction\n )","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 return 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 return 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!(\n p, real(allIxL), imag(allIxL); label = \"exact\", color = :red,\n linestyle = :dot\n )\n scalarplot!(\n p, real(allIL), imag(allIL); label = \"calc\", show = true, clear = false,\n color = :blue, linestyle = :solid\n )\n\n return 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\n return nothing\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 = \"png\", 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, data)\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\n    return nothing\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 in 1:2\n        boundary_dirichlet!(sys, ispec, 1, g_1)\n        boundary_dirichlet!(sys, ispec, 2, g_2)\n    end\n    return 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    return 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!(\n        vis,\n        subgrid1,\n        U1;\n        clear = false,\n        show = false,\n        color = :green,\n        label = \"u1\"\n    )\n    return scalarplot!(\n        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)\n    )\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, data)\n    return nothing\nend
\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, data)\n    if node.region == 3\n        react = k1 * u[1] - k2 * u[2]\n        f[1] = react\n        f[2] = -react\n    end\n    return nothing\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,\n  Int32}}(  \n  grid = ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=1, nnodes=75, ncells=74,\n  nbfaces=3),  \n  physics = Physics(flux=flux!, storage=default_storage, breaction=mal_reaction, ),  \n  num_species = 2)
\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, data)\n    if node.region == 3\n        react = 1.0e10 * (u[1] - u[2])\n        f[1] = react\n        f[2] = -react\n    end\n    return nothing\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, data)\n    if node.region == 3\n        react = 1.0e10 * (u[1] - u[2] - jump)\n        f[1] = react\n        f[2] = -react\n    end\n    return nothing\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, data)\n    if node.region == 3\n        react = k_r * (u[1] * u[2])\n        f[1] = react\n        f[2] = react\n    end\n    return nothing\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, data)\n    if node.region == 3\n        react = (u[1] - u[2]) / d\n        f[1] = react\n        f[2] = -react\n    end\n    return nothing\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 in 1:(N - 1)\n        xcoord = glue(xcoord, XX .+ i)\n    end\n    grid2 = simplexgrid(xcoord)\n    for i in 1:N\n        cellmask!(grid2, [i - 1], [i], i)\n    end\n    for i in 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}}(\n  grid = ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=1, nnodes=61, ncells=60,\n  nbfaces=7),  \n  physics = Physics(storage=default_storage, ),  \n  num_species = 0)
\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 in 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, data)\n    f[dspec] = u[dspec, 1] - u[dspec, 2]\n    return 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, data)\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\n    return nothing\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}(dim=1, nnodes=11, ncells=10, nbfaces=2)\n ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=1, nnodes=11, ncells=10, nbfaces=2)\n ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=1, nnodes=11, ncells=10, nbfaces=2)\n ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=1, nnodes=11, ncells=10, nbfaces=2)\n ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=1, nnodes=11, ncells=10, nbfaces=2)\n ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=1, nnodes=11, ncells=10, nbfaces=2)
\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!(\n        vis,\n        allsubgrids,\n        grid2,\n        dvws;\n        flimits = (-0.5, 1.5),\n        clear = false,\n        color = :red,\n        label = \"discontinuous species\"\n    )\n    scalarplot!(\n        vis,\n        allsubgrids,\n        grid2,\n        cvws;\n        flimits = (-0.5, 1.5),\n        clear = false,\n        color = :green,\n        label = \"continuous species\"\n    )\n    return 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.11.1 and

\nCairoMakie 0.12.16
\nExtendableGrids 1.9.2
\nGridVisualize 1.7.0
\nHypertextLiteral 0.9.5
\nLinearAlgebra 1.11.0
\nLinearSolve 2.34.0
\nPkg 1.11.0
\nPlutoUI 0.7.60
\nRevise 3.5.18
\nTest 1.11.0
\nVoronoiFVM 1.25.1\n
\n\n","category":"page"},{"location":"plutostatichtml_examples/interfaces1d/","page":"Internal interfaces (1D)","title":"Internal interfaces (1D)","text":"EditURL = \"https://github.com/WIAS-PDELib/VoronoiFVM.jl/blob/master/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\n    using Revise\n    using Printf\n    using VoronoiFVM\n    using SciMLBase: NoInit\n    using OrdinaryDiffEqBDF\n    using OrdinaryDiffEqRosenbrock\n    using OrdinaryDiffEqSDIRK\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}\n      dim =       1\n   nnodes =      51\n   ncells =      50\n  nbfaces =       2
\n\n\n

Direct implementation with VoronoiFVM

\n\n
function flux!(f, u, edge, data)\n    f[1] = u[1, 1] - u[1, 2]\n    return nothing\nend
\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, data)\n    f[1] = (ε + u[1])^(1.0 / m)\n    return nothing\nend
\n
storage! (generic function with 1 method)
\n\n
begin\n    physics = VoronoiFVM.Physics(\n        flux = flux!,\n        storage = storage!\n    )\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(tsol.history)\nend
\n
(seconds = 2.79, tasm = 2.33, tlinsolve = 0.44, steps = 731, iters = 2920, maxabsnorm = 1.73e-12, maxrelnorm = 1.75e-11, maxroundoff = 9.06e-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, data)\n    y[1] = u[2]\n    return nothing\nend
\n
dae_storage! (generic function with 1 method)
\n\n
function dae_reaction!(y, u, node, data)\n    y[2] = u[2]^m - u[1]\n    return nothing\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_tsol.history)\nend
\n
(seconds = 2.87, tasm = 2.46, tlinsolve = 0.383, 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(\n        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    return reveal(vis)\nend
\n
plotsolutions (generic function with 1 method)
\n
\n

Built with Julia 1.11.1 and

\n\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/WIAS-PDELib/VoronoiFVM.jl/blob/master/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 n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise,\n tend = 100\n )\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 = simplexgrid(X, Y)\n\n eps = 1.0e-2\n physics = VoronoiFVM.Physics(;\n breaction = function (f, u, node, data)\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 return nothing\n end, flux = function (f, u, edge, data)\n f[1] = eps * (u[1, 1] - u[1, 2])\n f[2] = eps * (u[2, 1] - u[2, 2])\n return nothing\n end, storage = function (f, u, node, data)\n f[1] = u[1]\n f[2] = u[2]\n return nothing\n end\n )\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\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!(\n p[1, 1], grid, U[1, :];\n title = @sprintf(\"U1: %.3g U1+U2:%8.3g\", I[1, 1], Uall),\n flimits = (0, 1)\n )\n scalarplot!(\n p[2, 1], grid, U[2, :]; title = @sprintf(\"U2: %.3g\", I[2, 1]),\n flimits = (0, 1)\n )\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\n return nothing\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 = simplexgrid(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 in 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 return nothing\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(\n grid, solution[1, :]; title = \"Nonlinear Poisson\", Plotter = Plotter,\n resolution = (300, 300)\n )\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\n return nothing\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

\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 = \"png\", 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 = [\n            point!(builder, center[1] + radius * sin(t), center[2] + radius * cos(t))\n                for t in range(0, 2π; length = n)\n        ]\n        for i in 1:(n - 1)\n            facet!(builder, points[i], points[i + 1])\n        end\n        facet!(builder, points[end], points[1])\n        return 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 = [\n        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\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 in 1:length(radii)\n        facetregion!(builder, i + 1)\n        circlehole!(builder, holes[:, i], radii[i])\n    end\n\n    return simplexgrid(builder)\nend
\n
swiss_cheese_2d (generic function with 1 method)
\n\n
grid = swiss_cheese_2d()
\n
ExtendableGrids.ExtendableGrid{Float64, Int32}\n      dim =       2\n   nnodes =    1434\n   ncells =    2443\n  nbfaces =     459
\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)\n    return nothing\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}}(\n  grid = ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=2, nnodes=1434, ncells=2443,\n  nbfaces=459),  \n  physics = Physics(data=Main.var\"workspace#3\".Params, flux=flux,\n  storage=default_storage, breaction=bc, ),  \n  num_species = 1)
\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, 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, data) = y[1] = src(node[1])
\n
source1d (generic function with 1 method)
\n\n
flux1d(y, u, edge, data) = y[1] = u[1, 1]^2 - u[1, 2]^2
\n
flux1d (generic function with 1 method)
\n\n
function bc1d(y, u, bnode, data)\n    boundary_dirichlet!(y, u, bnode; region = 1, value = 0.01)\n    boundary_dirichlet!(y, u, bnode; region = 2, value = 0.01)\n    return nothing\nend
\n
bc1d (generic function with 1 method)
\n\n
grid1d = simplexgrid(-1:0.01:1)
\n
ExtendableGrids.ExtendableGrid{Float64, Int32}\n      dim =       1\n   nnodes =     201\n   ncells =     200\n  nbfaces =       2
\n\n
sys1d = VoronoiFVM.System(\n    grid1d;\n    flux = flux1d,\n    bcondition = bc1d,\n    source = source1d,\n    species = [1],\n)
\n
VoronoiFVM.System{Float64, Float64, Int32, Int64, Matrix{Int32}}(\n  grid = ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=1, nnodes=201, ncells=200,\n  nbfaces=2),  \n  physics = Physics(flux=flux1d, storage=default_storage, source=source1d,\n  breaction=bc1d, ),  \n  num_species = 1)
\n\n
sol1d = solve(sys1d; inival = 0.1)
\n
1×201 VoronoiFVM.DenseSolutionArray{Float64, 2}:\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.11.1 and

\nCairoMakie 0.12.16
\nExtendableGrids 1.9.2
\nGridVisualize 1.7.0
\nPkg 1.11.0
\nPlutoUI 0.7.60
\nRevise 3.5.18
\nSimplexGridFactory 2.2.1
\nTest 1.11.0
\nTriangulate 2.3.4
\nVoronoiFVM 1.25.1\n
\n\n","category":"page"},{"location":"plutostatichtml_examples/flux-reconstruction/","page":"Obtaining vector fields","title":"Obtaining vector fields","text":"EditURL = \"https://github.com/WIAS-PDELib/VoronoiFVM.jl/blob/master/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":"VoronoiFVM\n\nVoronoiFVM.jl\n\n(Image: Build status) (Image: ) (Image: ) (Image: DOI) (Image: Zulip Chat) (Image: code style: runic)\n\nSolver 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.\n\nJuliaCon 2024 Lightning Talk: abstract, video\n\nRecent changes\n\nPlease look up the list of recent changes\n\nAccompanying packages\n\nExtendableSparse.jl: convenient and efficient sparse matrix assembly\nExtendableGrids.jl: unstructured grid management library\nSimplexGridFactory.jl: unified high level mesh generator interface\nTriangulate.jl: Julia wrapper for the Triangle triangle mesh generator by J. Shewchuk\nTetGen.jl: Julia wrapper for the TetGen tetrahedral mesh generator by H. Si.\nGridVisualize.jl: grid and function visualization related to ExtendableGrids.jl\nPlutoVista.jl: backend for GridVisualize.jl for use in Pluto notebooks.\n\nVoronoiFVM.jl and most of these packages are part of the meta package PDELib.jl.\n\nSome alternatives\n\nExtendableFEM.jl: finite element library implementing gradient robust FEM from the same package base by Ch. Merdon\nSkeelBerzins.jl: a Julian variation on Matlab's pdepe API\nTrixi.jl: numerical simulation framework for hyperbolic conservation laws\nGridAP.jl Grid-based approximation of partial differential equations in Julia\nFerrite.jl Finite element toolbox for Julia\nFinEtools.jl Finite element tools for Julia\nFiniteVolumeMethod.jl Finite volumes with Donald boxes\n\nSome projects and packages using VoronoiFVM.jl\n\nChargeTransport.jl: Drift diffusion simulator for semiconductor devices\nLiquidElectrolytes.jl: Generalized Nernst-Planck-Poisson model for liquid electrolytes\nMultiComponentReactiveMixtureProject: Model for heat and multi-component, reactive gas phase transport in porous media.\nRfbScFVM: Performance prediction of flow battery cells\nMosLab.jl: From semiconductor to transistor level modeling in Julia\n\nCitation\n\nIf you use this package in your work, please cite it according to CITATION.cff.\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, data)\n y[1] = u[1]\n return nothing\n end\n\n function flux!(y, u, edge, data)\n y[1] = u[1, 1] - u[1, 2]\n return nothing\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, data)\n y[1] = -1\n return nothing\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, data)\n h = meas(edge)\n y[1] = -1 * h^2 / (2 * dim)\n return nothing\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, data)\n ϕK = ϕ[edge.node[1]]\n ϕL = ϕ[edge.node[2]]\n y[1] = -(ϕK - ϕL) * (ϕK - ϕL) / 2\n return nothing\n end\n\n if case == :compare_max\n function bcondition!(y, u, node, data)\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 return nothing\n end\n\n sys_noderea = VoronoiFVM.System(\n grid; bcondition = bcondition!, flux = flux!,\n reaction = reaction!, storage = storage!,\n species = [1], is_linear = true, assembly\n )\n sys_edgerea = VoronoiFVM.System(\n grid; bcondition = bcondition!, flux = flux!,\n edgereaction = edgereaction!, storage = storage!,\n species = [1], is_linear = true, assembly\n )\n sys_edgerea2 = VoronoiFVM.System(\n grid; bcondition = bcondition!, flux = flux!,\n edgereaction = edgereaction2!, storage = storage!,\n species = [1], is_linear = true, assembly\n )\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!(\n vis[1, 1], grid, sol_noderea[1, :]; title = \"node reaction\",\n colormap = :hot\n )\n scalarplot!(vis[2, 1], grid, sol_edgerea[1, :]; title = \"edgerea1\", colormap = :hot)\n scalarplot!(\n vis[1, 2], grid, sol_edgerea2[1, :]; title = \"edgerea2\",\n colormap = :hot\n )\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, data)\n boundary_dirichlet!(y, u, node; species = 1, region = i1, value = 0)\n return nothing\n end\n\n sys2_noderea = VoronoiFVM.System(\n grid; bcondition = bcondition2!, flux = flux!,\n reaction = reaction!, storage = storage!,\n species = [1], is_linear = true\n )\n sys2_edgerea = VoronoiFVM.System(\n grid; bcondition = bcondition2!, flux = flux!,\n edgereaction = edgereaction!, storage = storage!,\n species = [1], is_linear = true\n )\n sys2_edgerea2 = VoronoiFVM.System(\n grid; bcondition = bcondition2!, flux = flux!,\n edgereaction = edgereaction2!, storage = storage!,\n species = [1], is_linear = true\n )\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!(\n vis[1, 1], grid, sol2_noderea[1, :]; title = \"node reaction\",\n colormap = :hot\n )\n scalarplot!(\n vis[2, 1], grid, sol2_edgerea[1, :]; title = \"edgerea1\",\n colormap = :hot\n )\n scalarplot!(\n vis[1, 2], grid, sol2_edgerea2[1, :]; title = \"edgerea2\",\n colormap = :hot\n )\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 in 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 in 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\n return nothing\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 n = 5, Plotter = nothing, tend = 20.0, unknown_storage = :sparse,\n reactionN = 5.0e0, reactionP = 5.0e0, assembly = :edgewise\n )\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, data)\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 return nothing\n end\n\n function reaction!(f, u, node, data)\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 return nothing\n end\n\n function flux!(f, u, node, data)\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 return nothing\n end\n\n function breaction!(f, u, bnode, data)\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 return nothing\n end\n\n function bstorage!(f, u, bnode, data)\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 return nothing\n end\n\n physics!(\n sys,\n VoronoiFVM.Physics(;\n flux = flux!,\n storage = storage!,\n reaction = reaction!,\n breaction = breaction!,\n bstorage = bstorage!\n )\n )\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 in 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 in 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\n return nothing\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":"module_examples/Example240_FiniteElementVelocities/#240:-2D-Convection-in-quadratic-stagnation-flow-velocity-field","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"","category":"section"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"(source code)","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"Solve the equation","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"-nabla cdot ( D nabla u - mathbfv u) = 0","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"in Omega=(0L)times (0H) with a homogeneous Neumann boundary condition at x=0, an outflow boundary condition at x=L, a Dirichlet inflow condition at y=H, and a homogeneous Dirichlet boundary condition on part of y=0.","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"The analytical expression for the (quadratic variant of the) velocity field is mathbfv(xy)=(x^2-2xy) in cartesian coordinates and (for the linear variant) mathbfv(rz)=(r-2z) in cylindrical coordinates, i.e. where the system is solved on Omega to represent a solution on the solid of revolution arising from rotating Omega around x=0.","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"We compute the solution u in both coordinate systems where mathbfv is given as an analytical expression and as a finite element interpolation onto the grid of Omega.","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"module Example240_FiniteElementVelocities\nusing Printf\nusing ExtendableFEMBase\nusing ExtendableFEM\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\nusing LinearAlgebra\n\nfunction stagnation_flow_cartesian(x, y)\n return (x^2, -2x * y)\nend","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"In the cylindrical case: since the reconstruction space mathttHDIVBDM2 is only quadratic, but we have to reconstruct r mathbfv(rz) for a (reconstructed) divergence-free solution, we can only resolve at most the linear case exactly.","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"function stagnation_flow_cylindrical(r, z)\n return (r, -2 * z)\nend\n\nfunction inflow_cylindrical(u, qpinfo)\n x = qpinfo.x\n u .= stagnation_flow_cylindrical(x[1], x[2])\n return nothing\nend\n\nfunction inflow_cartesian(u, qpinfo)\n x = qpinfo.x\n u .= stagnation_flow_cartesian(x[1], x[2])\n return nothing\nend\n\nfunction flux!(f, u, edge, data)\n vd = data.evelo[edge.index] / data.D\n bp = fbernoulli(vd)\n bm = fbernoulli(-vd)\n f[1] = data.D * (bp * u[1] - bm * u[2])\n return nothing\nend\n\nfunction bconditions!(f, u, node, data)\n # catalytic Dirichlet condition at y=0\n if node.region == 5\n boundary_dirichlet!(f, u, node, 1, node.region, 0.0)\n end\n\n # outflow condition at x = L\n if node.region == 2\n f[1] = data.bfvelo[node.ibnode, node.ibface] * u[1]\n end\n\n # inflow condition at y = H\n if node.region == 3\n boundary_dirichlet!(f, u, node, 1, node.region, data.cin)\n end\n return nothing\nend\n\nmutable struct Data\n D::Float64\n cin::Float64\n evelo::Vector{Float64}\n bfvelo::Matrix{Float64}\n\n Data() = new()\nend","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"Calculate the analytical or FEM solution to the stagnation point flow field mathbfv and use this as input to solve for the species concentration u of the corresponding convection-diffusion system.","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"The passed flags regulate the following behavior:","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"cylindrical_grid: if true, calculates both the velocity field mathbfv(rz) and the species concentration u(rz) in cylindrical coordinates, assuming rotationally symmetric solutions for both.\nusefem: if true, calculates the velocity field mathbfv using the finite element method provided by ExtendableFEM.\nreconst: if true, interpolates the FEM-calculated velocity field onto a \"reconstruction\" finite element space that provides an exactly divergence-free solution. In a cylindrical grid, this returns not mathbfv(rz), but r mathbfv(rz) as the velocity.\nuse_different_grids: if true, calculates the FEM solution of the velocity field on a uniform flowgrid and the species concentration on an adaptively refined chemgrid while still interpolating the calculated velocity correctly onto the chemgrid.","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"function main(;\n cylindrical_grid = false, usefem = true, reconst = cylindrical_grid, use_different_grids = false, nref = 1,\n Plotter = nothing, μ = 1.0e-2, D = 0.01, cin = 1.0, assembly = :edgewise,\n interpolation_eps = 1.0e-9\n )\n H = 1.0\n L = 5.0\n\n if cylindrical_grid\n coord_system = Cylindrical2D\n else\n coord_system = Cartesian2D\n end\n\n flowgrid = simplexgrid(\n range(0, L; length = 20 * 2^nref),\n range(0, H; length = 5 * 2^nref)\n )\n\n if use_different_grids\n h_fine = 1.0e-1\n X_bottom = geomspace(0.0, L / 2, 5.0e-1, h_fine)\n X_cat = range(L / 2, L; step = h_fine)\n chemgrid = simplexgrid(\n [X_bottom; X_cat[2:end]],\n geomspace(0.0, H, 1.0e-3, 1.0e-1)\n )\n bfacemask!(chemgrid, [L / 2, 0.0], [3 * L / 4, 0.0], 5)\n else\n chemgrid = deepcopy(flowgrid)\n bfacemask!(chemgrid, [L / 2, 0.0], [3 * L / 4, 0.0], 5)\n end\n\n if usefem\n velocity = compute_velocity(\n flowgrid, cylindrical_grid, reconst, μ; interpolation_eps\n )\n DivIntegrator = L2NormIntegrator([div(1)]; quadorder = 2 * 2, resultdim = 1)\n div_v = sqrt(sum(evaluate(DivIntegrator, [velocity])))\n @info \"||div(R(v))||_2 = $(div_v)\"\n else\n if cylindrical_grid\n velocity = stagnation_flow_cylindrical\n else\n velocity = stagnation_flow_cartesian\n end\n end\n\n if cylindrical_grid\n analytical_velocity = stagnation_flow_cylindrical\n else\n analytical_velocity = stagnation_flow_cartesian\n end\n\n # only the chemgrid needs its CoordinateSystem adjusted\n # since the velocity calculation works by adjusting\n # the kernels for the Stokes operator directly while\n # the finite volume machinery relies upon the CoordinateSystem\n # for selecting the correct geometrical factors for the\n # Voronoi cell contributions\n if cylindrical_grid\n chemgrid[CoordinateSystem] = Cylindrical2D\n end\n\n data = Data()\n data.D = D\n data.cin = cin\n\n evelo = edgevelocities(chemgrid, velocity; reconst)\n bfvelo = bfacevelocities(chemgrid, velocity; reconst)\n\n data.evelo = evelo\n data.bfvelo = bfvelo\n\n physics = VoronoiFVM.Physics(; flux = flux!, breaction = bconditions!, data)\n sys = VoronoiFVM.System(chemgrid, physics; assembly)\n enable_species!(sys, 1, [1])\n\n sol = solve(sys; inival = 0.0)\n\n fvm_divs = VoronoiFVM.calc_divergences(sys, evelo, bfvelo)\n @info \"||div(v)||_∞ = $(norm(fvm_divs, Inf))\"\n\n vis = GridVisualizer(; Plotter = Plotter)\n\n scalarplot!(\n vis[1, 1], chemgrid, sol[1, :]; flimits = (0, cin + 1.0e-5),\n show = true\n )\n\n minmax = extrema(sol)\n @info \"Minimal/maximal values of concentration: $(minmax)\"\n\n return sol, evelo, bfvelo, minmax\nend\n\nusing Test\nfunction runtests()\n cin = 1.0\n for cylindrical_grid in [false, true]\n sol_analytical, evelo_analytical, bfvelo_analytical, minmax_analytical = main(;\n cylindrical_grid, cin, usefem = false\n )\n sol_fem, evelo_fem, bfvelo_fem, minmax_fem = main(;\n cylindrical_grid, cin, usefem = true\n )\n @test norm(evelo_analytical .- evelo_fem, Inf) ≤ 1.0e-11\n @test norm(bfvelo_analytical .- bfvelo_fem, Inf) ≤ 1.0e-9\n @test norm(sol_analytical .- sol_fem, Inf) ≤ 1.0e-10\n @test norm(minmax_analytical .- [0.0, cin], Inf) ≤ 1.0e-15\n @test norm(minmax_fem .- [0.0, cin], Inf) ≤ 1.0e-11\n end\n return nothing\nend\n\nfunction compute_velocity(\n flowgrid, cylindrical_grid, reconst, μ = 1.0e-2; interpolation_eps = 1.0e-10\n )\n # define finite element spaces\n FE_v, FE_p = H1P2B{2, 2}, L2P1{1}\n reconst_FEType = HDIVBDM2{2}\n FES = [FESpace{FE_v}(flowgrid), FESpace{FE_p}(flowgrid; broken = true)]\n\n # describe problem\n Problem = ProblemDescription(\"incompressible Stokes problem\")\n v = Unknown(\"v\"; name = \"velocity\")\n p = Unknown(\"p\"; name = \"pressure\")\n assign_unknown!(Problem, v)\n assign_unknown!(Problem, p)\n\n # assign stokes operator\n assign_operator!(\n Problem,\n BilinearOperator(\n kernel_stokes!, cylindrical_grid ? [id(v), grad(v), id(p)] : [grad(v), id(p)];\n bonus_quadorder = 2, store = false,\n params = [μ, cylindrical_grid]\n )\n )\n\n # assign Dirichlet boundary conditions on all boundary regions to\n # enforce match with analytical solution\n if cylindrical_grid\n assign_operator!(\n Problem, InterpolateBoundaryData(v, inflow_cylindrical; regions = [1, 2, 3, 4])\n )\n else\n assign_operator!(\n Problem, InterpolateBoundaryData(v, inflow_cartesian; regions = [1, 2, 3, 4])\n )\n end\n\n velocity_solution = solve(Problem, FES)\n\n # ensure divergence free solution by projecting onto reconstruction spaces\n FES_reconst = FESpace{reconst_FEType}(flowgrid)\n R = FEVector(FES_reconst)\n if reconst\n if cylindrical_grid\n lazy_interpolate!(\n R[1], velocity_solution, [id(v)]; postprocess = multiply_r,\n bonus_quadorder = 2, eps = interpolation_eps\n )\n else\n lazy_interpolate!(\n R[1], velocity_solution, [id(v)];\n bonus_quadorder = 2, eps = interpolation_eps\n )\n end\n else\n return velocity_solution[1]\n end\n\n return R[1]\nend\n\nfunction kernel_stokes!(result, u_ops, qpinfo)\n μ = qpinfo.params[1]\n cylindrical_grid = qpinfo.params[2]\n if cylindrical_grid > 0\n r = qpinfo.x[1]\n u, ∇u, p = view(u_ops, 1:2), view(u_ops, 3:6), view(u_ops, 7)\n result[1] = μ / r * u[1] - p[1]\n result[2] = 0\n result[3] = μ * r * ∇u[1] - r * p[1]\n result[4] = μ * r * ∇u[2]\n result[5] = μ * r * ∇u[3]\n result[6] = μ * r * ∇u[4] - r * p[1]\n result[7] = -(r * (∇u[1] + ∇u[4]) + u[1])\n else\n ∇u, p = view(u_ops, 1:4), view(u_ops, 5)\n result[1] = μ * ∇u[1] - p[1]\n result[2] = μ * ∇u[2]\n result[3] = μ * ∇u[3]\n result[4] = μ * ∇u[4] - p[1]\n result[5] = -(∇u[1] + ∇u[4])\n end\n return nothing\nend\n\nfunction multiply_r(result, input, qpinfo)\n x = qpinfo.x\n result .= input * x[1]\n return nothing\nend\n\nend","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","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\nSystem state\nLinear solver control\nBlock preconditioning\nHistory handling\nMatrix extraction","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/#VoronoiFVM.fixed_timesteps!","page":"Solvers","title":"VoronoiFVM.fixed_timesteps!","text":"fixed_timesteps!(control,Δt; grow=1.0)\n\nModify control data such that the time steps are fixed to a geometric sequence such that Δtnew=Δtold*grow\n\n\n\n\n\n","category":"function"},{"location":"solver/#System-state","page":"Solvers","title":"System state","text":"","category":"section"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"VoronoiFVM.SystemState\nVoronoiFVM.SystemState(::Type, system::VoronoiFVM.AbstractSystem; data)\nVoronoiFVM.SystemState(system::VoronoiFVM.AbstractSystem; data)\nVoronoiFVM.solve!(state::VoronoiFVM.SystemState; kwargs...)\nBase.similar(state::VoronoiFVM.SystemState; kwargs...)","category":"page"},{"location":"solver/#VoronoiFVM.SystemState","page":"Solvers","title":"VoronoiFVM.SystemState","text":"mutable struct SystemState{Tv, Tp, TMatrix<:AbstractArray{Tv, 2}, TSolArray<:AbstractArray{Tv, 2}, TData}\n\nStructure holding state information for finite volume system.\n\nType parameters:\n\nTv: element type of solution vectors and matrix\nTMatrix: matrix type\nTSolArray: type of solution vector: (Matrix or SparseMatrixCSC)\nTData: type of user data\n\nType fields:\n\nsystem::VoronoiFVM.System: Related finite volume system\n\ndata::Any: User data\n\nsolution::AbstractMatrix: Solution vector\n\nmatrix::AbstractMatrix: Jacobi matrix for nonlinear problem\n\ndudp::Vector{TSolArray} where {Tv, TSolArray<:AbstractMatrix{Tv}}: Parameter derivative (vector of solution arrays)\n\nupdate::AbstractMatrix: Vector holding Newton update\n\nresidual::AbstractMatrix: Vector holding Newton residual\n\nlinear_cache::Union{Nothing, LinearSolve.LinearCache}: Linear solver cache\n\nparams::Vector: Parameter vector\n\nuhash::UInt64: Hash value of latest unknowns vector the assembly was called with (used by differential equation interface)\n\nhistory::Union{Nothing, VoronoiFVM.DiffEqHistory}: History record for solution process (used by differential equation interface)\n\n\n\n\n\n","category":"type"},{"location":"solver/#VoronoiFVM.SystemState-Tuple{Type, VoronoiFVM.AbstractSystem}","page":"Solvers","title":"VoronoiFVM.SystemState","text":"SystemState(Tv, system; data=system.physics.data, matrixtype=system.matrixtype)\n\nCreate state information for finite volume system.\n\nArguments:\n\nTv: value type of unknowns, matrix\nsystem: Finite volume system\n\nKeyword arguments:\n\ndata: User data. Default: data(system)\nmatrixtype. Default: system.matrixtype\n\n\n\n\n\n","category":"method"},{"location":"solver/#VoronoiFVM.SystemState-Tuple{VoronoiFVM.AbstractSystem}","page":"Solvers","title":"VoronoiFVM.SystemState","text":"SystemState(Tv, system; data=system.physics.data, matrixtype=system.matrixtype)\n\nCreate state information for finite volume system.\n\nArguments:\n\nTv: value type of unknowns, matrix\nsystem: Finite volume system\n\nKeyword arguments:\n\ndata: User data. Default: data(system)\nmatrixtype. Default: system.matrixtype\n\n\n\n\n\nSystemState(system; kwargs...)\n\nShortcut for creating state with value type defined by Tv type parameter of system\n\n\n\n\n\n","category":"method"},{"location":"solver/#CommonSolve.solve!-Tuple{VoronoiFVM.SystemState}","page":"Solvers","title":"CommonSolve.solve!","text":"solve!(state; kwargs...)\n\nBuilt-in solution method for VoronoiFVM.System. \n\nSolves finite volume system the satate is belonging to. Mutates the state and returns the solution.\n\nFor the keyword argumentsm see VoronoiFVM.solve.\n\n\n\n\n\n","category":"method"},{"location":"solver/#Base.similar-Tuple{VoronoiFVM.SystemState}","page":"Solvers","title":"Base.similar","text":"similar(state; data=state.data)\n\nCreate a new state of with the same system, different work arrays, and possibly different data. The matrix of the new state initially shares the sparsity structure with state.\n\n\n\n\n\n","category":"method"},{"location":"solver/#Linear-solver-control","page":"Solvers","title":"Linear solver control","text":"","category":"section"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"Linear systems are solved using LinearSolve.jl. Linear solve compatible solver strategies (factorizations, iterative solvers) can be specified wia method_linear keyword argument to LinearSolve (equivalent to the method_linear entry of SolverControl.","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"Currently supported possibilities are documented in the documentation of ExtendableSparse.jl.","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"VoronoiFVM.jl provides partitioning methods for block preconditioners.","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"Equationwise\npartitioning","category":"page"},{"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, mode)\n\nCalculate partitioning of system unknowns to be used in block preconditioners. Possible modes:\n\nEquationwise()\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 returned as history(solution)","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"NewtonSolverHistory\nTransientSolverHistory\nVoronoiFVM.DiffEqHistory\nBase.summary(::NewtonSolverHistory)\nBase.summary(::TransientSolverHistory)\ndetails\nhistory\n\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/#VoronoiFVM.DiffEqHistory","page":"Solvers","title":"VoronoiFVM.DiffEqHistory","text":"mutable struct DiffEqHistory\n\nHistory information for DiffEqHistory\n\nnd::Any: number of combined jacobi/rhs evaluations\nnjac::Any: number of combined jacobi evaluations\nnf::Any: number of rhs evaluations\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(sol)\n\nReturn solver history 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(sol)\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(sol)\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\nreshape(::AbstractDiffEqArray,::VoronoiFVM.AbstractSystem)\nSciMLBase.ODEFunction","category":"page"},{"location":"solver/#SciMLBase.ODEProblem","page":"Solvers","title":"SciMLBase.ODEProblem","text":"ODEProblem(state,inival,tspan,callback=SciMLBase.CallbackSet())\n\nCreate an ODEProblem from a system state. See SciMLBase.ODEProblem(sys::VoronoiFVM.System, inival, tspan;kwargs...) for more documentation.\n\nDefined in VoronoiFVM.jl.\n\n\n\n\n\nODEProblem(system,inival,tspan,callback=SciMLBase.CallbackSet())\n\nCreate an ODEProblem in mass matrix form which can be handled 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\nDefined in VoronoiFVM.jl.\n\n\n\n\n\n","category":"type"},{"location":"solver/#Base.reshape-Tuple{AbstractDiffEqArray, VoronoiFVM.AbstractSystem}","page":"Solvers","title":"Base.reshape","text":"reshape(ode_solution, system; times=nothing, state=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 order) interpolated solution at the given moments of time will be returned.\n\nDefined in VoronoiFVM.jl.\n\n\n\n\n\n","category":"method"},{"location":"solver/#SciMLBase.ODEFunction","page":"Solvers","title":"SciMLBase.ODEFunction","text":" ODEFunction(state,inival=unknowns(system,inival=0),t0=0)\n\nCreate an ODEFunction. For more documentation, see SciMLBase.ODEFunction(state::VoronoiFVM.SystemState; kwargs...)\n\n\n\n\n\n ODEFunction(system,inival=unknowns(system,inival=0),t0=0)\n\nCreate an ODEFunction in mass matrix form to be handled 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":"type"},{"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":"NewtonControl","category":"page"},{"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/#Deprecated-API","page":"Solvers","title":"Deprecated API","text":"","category":"section"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"The methods and struct in this section are deprecated as of version 2.4 and will be removed in version 3.","category":"page"},{"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":"VoronoiFVM.BlockStrategy\nNoBlock\nEquationBlock\nPointBlock","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":"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 OrdinaryDiffEqBDF\n    using OrdinaryDiffEqRosenbrock\n    using OrdinaryDiffEqSDIRK\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, data)\n    f[1] = u[1]\n    f[2] = u[2]\n    return nothing\nend;
\n\n\n
function bruss_diffusion(f, u, edge, data)\n    f[1] = bruss_D_1 * (u[1, 1] - u[1, 2])\n    f[2] = bruss_D_2 * (u[2, 1] - u[2, 2])\n    return nothing\nend;
\n\n\n
function bruss_reaction(f, u, node, data)\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]\n    return nothing\nend;
\n\n\n
begin\n\n    function ODESolver(system, inival, solver)\n        state = VoronoiFVM.SystemState(system)\n        problem = ODEProblem(state, inival, (0, bruss_tend))\n        odesol = solve(\n            problem,\n            solver,\n            dt = 1.0e-5, reltol = 1.0e-4\n        )\n        return reshape(odesol, system; state)\n    end\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)\nelse\n    bruss_X = -1:0.1:1\n    bruss_grid = simplexgrid(bruss_X, bruss_X)\nend;
\n\n\n
bruss_system = VoronoiFVM.System(\n    bruss_grid, species = [1, 2],\n    flux = bruss_diffusion, storage = bruss_storage, reaction = bruss_reaction\n);
\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 in 1:size(inival, 2)\n        inival[1, i] = fpeak(coord[:, i])\n        inival[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.history_details(bruss_tsol)...)
\n
(t_run = 14.628478842, nd = 1925, njac = 855, nf = 2780)
\n\n\n

dim:\\(\\;\\) method: \\(\\;\\) t: 150.0

\n\n\n\n\n\n\n
\n

Built with Julia 1.11.1 and

\n\n
\n\n","category":"page"},{"location":"plutostatichtml_examples/ode-brusselator/","page":"OrdinaryDiffEq.jl brusselator","title":"OrdinaryDiffEq.jl brusselator","text":"EditURL = \"https://github.com/WIAS-PDELib/VoronoiFVM.jl/blob/master/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 = \"png\", 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 implement 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])\n    return nothing\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)\n    return nothing\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)]\n    return nothing\nend
\n
boutflow (generic function with 1 method)
\n\n
function flowtransportsystem(grid; kwargs...)\n    data = FlowTransportData(; kwargs...)\n    return VoronoiFVM.System(\n        grid;\n        flux,\n        bcondition,\n        boutflow,\n        data,\n        outflowboundaries = [data.Γ_out],\n        species = [1, 2],\n    )\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    return (; 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}\n      dim =       1\n   nnodes =      11\n   ncells =      10\n  nbfaces =       2
\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.5000000000000003], out = [-1.0, -0.5000000000000003])
\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}\n      dim =       2\n   nnodes =     121\n   ncells =     200\n  nbfaces =      40
\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 VoronoiFVM.DenseSolutionArray{Float64, 2}:\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.5000000000000004], 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}\n      dim =       3\n   nnodes =    1331\n   ncells =    6000\n  nbfaces =    1200
\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 VoronoiFVM.DenseSolutionArray{Float64, 2}:\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.0000000000000369, 0.5000000000000185], out = [-1.000000000000039, -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.11.1 and

\nCairoMakie 0.12.16
\nExtendableGrids 1.9.2
\nGridVisualize 1.7.0
\nHypertextLiteral 0.9.5
\nPkg 1.11.0
\nPlutoUI 0.7.60
\nRevise 3.5.18
\nSimplexGridFactory 2.2.1
\nTest 1.11.0
\nTriangulate 2.3.4
\nUUIDs 1.11.0
\nVoronoiFVM 1.25.1\n
\n\n","category":"page"},{"location":"plutostatichtml_examples/outflow/","page":"Outflow boundary conditions","title":"Outflow boundary conditions","text":"EditURL = \"https://github.com/WIAS-PDELib/VoronoiFVM.jl/blob/master/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 = simplexgrid(X, Y)\n nn = num_nodes(grid)\n\n eps = 1.0e-2\n\n function reaction(f, u, node, data)\n f[1] = u[1]^2\n return nothing\n end\n\n function flux(f, u, edge, data)\n f[1] = eps * (u[1, 1]^2 - u[1, 2]^2)\n return nothing\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.0 * (x1^2 + x2^2))\n return nothing\n end\n\n function storage(f, u, node, data)\n f[1] = u[1]\n return nothing\n end\n\n function bcondition(f, u, node, data)\n boundary_dirichlet!(\n f,\n u,\n node;\n species = 1,\n region = 2,\n value = ramp(node.time; dt = (0, 0.1), du = (0, 1))\n )\n boundary_dirichlet!(\n f,\n u,\n node;\n species = 1,\n region = 4,\n value = ramp(node.time; dt = (0, 0.1), du = (0, 1))\n )\n return nothing\n end\n\n sys = VoronoiFVM.System(\n grid; reaction, flux, source, storage, bcondition, assembly,\n species = [1]\n )\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(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(),\n precon_linear = ILUZeroPreconditioner(),\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n\n @info \"Krylov-block1\"\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(),\n precon_linear = BlockPreconditioner(;\n partitioning = [1:(nn ÷ 2), (nn ÷ 2 + 1):nn],\n factorization = ILU0Preconditioner()\n ),\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n\n @info \"Krylov-block2\"\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(),\n precon_linear = BlockPreconditioner(;\n partitioning = [1:2:nn, 2:2:nn],\n factorization = UMFPACKFactorization()\n ),\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n\n @info \"Krylov - delayed factorization:\"\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(),\n precon_linear = SparspakFactorization(),\n keepcurrent_linear = false,\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n\n @info \"Krylov - jacobi:\"\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(),\n precon_linear = JacobiPreconditioner(),\n keepcurrent_linear = true,\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n\n @info \"Krylov - SA_AMG:\"\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(),\n precon_linear = SA_AMGPreconditioner(),\n keepcurrent_linear = true,\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n\n @info \"Krylov - AMGCL_AMG:\"\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(),\n precon_linear = AMGCL_AMGPreconditioner(),\n keepcurrent_linear = true,\n kwargs...\n )\n return @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\n return nothing\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 ExtendableSparse: ILUZeroPreconBuilder\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, data)\n f[1] = (1 + p) * (u[1, 1]^2 - u[1, 2]^2)\n return nothing\n end\n\n function r!(f, u, edge, data)\n f[1] = p * u[1]^5\n return nothing\n end\n\n function bc!(f, u, node, data)\n boundary_dirichlet!(f, u, node, ispec, 1, 0.0)\n boundary_dirichlet!(f, u, node, ispec, 3, p)\n return nothing\n end\n\n X = collect(0:(1.0 / n):1)\n grid = simplexgrid(X, X)\n sys = VoronoiFVM.System(\n grid; valuetype, species = [1], flux = flux!, reaction = r!,\n bcondition = bc!\n )\n tff = VoronoiFVM.TestFunctionFactory(sys)\n tfc = testfunction(tff, [1], [3])\n sol = solve(sys; inival = 0.5)\n return [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 in 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 return sum(DF)\nend\n\nfunction fluxg!(f, u, edge, data)\n f[1] = (1 + data.p) * (u[1, 1]^2 - u[1, 2]^2)\n return nothing\nend\n\nfunction rg!(f, u, edge, data)\n f[1] = data.p * u[1]^5\n return nothing\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)\n return nothing\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 = simplexgrid(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(\n grid; valuetype = Tv, species = [1], flux = fluxg!,\n reaction = rg!, bcondition = bcg!, data,\n unknown_storage = :dense\n )\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)\n return [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 in 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 return sum(DG)\nend\n\n#########################################################################\n\nfunction fluxh!(f, u, edge, data)\n p = parameters(u)[1]\n f[1] = (1 + p) * (u[1, 1]^2 - u[1, 2]^2)\n return nothing\nend\n\nfunction rh!(f, u, edge, data)\n p = parameters(u)[1]\n f[1] = p * u[1]^5\n return nothing\nend\n\nfunction bch!(f, u, node, data)\n p = parameters(u)[1]\n boundary_dirichlet!(f, u, node, 1, 1, 0.0)\n boundary_dirichlet!(f, u, node, 1, 3, p)\n return nothing\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 = simplexgrid(X, X)\n\n sys = VoronoiFVM.System(\n grid; species = [1], flux = fluxh!, reaction = rh!,\n bcondition = bch!, unknown_storage = :dense, nparams = 1\n )\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 return 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 return nothing\n end\n\n dp = 0.05\n P = 0.1:dp:2\n state = VoronoiFVM.SystemState(sys)\n U0 = solve!(state; inival = 0.5, params = [P[1]])\n\n ndof = num_dof(sys)\n colptr = [i for i in 1:(ndof + 1)]\n rowval = [1 for i in 1:ndof]\n nzval = [1.0 for in 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 in P\n params[1] = p\n sol = solve!(state; inival = 0.5, params)\n\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 = state.matrix \\ vec(state.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 return sum(DH)\nend\n\nusing Test\nfunction runtests()\n testval = 489.3432830184927\n @test runf() ≈ testval\n @test rung() ≈ testval\n @test runh() ≈ testval\n return nothing\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 return 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 return 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 return check(u) && check(v) && check(w)\nend\n\nusing Test\nfunction runtests()\n return @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(;\n nref = 0, Plotter = nothing, verbose = false, unknown_storage = :sparse,\n brea = false, assembly = :edgewise\n )\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 = simplexgrid(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, data)\n f[1] = u[1, 1] - u[1, 2]\n return nothing\n end\n function storage!(f, u, node, data)\n f[1] = u[1]\n return nothing\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, data)\n if node.region == 3\n f[1] = -Q\n end\n return nothing\n end\n\n # Create physics\n physics = VoronoiFVM.Physics(;\n flux = flux!,\n storage = storage!,\n breaction = breaction!\n )\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!(\n vis, grid, U[1, :]; title = @sprintf(\"Q=%.2f\", q), clear = true,\n show = true\n )\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\n return nothing\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]\n return nothing\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]\n return nothing\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\n return nothing\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(max(-1 + eps_reg, -u[ic, 1]))\n mu2 = -log1p(max(-1 + eps_reg, -u[ic, 2]))\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]\n return nothing\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)\n return nothing\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 )\n h = 1.0 / convert(Float64, n)\n grid = simplexgrid(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\n sys = VoronoiFVM.System(\n grid,\n physics;\n unknown_storage = unknown_storage,\n species = [1, 2],\n assembly = assembly,\n )\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(\n sys;\n method_linear = UMFPACKFactorization(),\n inival,\n times = [0.0, 10],\n control = control,\n )\n\n vis = GridVisualizer(; Plotter = Plotter, layout = (1, 1), fast = true)\n for log10t in -4:0.025:0\n time = 10^(log10t)\n sol = tsol(time)\n scalarplot!(\n vis[1, 1],\n grid,\n sol[iphi, :];\n label = \"ϕ\",\n title = @sprintf(\"time=%.3g\", time),\n flimits = (0, 5),\n color = :green,\n )\n scalarplot!(\n vis[1, 1],\n grid,\n sol[ic, :];\n label = \"c\",\n flimits = (0, 5),\n clear = false,\n color = :red,\n )\n reveal(vis)\n end\n return sum(tsol.u[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 U .= inival\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!(\n 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 )\n scalarplot!(\n vis[1, 1],\n grid,\n U[ic, :];\n label = \"c\",\n flimits = (0, 5),\n clear = false,\n color = :red,\n )\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!(\n vis[2, 1],\n v,\n c;\n color = :green,\n clear = false,\n title = \"C_dl\",\n )\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(\n main(; unknown_storage = :sparse, dlcap = false, assembly = :edgewise),\n evolval;\n rtol = rtol,\n )\n @test isapprox(\n main(; unknown_storage = :sparse, dlcap = true, assembly = :edgewise),\n dlcapval;\n rtol = rtol,\n )\n @test isapprox(\n main(; unknown_storage = :dense, dlcap = false, assembly = :edgewise),\n evolval;\n rtol = rtol,\n )\n @test isapprox(\n main(; unknown_storage = :dense, dlcap = true, assembly = :edgewise),\n dlcapval;\n rtol = rtol,\n )\n @test isapprox(\n main(; unknown_storage = :sparse, dlcap = false, assembly = :cellwise),\n evolval;\n rtol = rtol,\n )\n @test isapprox(\n main(; unknown_storage = :sparse, dlcap = true, assembly = :cellwise),\n dlcapval;\n rtol = rtol,\n )\n @test isapprox(\n main(; unknown_storage = :dense, dlcap = false, assembly = :cellwise),\n evolval;\n rtol = rtol,\n )\n @test isapprox(\n main(; unknown_storage = :dense, dlcap = true, assembly = :cellwise),\n dlcapval;\n rtol = rtol,\n )\n return nothing\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"}] +[{"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(;\n dim = 1, nref = 0, Plotter = nothing, plot_grid = false, verbose = false,\n unknown_storage = :sparse, assembly = :edgewise, strategy = nothing\n )\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 = simplexgrid(X)\n Γ_in = 1\n Γ_out = 2\n elseif dim == 2\n grid = simplexgrid(X, Y)\n Γ_in = 4\n Γ_out = 2\n else\n grid = simplexgrid(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, data)\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 return nothing\n end\n\n function source(f, node, data)\n if node.region == 1\n f[1] = 1.0e-4 * (3.0 - node[1])\n end\n return nothing\n end\n\n flux = function (f, u, edge, data)\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 return nothing\n end\n\n storage = function (f, u, node, data)\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 return nothing\n end\n\n sys = VoronoiFVM.System(\n grid; flux, reaction, storage, source,\n unknown_storage, assembly, is_linear = true\n )\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)\n U = solve(sys; control)\n @info num_dof(U)\n\n p = GridVisualizer(;\n Plotter = Plotter, layout = (3, 1),\n limits = (0, 1.0e-3),\n xlimits = (0, 3)\n )\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 = :darkred)\n scalarplot!(p[2, 1], subgrid2, U2; title = \"spec2\", color = :green)\n scalarplot!(p[3, 1], subgrid3, U3; title = \"spec3\", color = :navyblue)\n reveal(p)\n return U\nend\n\nfunction runtests()\n strategy = BICGstabIteration(AMGCL_AMGPreconditioner())\n @test sum(main(; dim = 1, strategy, unknown_storage = :dense)[2, :]) ≈ 0.014101758266210086\n @test sum(main(; dim = 1, strategy, unknown_storage = :sparse)[2, :]) ≈ 0.014101758266210086\n @test sum(main(; dim = 2, strategy, unknown_storage = :dense)[2, :]) ≈ 0.12691582439590407\n @test sum(main(; dim = 2, strategy, unknown_storage = :sparse)[2, :]) ≈ 0.12691582439590407\n @test sum(main(; dim = 3, strategy, unknown_storage = :dense)[2, :]) ≈ 1.1422561017685693\n @test sum(main(; dim = 3, strategy, unknown_storage = :sparse)[2, :]) ≈ 1.1422561017685693\n return nothing\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 called 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 neighboring 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: :sparse, :tridiagonal, :banded, :auto. Default: :sparse. :auto leads to automatic choice for dense solution storage depending on space dimension and number of species.\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). Uses a lock to ensure parallel access.\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/#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. 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\ndata\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.data","page":"System","title":"VoronoiFVM.data","text":"data(system)\n\n\nRetrieve user data record.\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)} <: VoronoiFVM.AbstractSystem{Tv, Tc, Ti, Tm}\n\nStructure holding data for finite volume system.\n\nSubtype of AbstractSystem.\n\nType parameters:\n\nTSpecMat: Type of matrix storing species information (Matrix or SparseMatrixCSC)\n\nFor the other type parameters, see AbstractSystem.\n\ngrid::ExtendableGrid: Grid\n\nphysics::VoronoiFVM.Physics: Physics data\n\nboundary_values::Matrix: Array of boundary condition values\n\nboundary_factors::Matrix: Array of boundary condition factors\n\nregion_species::AbstractMatrix: Matrix containing species numbers for inner regions\n\nbregion_species::AbstractMatrix: Matrix containing species numbers for boundary regions\n\nnode_dof::AbstractMatrix: Matrix containing degree of freedom numbers for each node\n\nmatrixtype::Symbol: - :multidiagonal (currently disabled)\n:sparse\n:banded\n:tridiagonal\n\nspecies_homogeneous::Bool: Flag which says if the number of unknowns per node is constant\n\nnum_quantities::Any: Number of quantities defined on system\n\nnum_parameters::Any: Number of parameter the system depends on.\n\nassembly_data::Union{Nothing, VoronoiFVM.AbstractAssemblyData{Tc, Ti}} where {Tc, Ti}: Precomputed form factors for bulk assembly\n\nboundary_assembly_data::VoronoiFVM.AbstractAssemblyData: Precomputed form factors for boundary assembly\n\nassembly_type::Symbol: :edgewise or :cellwise\n\nis_linear::Bool: Is the system linear ?\n\noutflownoderegions::Union{Nothing, SparseArrays.SparseMatrixCSC{Bool, Int64}}: Outflow nodes with their region numbers.\n\ngeneric_matrix::SparseArrays.SparseMatrixCSC: Sparse matrix for generic operator handling\n\ngeneric_matrix_colors::Vector: Sparse matrix colors for generic operator handling\n\nis_complete::Bool: Has the system been completed (species information compiled)?\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{Tv}, ispec, ibc, v) where {Tv}\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!-Union{Tuple{Tv}, Tuple{VoronoiFVM.AbstractSystem{Tv}, Any, Any, Any}} where Tv","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 n = 20, m = 2.0, Plotter = nothing, verbose = false,\n unknown_storage = :sparse, tend = 0.01, tstep = 0.0001, assembly = :edgewise\n )\n\n # Create a one-dimensional discretization\n h = 1.0 / convert(Float64, n / 2)\n X = collect(-1:h:1)\n grid = simplexgrid(X)\n\n # Flux function which describes the flux\n # between neighboring control volumes\n function flux!(f, u, edge, data)\n f[1] = u[1, 1] - u[1, 2]\n return nothing\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, data)\n f[1] = (ϵ + u[1])^(1.0 / m)\n return nothing\n end\n\n # Create a physics structure\n physics = VoronoiFVM.Physics(;\n flux = flux!,\n storage = storage!\n )\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 in 1:length(tsol)\n time = tsol.t[i]\n scalarplot!(\n p[1, 1], grid, tsol[1, :, i]; title = @sprintf(\"t=%.3g\", time),\n color = :red, label = \"numerical\"\n )\n scalarplot!(\n p[1, 1], grid, map(x -> barenblatt(x, time, m)^m, grid); clear = false,\n color = :green, label = \"exact\"\n )\n reveal(p)\n sleep(1.0e-2)\n end\n end\n return sum(tsol.u[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\n\n return nothing\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":"module_examples/Example440_ParallelState/#440:-Parallel-solves","page":"440: Parallel solves","title":"440: Parallel solves","text":"","category":"section"},{"location":"module_examples/Example440_ParallelState/","page":"440: Parallel solves","title":"440: Parallel solves","text":"(source code)","category":"page"},{"location":"module_examples/Example440_ParallelState/","page":"440: Parallel solves","title":"440: Parallel solves","text":"Demonstrate how to solve one system with different data in parallel using the SystemState (new in v2.0).","category":"page"},{"location":"module_examples/Example440_ParallelState/","page":"440: Parallel solves","title":"440: Parallel solves","text":"module Example440_ParallelState\n\n\nusing VoronoiFVM, ExtendableGrids\nusing GridVisualize\nusing ChunkSplitters\n\nfunction flux(y, u, node, data)\n y[1] = u[1, 1]^2 - u[1, 2]^2\n return nothing\nend\n\nfunction bcondition(y, u, node, data)\n boundary_dirichlet!(y, u, node, species = 1, region = 1, value = 0.1)\n boundary_neumann!(y, u, node, species = 1, region = 2, value = data.influx)\n return nothing\nend\n\nfunction main(; nref = 5, Plotter = nothing)\n grid = simplexgrid(range(0, 1, length = 10 * 2^nref + 1))\n sys = VoronoiFVM.System(grid; flux, bcondition, species = [1], data = (influx = 0.0,))\n\n # Initial state. First solution creates the matrix\n state0 = VoronoiFVM.SystemState(sys)\n sol = solve!(state0; inival = 0.1)\n\n # Prepare parameter and result data\n influxes = range(0.0, 10.0, length = 100)\n masses = similar(influxes)\n\n # Split the index range in as many chunks as threads\n Threads.@threads for indexes in chunks(1:length(influxes); n = Threads.nthreads())\n # Create a new state sharing the system - one for each chunk\n state = similar(state0)\n # Solve for all data values in chunk\n for iflux in indexes\n data = (influx = influxes[iflux],)\n sol = solve!(state; data, inival = 0.1, verbose = \"\")\n masses[iflux] = integrate(sys, sol)[1, 1]\n end\n end\n scalarplot(influxes, masses; Plotter, xlabel = \"influx\", ylabel = \"mass\")\n return sum(masses)\nend\n\nusing Test\nfunction runtests()\n testval = 140.79872772042577\n @test main() ≈ testval\n return nothing\nend\n\nend","category":"page"},{"location":"module_examples/Example440_ParallelState/","page":"440: Parallel solves","title":"440: Parallel solves","text":"","category":"page"},{"location":"module_examples/Example440_ParallelState/","page":"440: Parallel solves","title":"440: Parallel solves","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/#ForwardDiff.value","page":"Miscellaneous","title":"ForwardDiff.value","text":"value(x)\n\nReturn the value of a dual number (for debugging in callback functions). Re-exported from ForwardDiff.\n\n\n\n\n\n","category":"function"},{"location":"misc/#Form-factor-calculatione","page":"Miscellaneous","title":"Form factor calculatione","text":"","category":"section"},{"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\ncompat: Compat\nDeprecated as of v1.20\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.coordinates-Tuple{ExtendableGrid}","page":"Miscellaneous","title":"VoronoiFVM.coordinates","text":"coordinates(grid::ExtendableGrid)\n\nReturn coordinate array of grid. \n\ncompat: Compat\nDeprecated as of v1.20\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 = simplexgrid(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(;\n flux = function (f, u, edge, data)\n f[1] = eps * (u[1, 1] - u[1, 2])\n return nothing\n end,\n bflux = function (f, u, edge, data)\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 return nothing\n end,\n breaction = function (f, u, node, data)\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 return nothing\n end,\n bsource = function (f, bnode, data)\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 return nothing\n end, bstorage = function (f, u, node, data)\n if node.region == breg\n f[2] = u[2]\n end\n return nothing\n end, storage = function (f, u, node, data)\n f[1] = u[1]\n return nothing\n end\n )\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 return nothing\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.keepcurrent_linear = false\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 return 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)\n return nothing\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 n = 20, m = 2, Plotter = nothing, verbose = false,\n unknown_storage = :sparse, tend = 0.01, tstep = 0.0001, assembly = :edgewise\n )\n\n # Create a one-dimensional discretization\n h = 1.0 / convert(Float64, n / 2)\n X = collect(-1:h:1)\n grid = simplexgrid(X)\n\n # Flux function which describes the flux\n # between neighboring control volumes\n function flux!(f, u, edge, data)\n f[1] = u[1, 1]^m - u[1, 2]^m\n return nothing\n end\n\n # Storage term\n function storage!(f, u, node, data)\n f[1] = u[1]\n return nothing\n end\n\n # Create a physics structure\n physics = VoronoiFVM.Physics(;\n flux = flux!,\n storage = storage!\n )\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 in 1:length(tsol)\n time = tsol.t[i]\n scalarplot!(\n p[1, 1], grid, tsol[1, :, i]; title = @sprintf(\"t=%.3g\", time),\n color = :red, label = \"numerical\",\n markershape = :circle, markevery = 1\n )\n scalarplot!(\n p[1, 1], grid, map(x -> barenblatt(x, time, m), grid); clear = false,\n color = :green,\n label = \"exact\", markershape = :none\n )\n reveal(p)\n sleep(1.0e-2)\n end\n return sum(tsol.u[end])\nend\n\nusing Test\nfunction runtests()\n testval = 46.66666666647518\n return @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\n nothing\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: geomspace, simplexgrid\nusing GridVisualize\nusing OrdinaryDiffEqSDIRK\n\nfunction main(;\n nref = 0, Plotter = nothing, verbose = false,\n unknown_storage = :sparse, assembly = :edgewise,\n time_embedding = :none,\n L = 1.0, R = 1.0, D = 1.0, C = 1.0,\n ω0 = 1.0e-3, ω1 = 5.0e1\n )","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 = 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 = simplexgrid(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 return nothing\n end\n\n storage = function (f, u, node, data)\n f[1] = data.C * u[1]\n return nothing\n end\n\n reaction = function (f, u, node, data)\n f[1] = data.R * u[1]\n return nothing\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 return nothing\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(\n 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\n )","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 tend = 1.0\n if time_embedding == :builtin\n tsol = solve(sys; inival = 0.0, params = [1.0], times = (0.0, tend), force_first_step = true)\n steadystate = tsol.u[end]\n elseif time_embedding == :ordinarydiffeq\n inival = unknowns(sys, inival = 0)\n problem = ODEProblem(sys, inival, (0, tend); params = [1.0])\n odesol = solve(problem, ImplicitEuler())\n tsol = reshape(odesol, sys)\n steadystate = tsol.u[end]\n elseif time_embedding == :none\n steadystate = solve(sys; inival = 0.0, params = [1.0])\n else\n error(\"time_embedding must be one of :builtin, :ordinarydiffeq, :none\")\n end\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 return 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 return 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 vis = GridVisualizer(; Plotter = Plotter)\n scalarplot!(\n vis, real(allIxL), imag(allIxL); label = \"exact\", color = :red,\n linestyle = :dot\n )\n scalarplot!(\n vis, real(allIL), imag(allIL); label = \"calc\", show = true, clear = false,\n color = :blue, linestyle = :solid\n )\n\n return sum(allIL)\nend\n\nusing Test\nfunction runtests()\n testval = 57.92710286186797 + 23.163945443946027im\n for unknown_storage in (:sparse, :dense)\n for assembly in (:edgewise, :cellwise)\n for time_embedding in (:none, :builtin, :ordinarydiffeq)\n @test main(; unknown_storage, assembly, time_embedding) ≈ testval\n end\n end\n end\n return nothing\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/Example003_Solvers/#003:-New-linear-solver-API","page":"003: New linear solver API","title":"003: New linear solver API","text":"","category":"section"},{"location":"module_examples/Example003_Solvers/","page":"003: New linear solver API","title":"003: New linear solver API","text":"(source code)","category":"page"},{"location":"module_examples/Example003_Solvers/","page":"003: New linear solver API","title":"003: New linear solver API","text":"module Example003_Solvers\n\n# under development\n\nusing Printf\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\nusing LinearSolve\nusing ExtendableSparse\nusing ExtendableSparse: ILUZeroPreconBuilder, JacobiPreconBuilder, SmoothedAggregationPreconBuilder\nusing SparseArrays\nusing AMGCLWrap\nusing AlgebraicMultigrid\nusing LinearAlgebra\n\n\nusing Test\n\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 = simplexgrid(X, Y)\n nn = num_nodes(grid)\n\n eps = 1.0e-2\n\n function reaction(f, u, node, data)\n f[1] = u[1]^2\n return nothing\n end\n\n function flux(f, u, edge, data)\n f[1] = eps * (u[1, 1]^2 - u[1, 2]^2)\n return nothing\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.0 * (x1^2 + x2^2))\n return nothing\n end\n\n function storage(f, u, node, data)\n f[1] = u[1]\n return nothing\n end\n\n function bcondition(f, u, node, data)\n boundary_dirichlet!(\n f,\n u,\n node;\n species = 1,\n region = 2,\n value = ramp(node.time; dt = (0, 0.1), du = (0, 1))\n )\n boundary_dirichlet!(\n f,\n u,\n node;\n species = 1,\n region = 4,\n value = ramp(node.time; dt = (0, 0.1), du = (0, 1))\n )\n return nothing\n end\n\n sys = VoronoiFVM.System(\n grid; reaction, flux, source, storage, bcondition, assembly,\n species = [1]\n )\n @info \"UMFPACK:\"\n umf_sol = solve(sys; inival = 0.5, method_linear = LinearSolve.UMFPACKFactorization(), kwargs...)\n\n @info \"KLU:\"\n sol = solve(sys; inival = 0.5, method_linear = LinearSolve.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 = LinearSolve.SparspakFactorization(), kwargs...)\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n\n @info \"Krylov-ilu0:\"\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(precs = ILUZeroPreconBuilder()),\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n\n @info \"Krylov-block1\"\n precs = BlockPreconBuilder(; precs = ILUZeroPreconBuilder(), partitioning = A -> [1:(size(A, 1) ÷ 2), (size(A, 1) ÷ 2 + 1):size(A, 1)])\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(; precs),\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n\n @info \"Krylov-block2\"\n precs = BlockPreconBuilder(; precs = ILUZeroPreconBuilder(), partitioning = A -> [1:2:size(A, 1), 2:2:size(A, 1)])\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(; precs),\n log = true,\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n\n @info \"Krylov - delayed factorization:\"\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(; precs = LinearSolvePreconBuilder(SparspakFactorization())),\n keepcurrent_linear = false,\n log = true,\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n @test summary(history(sol)).factorizations == 1\n\n @info \"Krylov - jacobi:\"\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(; precs = JacobiPreconBuilder()),\n keepcurrent_linear = true, log = true,\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n @test summary(history(sol)).factorizations > 1\n\n @info \"Krylov - SA_AMG:\"\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(; precs = SmoothedAggregationPreconBuilder()),\n keepcurrent_linear = true,\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n\n @info \"Krylov - AMGCL_AMG:\"\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(; precs = AMGPreconBuilder()),\n keepcurrent_linear = true,\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n\n @info \"Krylov - AMGnCL_RLX:\"\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(; precs = RLXPreconBuilder()),\n keepcurrent_linear = true,\n kwargs...\n )\n return @test norm(sol - umf_sol, Inf) < 1.0e-7\nend\n\nfunction runtests()\n @testset \"edgewise\" begin\n main(; assembly = :edgewise)\n end\n @testset \"cellwise\" begin\n main(; assembly = :cellwise)\n end\n return nothing\nend\nend","category":"page"},{"location":"module_examples/Example003_Solvers/","page":"003: New linear solver API","title":"003: New linear solver API","text":"","category":"page"},{"location":"module_examples/Example003_Solvers/","page":"003: New linear solver API","title":"003: New linear solver API","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\nimport Metis\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 = simplexgrid(X, Y)\n\n grid = partition(grid, PlainMetisPartitioning(npart = 10))\n @show grid\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 return nothing\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 return nothing\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 return nothing\n end\n\n function storage!(f, u, node, data)\n f[1] = u[1]\n f[2] = u[2]\n return nothing\n end\n\n physics = VoronoiFVM.Physics(;\n data = data,\n flux = flux!,\n storage = storage!,\n reaction = reaction!,\n source = source!\n )\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 testval = 0\n p = GridVisualizer(; Plotter = Plotter, layout = (2, 1))\n @time while time < 1\n time = time + tstep\n U = solve(sys; inival, control, tstep)\n inival .= U\n testval = sum(U)\n tstep *= 1.0\n istep = istep + 1\n scalarplot!(p[1, 1], grid, U[1, :]; clear = true, limits = (0, 0.5))\n scalarplot!(p[2, 1], grid, U[2, :]; show = true, limits = (0, 0.5))\n end\n return testval\nend\n\nusing Test\nfunction runtests()\n testval = 16.01812472041518\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\n return nothing\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, data::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, data::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 nofunc\n\n\n\n\n\n","category":"type"},{"location":"internal/#VoronoiFVM.ResEvaluator-Union{Tuple{Tv}, Tuple{Any, 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 nofunc\n\n\n\n\n\n","category":"type"},{"location":"internal/#VoronoiFVM.ResJacEvaluator-Union{Tuple{Tv}, Tuple{Any, Any, Symbol, Vector{Tv}, Any, Int64}} where Tv","page":"Internal API","title":"VoronoiFVM.ResJacEvaluator","text":"ResJacEvaluator(physics, data, 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/#Manipulating-systems","page":"Internal API","title":"Manipulating systems","text":"","category":"section"},{"location":"internal/","page":"Internal API","title":"Internal API","text":"VoronoiFVM.update_grid_cellwise!\nVoronoiFVM.update_grid_edgewise!\nVoronoiFVM.sysmutatelock\nVoronoiFVM._complete!","category":"page"},{"location":"internal/#VoronoiFVM.update_grid_cellwise!","page":"Internal API","title":"VoronoiFVM.update_grid_cellwise!","text":"update_grid_cellwise!(system)\n\nUpdate cellwise assembly data for new grid\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.update_grid_edgewise!","page":"Internal API","title":"VoronoiFVM.update_grid_edgewise!","text":"update_grid_edgewise!(system)\n\nUpdate edgewise assembly data for new grid\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.sysmutatelock","page":"Internal API","title":"VoronoiFVM.sysmutatelock","text":"const sysmutatelock\n\nReentrant lock to safeguard mutating methods _complete! and update_grid!.\n\n\n\n\n\n","category":"constant"},{"location":"internal/#VoronoiFVM._complete!","page":"Internal API","title":"VoronoiFVM._complete!","text":"_complete!(system)\n\nUpdate grid and compile species information for system. Uses a lock to ensure parallel access.\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\npcolor_partitions::Vector\npartition_cells::Vector\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\npcolor_partitions::Vector\npartition_nodes::Vector\npartition_edges::Vector\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\nVoronoiFVM.increase_num_species!\nVoronoiFVM.addzrows\nVoronoiFVM.dofs","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/#VoronoiFVM.increase_num_species!","page":"Internal API","title":"VoronoiFVM.increase_num_species!","text":" increase_num_species!(system,maxspec)\n\nIncrease number of species in system to maxspec by adding new rows to all relevant matrices.\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.addzrows","page":"Internal API","title":"VoronoiFVM.addzrows","text":"addzrows(matrix,maxrow)\n\nReturn matrix with number of rows increased to maxrow, and set the new elements to zero.\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.dofs","page":"Internal API","title":"VoronoiFVM.dofs","text":"dofs(a)\n\n\nVector of degrees of freedom in solution array.\n\n\n\n\n\ndofs(a)\n\n\nVector of degrees of freedom in sparse solution array.\n\n\n\n\n\n","category":"function"},{"location":"internal/#Geometry-data","page":"Internal API","title":"Geometry data","text":"","category":"section"},{"location":"internal/","page":"Internal API","title":"Internal API","text":"VoronoiFVM.AbstractGeometryItem\nVoronoiFVM.AbstractNode\nVoronoiFVM.AbstractNodeData\nVoronoiFVM.AbstractEdge\nVoronoiFVM.AbstractEdgeData\nVoronoiFVM.outflownode!\nVoronoiFVM.NodeUnknowns\nVoronoiFVM.NodeRHS","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} <: 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} <: 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/#VoronoiFVM.outflownode!","page":"Internal API","title":"VoronoiFVM.outflownode!","text":"outflownode!(edge)\n\nSet edge.outflownode entry.\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.NodeUnknowns","page":"Internal API","title":"VoronoiFVM.NodeUnknowns","text":"struct NodeUnknowns{Tv, Tc, Tp, Ti} <: VoronoiFVM.AbstractNodeData{Tv}\n\nUnknown data on node. \n\n\n\n\n\n","category":"type"},{"location":"internal/#VoronoiFVM.NodeRHS","page":"Internal API","title":"VoronoiFVM.NodeRHS","text":"struct NodeRHS{Tv, Tc, Tp, Ti} <: VoronoiFVM.AbstractNodeData{Tv}\n\nRHS data on 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.factorizationstrategy\nVoronoiFVM.solve_step!\nVoronoiFVM.solve_transient!\nVoronoiFVM.eval_and_assemble\nVoronoiFVM._eval_and_assemble_generic_operator\nVoronoiFVM._addnz\nVoronoiFVM._add","category":"page"},{"location":"internal/#VoronoiFVM.factorizationstrategy","page":"Internal API","title":"VoronoiFVM.factorizationstrategy","text":"factorizationstrategy(preconditioner, blockstratrgy, system)\n\nCreate a factorizations strategy from preconditioner and block information\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.solve_step!","page":"Internal API","title":"VoronoiFVM.solve_step!","text":"solve_step!(\n state,\n solution,\n oldsol,\n control,\n time,\n tstep,\n embedparam,\n params\n)\n\n\nSolve time step problem. This is the core routine for implicit Euler and stationary solve.\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.solve_transient!","page":"Internal API","title":"VoronoiFVM.solve_transient!","text":" solve_transient(inival, system, times; kwargs...)\n\nSolve transient or embedding problem.\n\n\n\n\n\n","category":"function"},{"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 matrix,\n dudp,\n time,\n tstep,\n λ,\n data,\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._eval_and_assemble_generic_operator","page":"Internal API","title":"VoronoiFVM._eval_and_assemble_generic_operator","text":"Evaluate and assemble jacobian for generic operator part.\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_addnz(matrix, i, j, v, fac, part)\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::VoronoiFVM.DenseSolutionArray, idof, val) -> Any\n\n\nAdd residual value into global degree of freedom\n\n(Internal method)\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(internal)\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!(state, 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, state, t)\n\n\nInterpret the discrete problem as an ODE/DAE problem. Provide the rhs function for SciMLBase.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, state, t)\n\n\nInterpret the discrete problem as an ODE/DAE problem. Provide the jacobi matrix calculation function for SciMLBase.ODEFunction\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.mass_matrix","page":"Internal API","title":"VoronoiFVM.mass_matrix","text":"mass_matrix(state)\n\n\nCalculate the mass matrix for use with SciMLBase.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!(state, 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":"internal/#Misc-tools","page":"Internal API","title":"Misc tools","text":"","category":"section"},{"location":"internal/","page":"Internal API","title":"Internal API","text":"VoronoiFVM.solutionarray\nVoronoiFVM.integrate(::Type{<:Cartesian2D}, coordl, coordr, hnormal, velofunc; kwargs...)\nVoronoiFVM.integrate(::Type{<:Cylindrical2D}, coordl, coordr, hnormal, velofunc; kwargs...)\nVoronoiFVM.doolittle_ludecomp!\nVoronoiFVM.doolittle_lusolve!\nVoronoiFVM.bernoulli_horner\nVoronoiFVM._print_error","category":"page"},{"location":"internal/#VoronoiFVM.solutionarray","page":"Internal API","title":"VoronoiFVM.solutionarray","text":"solutionarray(a::Matrix)\n\n\n\n\n\nsolutionarray(a::SparseMatrixCSC)\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.integrate-Tuple{Type{<:Cartesian2D}, Vararg{Any, 4}}","page":"Internal API","title":"VoronoiFVM.integrate","text":"integrate(, coordl, coordr, hnormal, velofunc; kwargs...)\n\n\nThis is an internal function to integrate velofunc along the edge sigma=overlinemathttcoordlmathttcoordr between the x_K and x_L where mathtthnormal=x_K-x_L using Simpson's Rule. To be precise, compute for a cartesian coordinate system: int_sigma mathbfv cdot mathbfn mathrmds lvert x_K - x_L rvert lvertsigmarvert.\n\n\n\n\n\n","category":"method"},{"location":"internal/#VoronoiFVM.integrate-Tuple{Type{<:Cylindrical2D}, Vararg{Any, 4}}","page":"Internal API","title":"VoronoiFVM.integrate","text":"integrate(, coordl, coordr, hnormal, velofunc; kwargs...)\n\n\nThis is an internal function similar to integrate(::Type{<:Cartesian2D},...), but computes instead int_sigma r mathbfv cdot mathbfn mathrmds lvert x_K - x_L rvert left ( lvertsigmarvert r(mathrmmid(sigma)) right ) where r(mathrmmid(sigma)) is the r-coordinate of the mid-point of sigma.\n\n\n\n\n\n","category":"method"},{"location":"internal/#VoronoiFVM.doolittle_ludecomp!","page":"Internal API","title":"VoronoiFVM.doolittle_ludecomp!","text":"doolittle_ludecomp!(LU)\n\n\nNon-pivoting inplace LU factorization using Doolittle's method. Adapted from https://en.wikipedia.org/wiki/LUdecomposition#MATLABcode_example.\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.doolittle_lusolve!","page":"Internal API","title":"VoronoiFVM.doolittle_lusolve!","text":"doolittle_lusolve!(LU, b)\n\n\nNon-pivoting inplace upper and lower triangular solve of matrix factorized with doolittle_ludecomp!. Adapted from https://en.wikipedia.org/wiki/LUdecomposition#MATLABcode_example.\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM.bernoulli_horner","page":"Internal API","title":"VoronoiFVM.bernoulli_horner","text":"bernoulli_horner(x)\n\n\nCalculation of Bernoulli function via Horner scheme based on Taylor coefficients around 0.\n\n\n\n\n\n","category":"function"},{"location":"internal/#VoronoiFVM._print_error","page":"Internal API","title":"VoronoiFVM._print_error","text":"Print error when catching exceptions\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 = simplexgrid(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, data)\n f[1] = ϵ * (u[1, 1] - u[1, 2])\n return nothing\n end\n\n # Source term\n function source!(f, node, data)\n if node[1] <= 0.5\n f[1] = 1\n else\n f[1] = -1\n end\n return nothing\n end\n\n # Reaction term\n function reaction!(f, u, node, data)\n f[1] = exp(u[1]) - exp(-u[1])\n return nothing\n end\n\n # Create a physics structure\n physics = VoronoiFVM.Physics(;\n flux = flux!,\n source = source!,\n reaction = reaction!\n )\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\n return nothing\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\nusing OrdinaryDiffEqRosenbrock\nusing SciMLBase: NoInit\n\nfunction reaction(f, u, node, data)\n k = data.k\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 return nothing\nend\n\nfunction source(f, node, data)\n if node.region == 1\n f[1] = 1.0e-4 * (3.0 - node[1])\n end\n return nothing\nend\n\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.\nfunction correctionflux(f, u, edge, data)\n eps = data.eps\n for i in 1:3\n f[i] = eps[i] * (u[i, 1] - u[i, 2])\n end\n return nothing\nend\n\nfunction correctionstorage(f, u, node, data)\n f .= u\n return nothing\nend\n\n# This is the \"old\" way:\n# Write into result only where\n# the corresponding species has been enabled\nfunction pickyflux(f, u, edge, data)\n eps = data.eps\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 return nothing\nend\n\nfunction pickystorage(f, u, node, data)\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 return nothing\nend\n\n\nfunction main(;\n n = 30, Plotter = nothing, plot_grid = false, verbose = false,\n unknown_storage = :sparse, tend = 10,\n diffeq = false,\n rely_on_corrections = false, assembly = :edgewise\n )\n\n X = range(0, 3, length = n)\n grid = simplexgrid(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 data = (eps = [1, 1, 1], k = [1, 1, 1])\n\n flux = rely_on_corrections ? correctionflux : pickyflux\n storage = rely_on_corrections ? correctionstorage : pickystorage\n\n sys = VoronoiFVM.System(\n grid; data,\n flux, reaction, storage, source,\n unknown_storage, assembly\n )\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 function plot_timestep(U, time)\n U1 = view(U[1, :], subgrid1)\n U2 = view(U[2, :], subgrid2)\n U3 = view(U[3, :], subgrid3)\n\n scalarplot!(\n p[1, 1], subgrid1, U1; label = \"spec1\", color = :darkred,\n xlimits = (0, 3), flimits = (0, 1.0e-3),\n title = @sprintf(\"three regions t=%.3g\", time)\n )\n scalarplot!(\n p[1, 1], subgrid2, U2; label = \"spec2\", color = :green,\n clear = false\n )\n scalarplot!(\n p[1, 1], subgrid3, U3; label = \"spec3\", color = :navyblue,\n clear = false, show = true\n )\n return if ismakie(Plotter)\n sleep(0.02)\n end\n end\n\n if diffeq\n inival = unknowns(sys, inival = 0)\n problem = ODEProblem(sys, inival, (0, tend))\n # use fixed timesteps just for the purpose of CI\n odesol = solve(problem, Rosenbrock23(), initializealg = NoInit(), dt = 1.0e-2, adaptive = false)\n tsol = reshape(odesol, sys)\n else\n tsol = solve(\n sys; inival = 0, times = (0, tend),\n verbose, Δu_opt = 1.0e-5,\n method_linear = KLUFactorization()\n )\n end\n\n testval = 0.0\n for i in 2:length(tsol.t)\n ui = view(tsol, 2, :, i)\n Δt = tsol.t[i] - tsol.t[i - 1]\n testval += sum(view(ui, subgrid2)) * Δt\n end\n\n if !isnothing(Plotter)\n for i in 2:length(tsol.t)\n plot_timestep(tsol.u[i], tsol.t[i])\n end\n end\n return testval\nend\n\nusing Test\n\nfunction runtests()\n testval = 0.06922262169719146\n testvaldiffeq = 0.06889809741891571\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\n\n\n @test main(; diffeq = true, unknown_storage = :sparse, rely_on_corrections = false, assembly = :edgewise) ≈ testvaldiffeq\n @test main(; diffeq = true, unknown_storage = :dense, rely_on_corrections = false, assembly = :edgewise) ≈ testvaldiffeq\n @test main(; diffeq = true, unknown_storage = :sparse, rely_on_corrections = true, assembly = :edgewise) ≈ testvaldiffeq\n @test main(; diffeq = true, unknown_storage = :dense, rely_on_corrections = true, assembly = :edgewise) ≈ testvaldiffeq\n @test main(; diffeq = true, unknown_storage = :sparse, rely_on_corrections = false, assembly = :cellwise) ≈ testvaldiffeq\n @test main(; diffeq = true, unknown_storage = :dense, rely_on_corrections = false, assembly = :cellwise) ≈ testvaldiffeq\n @test main(; diffeq = true, unknown_storage = :sparse, rely_on_corrections = true, assembly = :cellwise) ≈ testvaldiffeq\n @test main(; diffeq = true, unknown_storage = :dense, rely_on_corrections = true, assembly = :cellwise) ≈ testvaldiffeq\n return nothing\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/","page":"Changelog","title":"Changelog","text":"using Markdown\nMarkdown.parse(read(\"../../CHANGELOG.md\",String))","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 callbacks 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":"plutostatichtml_examples/bernoulli/","page":"Bernoulli function test","title":"Bernoulli function test","text":"\n\n\n\n
begin\n    import Pkg as _Pkg\n    haskey(ENV, \"PLUTO_PROJECT\") && _Pkg.activate(ENV[\"PLUTO_PROJECT\"])\n    using CairoMakie\n    using Revise\n    using VoronoiFVM\n    using ForwardDiff: derivative\nend
\n\n\n\n

Bernoulli function test

We test the implementation of the Bernoulli function in VoronoiFVM against the evaluation with BigFloat. This allows to optimize thresholds for switching between evaluation expressions.

\n\n\n

Reference with BigFLoat

\n\n
function B_Big(x)\n    bx = BigFloat(x)\n    return Float64(bx / expm1(bx))\nend
\n
B_Big (generic function with 1 method)
\n\n
function DB_Big(x)\n    bx = BigFloat(x)\n    bone = one(BigFloat)\n    bex = exp(bx)\n    b = -(x * bex - bex + bone) / ((bex - bone) * (bex - bone))\n    return Float64(b)\nend
\n
DB_Big (generic function with 1 method)
\n\n\n

Implementation using expm1

\n\n
B(x) = x / expm1(x)
\n
B (generic function with 1 method)
\n\n","category":"page"},{"location":"plutostatichtml_examples/bernoulli/#Approximation-for-small-x","page":"Bernoulli function test","title":"Approximation for small x","text":"","category":"section"},{"location":"plutostatichtml_examples/bernoulli/","page":"Bernoulli function test","title":"Bernoulli function test","text":"
\n

For small values of x, a Taylor approximation implemented using a Horner scheme is utilized, as the exponential expression runs into errors in the vicinity of zero and fails to evaluate at zero.. As as long as its error is large than that of the Taylor approximation calculated with the Taylor scheme, we should use the later one.

\n\n
B(0.0)
\n
NaN
\n\n
fbernoulli(0.0)
\n
1.0
\n\n
B(nextfloat(0.0))
\n
1.0
\n\n
fbernoulli(nextfloat(0.0))
\n
1.0
\n\n
derivative(B, 0.0)
\n
NaN
\n\n
derivative(fbernoulli, 0.0)
\n
-0.5
\n\n
derivative(B, nextfloat(0.0))
\n
NaN
\n\n
derivative(fbernoulli, nextfloat(0.0))
\n
-0.5
\n\n
smallX = collect(-0.5:(1.0e-4 + 1.0e-8):0.5);
\n\n\n\n\n\n\n\n\n","category":"page"},{"location":"plutostatichtml_examples/bernoulli/#Error-comparison-for-VoronoiFVM-implementation","page":"Bernoulli function test","title":"Error comparison for VoronoiFVM implementation","text":"","category":"section"},{"location":"plutostatichtml_examples/bernoulli/","page":"Bernoulli function test","title":"Bernoulli function test","text":"
\n
\n\n
largeX = -100:1.00001e-3:100;
\n\n\n\n\n\n\n\n\n\n

Derivative error

\n\n\n\n\n\n
\n
\n

Built with Julia 1.11.1 and

\nCairoMakie 0.12.16
\nForwardDiff 0.10.38
\nPkg 1.11.0
\nRevise 3.5.18
\nVoronoiFVM 1.25.1\n
\n\n","category":"page"},{"location":"plutostatichtml_examples/bernoulli/","page":"Bernoulli function test","title":"Bernoulli function test","text":"EditURL = \"https://github.com/WIAS-PDELib/VoronoiFVM.jl/blob/master/nothing\"","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, ExtendableGrids\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, data)\n f[1] = u[1, 1] - u[1, 2]\n return nothing\n end\n\n function bcond!(f, u, node, data)\n boundary_dirichlet!(f, u, node; region = 1, value = 0)\n boundary_dirichlet!(f, u, node; region = 2, value = 1)\n return nothing\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 = simplexgrid(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\n return nothing\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 n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise,\n dim = 2, tend = 5, dt = 0.2\n )\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, data)\n f .= u\n return nothing\n end\n\n function flux(f, u, edge, data)\n f[1] = u[1, 1] - u[1, 2]\n f[2] = u[2, 1] - u[2, 2]\n return nothing\n end\n\n r(u1, u2) = u1 - 0.1 * u2\n\n function reaction(f, u, node, data)\n f[1] = r(u[1], u[2])\n f[2] = -r(u[1], u[2])\n return nothing\n end\n\n function source(f, node, data)\n f[1] = 1.0\n return nothing\n end\n\n physics = VoronoiFVM.Physics(;\n flux = flux,\n storage = storage,\n reaction = reaction,\n source = source\n )\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(;\n Plotter = Plotter, layout = (1, 2), resolution = (600, 300),\n fignumber = 1\n )\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, data) -> source(f, node, data), 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(;\n Plotter = Plotter, layout = (1, 2), resolution = (600, 300),\n fignumber = 4\n )\n\n for i in 1:length(tsol)\n sol = tsol.u[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 in 2:length(tsol)\n ofr = integrate(system, T, tsol.u[i], tsol.u[i - 1], tsol.t[i] - tsol.t[i - 1])\n push!(outflow_rate, ofr[2])\n end\n\n vis2 = GridVisualizer(;\n Plotter = Plotter, layout = (1, 1), resolution = (600, 300),\n fignumber = 2\n )\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 in 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.u[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 return 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)\n return nothing\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 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 ###########################################################################\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, data)\n f[1] = d1 * (u[1, 1] - u[1, 2])\n return nothing\n end\n\n function reaction!(f, u, node, data)\n f[1] = k1[node.index] * u[1]\n return nothing\n end\n\n function source!(f, node::VoronoiFVM.Node, data)\n f[1] = c1[node.index]\n return nothing\n end\n\n sys_1D = VoronoiFVM.System(\n grid_1D,\n VoronoiFVM.Physics(;\n flux = flux!, reaction = reaction!,\n source = source!\n )\n )","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(;\n Plotter = Plotter, layout = (2, 1), clear = true,\n resolution = (800, 500)\n )\n\n scalarplot!(\n p[1, 1], grid_1D, sol_1D[1, :]; show = true,\n title = \"1D calculation (d1 = $d1, kmax = $kmax, cmax = $cmax)\"\n )\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, data)\n f[ispec_2D] = d2 * (u[ispec_2D, 1] - u[ispec_2D, 2])\n return nothing\n end\n\n function reaction2D!(f, u, node, data)\n f[ispec_2D] = k2 * u[ispec_2D]\n return nothing\n end\n\n function source2D!(f, node, data)\n f[ispec_2D] = c2\n return nothing\n end\n\n #### discretization functions for boundary species at active boundary ####\n function bflux!(f, u, bedge, data)\n if bedge.region == active_boundary\n f[ispec_boundary] = db * (u[ispec_boundary, 1] - u[ispec_boundary, 2])\n end\n return nothing\n end\n\n function breaction!(f, u, bnode, data)\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 return nothing\n end\n\n function bsource!(f, bnode, data)\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 return nothing\n end\n\n sys_2D = VoronoiFVM.System(\n grid_2D,\n VoronoiFVM.Physics(;\n flux = flux2D!, reaction = reaction2D!,\n source = source2D!,\n bflux = bflux!, breaction = breaction!,\n bsource = bsource!\n );\n unknown_storage = unknown_storage, assembly = assembly\n )","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 return nothing\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!(\n 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\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\n return nothing\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":"extensions/#[ExtendableFEMBase](https://github.com/chmerdon/ExtendableFEMBase.jl/)-Extension","page":"ExtendableFEMBase Extension","title":"ExtendableFEMBase Extension","text":"","category":"section"},{"location":"extensions/","page":"ExtendableFEMBase Extension","title":"ExtendableFEMBase Extension","text":"The extension for ExtendableFEMBase extends the functions below for solution types of ExtendableFEM. The name of the extension originates from the solution type FEVectorBlock that is defined in ExtendableFEMBase.","category":"page"},{"location":"extensions/","page":"ExtendableFEMBase Extension","title":"ExtendableFEMBase Extension","text":"edgevelocities(grid::ExtendableGrid, vel::FEVectorBlock; kwargs...)\nbfacevelocities(grid::ExtendableGrid, vel::FEVectorBlock; kwargs...)","category":"page"},{"location":"extensions/#VoronoiFVM.edgevelocities-Tuple{ExtendableGrid, FEVectorBlock}","page":"ExtendableFEMBase Extension","title":"VoronoiFVM.edgevelocities","text":"edgevelocities(grid, vel; kwargs...)\n\n\nCompute VoronoiFVM.edgevelocities for a finite element flow field computed by ExtendableFEM.\n\n\n\n\n\n","category":"method"},{"location":"extensions/#VoronoiFVM.bfacevelocities-Tuple{ExtendableGrid, FEVectorBlock}","page":"ExtendableFEMBase Extension","title":"VoronoiFVM.bfacevelocities","text":"bfacevelocities(grid, vel; kwargs...)\n\n\nCompute VoronoiFVM.bfacevelocities for a finite element flow field computed by ExtendableFEM.\n\n\n\n\n\n","category":"method"},{"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 n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse,\n dim = 2, assembly = :edgewise\n )\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, data)\n f .= u\n return nothing\n end\n\n function flux(f, u, edge, data)\n f[1] = u[1, 1] - u[1, 2]\n return nothing\n end\n\n function breaction(f, u, node, data)\n if node.region == Γ_where_T_equal_1[1]\n f[1] = u[1]^2\n end\n return nothing\n end\n\n physics = VoronoiFVM.Physics(;\n flux = flux,\n storage = storage,\n breaction = breaction\n )\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 return 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)\n return nothing\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\nimport Metis\n\n# Flux function which describes the flux\n# between neighboring control volumes\nfunction g!(f, u, edge, data)\n f[1] = u[1, 1] - u[1, 2]\n return nothing\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 = simplexgrid(X, X)\n grid = partition(grid, PlainMetisPartitioning(npart = 20))\n @show grid\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, vscale = 0.5, rasterpoints = 10)\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\n return nothing\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 OrdinaryDiffEqBDF\n    using OrdinaryDiffEqRosenbrock\n    using OrdinaryDiffEqSDIRK\n    using LinearAlgebra\n    using PlutoUI\n    using ExtendableGrids\n    using DataStructures\n    using GridVisualize, CairoMakie\n    CairoMakie.activate!(type = \"png\")\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 order 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            y[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\n    return nothing\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}}(\n  grid = ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=1, nnodes=151, ncells=150,\n  nbfaces=2),  \n  physics = Physics(data=@NamedTuple{c::Float64, bctype::Symbol}, flux=flux,\n  storage=storage, reaction=reaction, breaction=brea, ),  \n  num_species = 2)
\n\n\n

Perturbation in the center of the domain:

\n\n
begin\n    inival = unknowns(sys, inival = 0)\n    inival[1, :] .= map(x -> cos(κ * π * x) * exp(-x^2 / 0.1), grid)\nend;
\n\n\n
problem = ODEProblem(sys, inival, (0.0, tend));
\n\n\n
tsol = solve(\n    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\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
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.11.1 and

\n\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/WIAS-PDELib/VoronoiFVM.jl/blob/master/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 LinearSolve\nimport Triangulate\nimport Metis\n\nfunction main(;\n nref = 0, Plotter = nothing, verbose = \"and\", unknown_storage = :sparse, assembly = :edgewise,\n ythin = 0.25\n )\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 grid = partition(grid, PlainMetisPartitioning(npart = 20); nodes = true, edges = true)\n @show grid\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, data)\n y[iT] = c * u[iT]\n return nothing\n end\n\n κ(T) = κ0 * exp(α * (T - T0))\n\n function flux!(y, u, edge, data)\n y[iϕ] = κ(y[iT]) * (u[iϕ, 1] - u[iϕ, 2])\n y[iT] = λ * (u[iT, 1] - u[iT, 2])\n return nothing\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, data)\n y[iT] = -κ(y[iT]) * (u[iϕ, 1] - u[iϕ, 2]) * (u[iϕ, 1] - u[iϕ, 2])\n return nothing\n end\n\n function bcondition!(y, u, node, data)\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 return nothing\n end\n\n sys = VoronoiFVM.System(\n grid; bcondition = bcondition!, flux = flux!,\n edgereaction = jouleheat!, storage = storage!,\n species = [iϕ, iT], assembly = assembly\n )\n\n sol = solve(\n sys; verbose,\n method_linear = KrylovJL_BICGSTAB(precs = LinearSolvePreconBuilder(UMFPACKFactorization())),\n keepcurrent_linear = false\n )\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 return norm(sol, Inf)\nend\n\nusing Test\nfunction runtests()\n testval = 24.639120035942938\n @test main(; assembly = :edgewise) ≈ testval &&\n main(; assembly = :cellwise) ≈ testval\n return nothing\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 OrdinaryDiffEqBDF\n    using OrdinaryDiffEqRosenbrock\n    using OrdinaryDiffEqSDIRK\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 conditions 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, data)\n        f[1] = u[1, 1]^m - u[1, 2]^m\n        return nothing\n    end\n\n    storage!(f, u, node, data) = f[1] = u[1]\n\n    sys = VoronoiFVM.System(grid, flux = flux!, storage = storage!, species = 1)\n    return sys, X\nend
\n
create_porous_medium_problem (generic function with 1 method)
\n\n
begin\n    function 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        return sol, sys, err\n    end\n    run_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        state = VoronoiFVM.SystemState(sys)\n        problem = ODEProblem(state, inival, (t0, tend))\n        odesol = solve(problem, solver)\n        sol = reshape(odesol, sys; state)\n        err = norm(sol[1, :, end] - map(x -> barenblatt(x, tend, m), X))\n        return sol, sys, err\n    end\n    for method in diffeqmethods\n        run_diffeq(m = 2, n = 10, solver = 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
t1 = @elapsed sol1, sys1, err1 = run_vfvm(m = m, n = n);history_summary(sol1)
\n
(seconds = 2.21, tasm = 2.02, tlinsolve = 0.16, 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(sol2)
\n
(nd = 165, njac = 82, nf = 248)
\n\n\n\n\n\n

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

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

\n\n
@test err2 < err1
\n
Test Passed
\n
\n

Built with Julia 1.11.1 and

\n\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/WIAS-PDELib/VoronoiFVM.jl/blob/master/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: ILUZeroPreconBuilder\nusing GridVisualize\nusing LinearSolve\nusing ILUZero\n\nfunction main(;\n n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse,\n method_linear = nothing, assembly = :edgewise\n )\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 = simplexgrid(X, Y)\n\n eps = 1.0e-2\n\n physics = VoronoiFVM.Physics(;\n reaction = function (f, u, node, data)\n f[1] = u[1]^2\n return nothing\n end, flux = function (f, u, edge, data)\n f[1] = eps * (u[1, 1]^2 - u[1, 2]^2)\n return nothing\n end, source = function (f, node, data)\n x1 = node[1] - 0.5\n x2 = node[2] - 0.5\n f[1] = exp(-20.0 * (x1^2 + x2^2))\n return nothing\n end, storage = function (f, u, node, data)\n f[1] = u[1]\n return nothing\n end\n )\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 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(;\n unknown_storage = :sparse, method_linear = KrylovJL_CG(precs = ILUZeroPreconBuilder()),\n assembly = :edgewise\n ) ≈ testval &&\n main(;\n unknown_storage = :dense, method_linear = KrylovJL_CG(precs = ILUZeroPreconBuilder()),\n assembly = :edgewise\n ) ≈ testval &&\n main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval &&\n main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval &&\n main(;\n unknown_storage = :sparse, method_linear = KrylovJL_CG(precs = ILUZeroPreconBuilder()),\n assembly = :cellwise\n ) ≈ testval &&\n main(;\n unknown_storage = :dense, method_linear = KrylovJL_CG(precs = ILUZeroPreconBuilder()),\n assembly = :cellwise\n ) ≈ testval\n return nothing\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(\n range(0, L; length = 10 * 2^nref),\n range(0, H; length = 10 * 2^nref)\n )\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, data)\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 return nothing\n end\n\n function outflow!(f, u, node, data)\n if node.region == Γ_out\n f[1] = bfvelo[node.ibnode, node.ibface] * u[1]\n end\n return nothing\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 return 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)\n return nothing\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\n return nothing\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\n return nothing\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]\n return nothing\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 = simplexgrid(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\n return nothing\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, data)\n f[1] = u[1, 1] - u[1, 2]\n return nothing\nend\n\nfunction s(f, node, data)\n n = view(node.coord, :, node.index)\n f[1] = n[1] * sin(5.0 * n[2]) * exp(n[3])\n return nothing\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 = simplexgrid(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\n return nothing\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 = simplexgrid(collect(0:h:1))\n\n eps::Vector{Float64} = [1.0, 1.0]\n\n physics = VoronoiFVM.Physics(\n ; reaction = function (f, u, node, data)\n f[1] = u[1] * u[2]\n f[2] = -u[1] * u[2]\n return nothing\n end,\n flux = function (f, u, edge, data)\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 return nothing\n end,\n source = function (f, node, data)\n f[1] = 1.0e-4 * (0.01 + node[1])\n f[2] = 1.0e-4 * (0.01 + 1.0 - node[1])\n return nothing\n end,\n storage = function (f, u, node, data)\n f[1] = u[1]\n f[2] = u[2]\n return nothing\n end\n )\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!(\n p[2, 1], grid, U[2, :]; clear = true, title = \"U2, eps=$(xeps)\",\n reveal = true\n )\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\n\n return nothing\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 = \"png\", 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(;\n        resolution = (500, 200),\n        xlabel = \"t\",\n        ylabel = \"outflow\",\n        legend = :rb,\n        title = \"Breakthrough Curves\"\n    )\n    scalarplot!(p1, sol_n.t, bt_n; label = \"naive grid\", color = :red)\n    scalarplot!(\n        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    )\n    scalarplot!(\n        p1,\n        sol_f.t,\n        bt_f;\n        label = \"grid with fix\",\n        markershape = :circle,\n        color = :green,\n        clear = false\n    )\n    scalarplot!(\n        p1,\n        sol_ϕ.t,\n        bt_ϕ;\n        label = \"modified ϕ\",\n        markershape = :cross,\n        color = :blue,\n        clear = false\n    )\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.00023622450350365277

  • 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    return 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    return 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 in 2:length(sol.t)\n        of[i] = -integrate(sys, tf, sol.u[i], sol.u[i - 1], t[i] - t[i - 1])[ic]\n    end\n    return of\nend
\n
breakthrough (generic function with 1 method)
\n\n\n

Transient solver:

\n\n
function trsolve(\n        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    )\n    function flux(y, u, edge, data)\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        return nothing\n    end\n\n    function stor(y, u, node, data)\n        y[ip] = 0\n        y[ic] = ϕ[node.region] * u[ic]\n        return nothing\n    end\n\n    dim = dim_space(grid)\n    function bc(y, u, bnode, data)\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        return nothing\n    end\n\n    sys = VoronoiFVM.System(\n        grid;\n        check_allocs = true,\n        flux = flux,\n        storage = stor,\n        bcondition = bc,\n        species = [ip, ic],\n    )\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(\n        sys;\n        inival = inival,\n        times = [0, tend],\n        Δt = 1.0e-4,\n        Δt_min = 1.0e-6,\n    )\n\n    bt = breakthrough(sys, tfc, sol)\n    if dim == 1\n        bt = bt * W\n    end\n\n    return 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, data)\n        y[1] = D[edge.region] * (u[1, 1] - u[1, 2])\n        return nothing\n    end\n\n    function rea(y, u, node, data)\n        y[1] = R[node.region] * u[1]\n        return nothing\n    end\n    function bc(y, u, bnode, data)\n        boundary_dirichlet!(y, u, bnode, 1, Γ_in, 1)\n        boundary_dirichlet!(y, u, bnode, 1, Γ_out, 0)\n        return nothing\n    end\n    sys = VoronoiFVM.System(\n        grid;\n        flux = flux,\n        reaction = rea,\n        species = 1,\n        bcondition = bc,\n        check_allocs = true\n    )\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    return 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.11.1 and

\nCairoMakie 0.12.16
\nExtendableGrids 1.9.2
\nGridVisualize 1.7.0
\nPkg 1.11.0
\nPlutoUI 0.7.60
\nRevise 3.5.18
\nTest 1.11.0
\nVoronoiFVM 1.25.1\n
\n\n","category":"page"},{"location":"plutostatichtml_examples/problemcase/","page":"A case for caution","title":"A case for caution","text":"EditURL = \"https://github.com/WIAS-PDELib/VoronoiFVM.jl/blob/master/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 updating CITATION.bib if you have published work which could be added to this list.","category":"page"},{"location":"","page":"Home","title":"Home","text":"D. Brust, M. Wullenkord, H. G. Gómez, J. Albero and C. Sattler. Experimental Investigation of Photo-Thermal Catalytic Reactor for the Reverse Water Gas Shift Reaction under Concentrated Irradiation. Journal of Environmental Chemical Engineering, 113372 (2024).\n\n\n\nD. Abdel. Modeling and simulation of vacancy-assisted charge transport in innovative semiconductor devices. PhD thesis, Freie Universität Berlin (2024).\n\n\n\nD. Brust, K. Hopf, J. Fuhrmann, A. Cheilytko, M. Wullenkord and C. Sattler. Transport of Heat and Mass for Reactive Gas Mixtures in Porous Media: Modeling and Application (SSRN, 2024).\n\n\n\nM. U. Qureshi, S. Matera, D. Runge, C. Merdon, J. Fuhrmann, J.-U. Repke and G. Brösigke. Reduced Order Cfd Modeling Approach Based on the Asymptotic Expansion-An Application for Heterogeneous Catalytic Systems (SSRN, 2024).\n\n\n\nT. Belin, P. Lafitte-Godillon, V. Lescarret, J. Fuhrmann and C. Mascia. Entropy solutions of a diffusion equation with discontinuous hysteresis and their finite volume approximation (HAL, 2024).\n\n\n\nS. 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(\n range(0, L; length = 20 * 2^nref),\n range(0, H; length = 5 * 2^nref)\n )\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, data)\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 return nothing\n end\n\n function outflow!(f, u, node, data)\n if node.region == 2\n f[1] = bfvelo[node.ibnode, node.ibface] * u[1]\n end\n return nothing\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 in 1:length(tsol.t)\n scalarplot!(\n vis[1, 1], grid, tsol[1, :, i]; flimits = (0, cin + 1.0e-5),\n title = @sprintf(\"time=%3f\", tsol.t[i]), show = true\n )\n end\n return tsol\nend\n\nusing Test\nfunction runtests()\n tsol1 = main(; assembly = :edgewise)\n tsol2 = main(; assembly = :cellwise)\n @test all(tsol1.u[end] .≈ 1)\n @test all(tsol1.u[end] .≈ 1)\n return nothing\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 OrdinaryDiffEqRosenbrock\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 n = 20, m = 2, Plotter = nothing, verbose = false,\n unknown_storage = :sparse, tend = 0.01, assembly = :edgewise, solver = Rosenbrock23()\n )\n\n # Create a one-dimensional discretization\n h = 1.0 / convert(Float64, n / 2)\n X = collect(-1:h:1)\n grid = simplexgrid(X)\n\n # Flux function which describes the flux\n # between neighboring control volumes\n function flux!(f, u, edg, data)\n f[1] = u[1, 1]^m - u[1, 2]^m\n return nothing\n end\n\n # Storage term\n function storage!(f, u, node, data)\n f[1] = u[1]\n return nothing\n end\n\n # Create a physics structure\n physics = VoronoiFVM.Physics(;\n flux = flux!,\n storage = storage!\n )\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 in 1:length(tsol)\n time = tsol.t[i]\n scalarplot!(\n p[1, 1], grid, tsol[1, :, i]; title = @sprintf(\"t=%.3g\", time),\n color = :red, label = \"numerical\",\n markershape = :circle, markevery = 1\n )\n scalarplot!(\n p[1, 1], grid, map(x -> barenblatt(x, time, m), grid); clear = false,\n color = :green,\n label = \"exact\", markershape = :none\n )\n reveal(p)\n sleep(1.0e-2)\n end\n return sum(tsol.u[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\n return nothing\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 = \"png\", 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, data)\n        f[1] = u[1]^2\n        return nothing\n    end\n\n    function flux(f, u, edge, data)\n        f[1] = eps * (u[1, 1]^2 - u[1, 2]^2)\n        return nothing\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.0 * (x1^2 + x2^2))\n        return nothing\n    end\n\n    function storage(f, u, node, data)\n        f[1] = u[1]\n        return nothing\n    end\n\n    function bcondition(f, u, node, data)\n        boundary_dirichlet!(\n            f,\n            u,\n            node;\n            species = 1,\n            region = 2,\n            value = ramp(node.time; dt = (0, 0.1), du = (0, 1))\n        )\n        boundary_dirichlet!(\n            f,\n            u,\n            node;\n            species = 1,\n            region = 4,\n            value = ramp(node.time; dt = (0, 0.1), du = (0, 1))\n        )\n        return nothing\n    end\n\n    sys0 = VoronoiFVM.System(\n        0.0:h:1.0,\n        0.0:h:1.0;\n        reaction,\n        flux,\n        source,\n        storage,\n        bcondition,\n        species = [1],\n    )\nend
\n
VoronoiFVM.System{Float64, Float64, Int32, Int64, Matrix{Int32}}(\n  grid = ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=2, nnodes=10201,\n  ncells=20000, nbfaces=400),  \n  physics = Physics(flux=flux, storage=storage, reaction=reaction, source=source,\n  breaction=bcondition, ),  \n  num_species = 1)
\n\n\n

Deprecated call:

\n\n\n

begin inival = unknowns(sys0; inival = 0.1) sol00 = unknowns(sys0) solve!(sol00, inival, sys0) end

\n\n\n

Replace this by:

\n\n
sol0 = solve(sys0; inival = 0.1)
\n
1×10201 VoronoiFVM.DenseSolutionArray{Float64, 2}:\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...)

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.

  • 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:

    • UMFPACKFactorization() for sparse matrices with Float64

    • SparspakFactorization() for sparse matrices with general number types.

    • Defaults from LinearSolve.jl for tridiagonal and banded matrices

    Users should experiment with what works best for their problem.

    For an overeview on possible alternatives, see 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 ? Translates to reuse_precs=!keepcurrent_linear for LinearSolve.

  • Δ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 embedding 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 VoronoiFVM.DenseSolutionArray{Float64, 2}:\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 VoronoiFVM.DenseSolutionArray{Float64, 2}:\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(\n    sys0;\n    inival = 0.1,\n    method_linear = KrylovJL_BICGSTAB(),\n    precon_linear = JacobiPreconditioner,\n    verbose = true,\n)
\n
1×10201 VoronoiFVM.DenseSolutionArray{Float64, 2}:\n 1.02242e-33  0.0319717  0.045243  0.0554731  …  0.045243  0.0319717  1.02242e-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(\n    sys0;\n    inival = 0.1,\n    method_linear = KrylovJL_BICGSTAB(),\n    precon_linear = SparspakFactorization(),\n    verbose = \"nlad\",\n)
\n
1×10201 VoronoiFVM.DenseSolutionArray{Float64, 2}:\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(\n    sys0;\n    inival = 0.5,\n    method_linear = KrylovJL_BICGSTAB(),\n    precon_linear = ILUZeroPreconditioner,\n    verbose = true,\n)
\n
1×10201 VoronoiFVM.DenseSolutionArray{Float64, 2}:\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, data)\n    return 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}}(\n  grid = ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=1, nnodes=1001, ncells=1000,\n  nbfaces=2),  \n  physics = Physics(flux=xflux, storage=default_storage, ),  \n  num_species = 1)
\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, data)\n    f[1] = D1 * (u[1, 1]^2 - u[1, 2]^2)\n    return nothing\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}}(\n  grid = ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=1, nnodes=1001, ncells=1000,\n  nbfaces=2),  \n  physics = Physics(flux=xflux1, storage=default_storage, ),  \n  num_species = 1)
\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, data)\n    for i in 1:(edge.nspec)\n        y[i] = u[i, 1] - u[i, 2]\n    end\n    return nothing\nend
\n
multispecies_flux (generic function with 1 method)
\n\n
function test_reaction(y, u, node, data)\n    y[1] = u[1]\n    y[2] = -u[1]\n    return nothing\nend
\n
test_reaction (generic function with 1 method)
\n\n
begin\n    system1 = VoronoiFVM.System(\n        grid1;\n        flux = multispecies_flux,\n        reaction = test_reaction,\n        species = [1, 2]\n    )\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, data)\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)\n    return nothing\nend;
\n\n\n
system2 = VoronoiFVM.System(\n    grid1;\n    flux = multispecies_flux,\n    reaction = test_reaction,\n    species = [1, 2],\n    bcondition = bcond2,\n    check_allocs = false\n);
\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, data)\n    boundary_dirichlet!(y, u, bnode; region = 4, value = bnode[2])\n    boundary_dirichlet!(y, u, bnode; region = 2, value = -bnode[2])\n    return nothing\nend;
\n\n\n
system3 = VoronoiFVM.System(\n    -1:0.1:1,\n    -1:0.1:1;\n    flux = multispecies_flux,\n    bcondition = bcond3,\n    species = 1\n);
\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, data) = y[1] = -bnode[1]^2 + u[1]^4;
\n\n\n
bc4(f, u, node, data) = boundary_dirichlet!(f, u, node; value = 0);
\n\n\n
system4 = VoronoiFVM.System(\n    -10:0.1:10;\n    species = [1],\n    reaction = reaction4,\n    flux = multispecies_flux,\n    bcondition = bc4\n);
\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.11.1 and

\nCairoMakie 0.12.16
\nExtendableGrids 1.9.2
\nExtendableSparse 1.5.3
\nGridVisualize 1.7.0
\nILUZero 0.2.0
\nLinearAlgebra 1.11.0
\nLinearSolve 2.34.0
\nPkg 1.11.0
\nPlutoUI 0.7.60
\nRevise 3.5.18
\nTest 1.11.0
\nVoronoiFVM 1.25.1\n
\n\n","category":"page"},{"location":"plutostatichtml_examples/api-update/","page":"API Updates","title":"API Updates","text":"EditURL = \"https://github.com/WIAS-PDELib/VoronoiFVM.jl/blob/master/nothing\"","category":"page"},{"location":"solutions/#Solution-objects","page":"Solution objects","title":"Solution objects","text":"","category":"section"},{"location":"solutions/","page":"Solution objects","title":"Solution objects","text":"AbstractSolutionArray","category":"page"},{"location":"solutions/#VoronoiFVM.AbstractSolutionArray","page":"Solution objects","title":"VoronoiFVM.AbstractSolutionArray","text":"abstract type AbstractSolutionArray{T, N} <: AbstractArray{T, N}\n\nAbstract type for stationary solution. Subtype of AbstractArray.\n\n\n\n\n\n","category":"type"},{"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":"mutable struct DenseSolutionArray{T, N} <: AbstractSolutionArray{T, N}\n\nDense storage of solution. Subtype of AbstractSolutionArray\n\nFields:\n\nu::Array\nhistory::Union{Nothing, NewtonSolverHistory}\n\n\n\n\n\n","category":"type"},{"location":"solutions/#VoronoiFVM.DenseSolutionArray-Union{Tuple{Int64, Int64}, Tuple{T}} where T","page":"Solution objects","title":"VoronoiFVM.DenseSolutionArray","text":"DenseSolutionArray constructor.\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.DenseSolutionArray-Union{Tuple{Matrix{T}}, Tuple{T}} where T","page":"Solution objects","title":"VoronoiFVM.DenseSolutionArray","text":"DenseSolutionArray(\n u::Array{T, 2}\n) -> VoronoiFVM.DenseSolutionArray{_A, 2} where _A\n\n\nDenseSolutionArray constructor.\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.DenseSolutionArray-Union{Tuple{UndefInitializer, Int64, Int64}, Tuple{T}} where T","page":"Solution objects","title":"VoronoiFVM.DenseSolutionArray","text":"DenseSolutionArray constructor.\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM._add-Tuple{VoronoiFVM.DenseSolutionArray, Any, Any}","page":"Solution objects","title":"VoronoiFVM._add","text":"_add(U::VoronoiFVM.DenseSolutionArray, idof, val) -> Any\n\n\nAdd residual value into global degree of freedom\n\n(Internal method)\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM._set-Tuple{VoronoiFVM.DenseSolutionArray, Any, Any}","page":"Solution objects","title":"VoronoiFVM._set","text":"_set(U::VoronoiFVM.DenseSolutionArray, idof, val) -> Any\n\n\nSet residual value for global degree of freedom\n\n(Internal method)\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.dof-Tuple{VoronoiFVM.DenseSolutionArray, Any, Any}","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.dofs-Tuple{VoronoiFVM.DenseSolutionArray}","page":"Solution objects","title":"VoronoiFVM.dofs","text":"dofs(a)\n\n\nVector of degrees of freedom in solution array.\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.solutionarray-Union{Tuple{Matrix{T}}, Tuple{T}} where T","page":"Solution objects","title":"VoronoiFVM.solutionarray","text":"solutionarray(a::Matrix)\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.unknown_indices-Tuple{VoronoiFVM.DenseSolutionArray}","page":"Solution objects","title":"VoronoiFVM.unknown_indices","text":"unknown_indices(a)\n\n\nReturn indices for dense 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":"mutable struct SparseSolutionArray{T, N, Ti} <: AbstractSolutionArray{T, N}\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\nu::SparseArrays.SparseMatrixCSC{T, Ti} where {T, Ti}: Sparse matrix holding actual data.\n\nhistory::Union{Nothing, NewtonSolverHistory}\n\n\n\n\n\n","category":"type"},{"location":"solutions/#VoronoiFVM.SparseSolutionArray-Union{Tuple{SparseArrays.SparseMatrixCSC{Tv, Ti}}, Tuple{Ti}, Tuple{Tv}} where {Tv, Ti}","page":"Solution objects","title":"VoronoiFVM.SparseSolutionArray","text":"SparseSolutionArray(\n a::SparseArrays.SparseMatrixCSC{Tv, Ti}\n) -> VoronoiFVM.SparseSolutionArray{_A, 2} where _A\n\n\nSparseSolutionArray constructor\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.SparseSolutionIndices","page":"Solution objects","title":"VoronoiFVM.SparseSolutionIndices","text":"struct SparseSolutionIndices\n\nWrapper structure to access sparse solution indices.\n\n\n\n\n\n","category":"type"},{"location":"solutions/#Base.copy-Union{Tuple{VoronoiFVM.SparseSolutionArray{T, N, Ti}}, Tuple{Ti}, Tuple{N}, Tuple{T}} where {T, N, 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, Int64, Int64}","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, Int64, Int64}","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{T, N, Ti}}, Tuple{Ti}, Tuple{N}, Tuple{T}} where {T, N, 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/#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(internal)\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM._set-Tuple{VoronoiFVM.SparseSolutionArray, Any, Any}","page":"Solution objects","title":"VoronoiFVM._set","text":"_set(U::VoronoiFVM.SparseSolutionArray, idof, val) -> Any\n\n\nSet residual value for global degree of freedom\n\n(internal)\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.dof-Tuple{VoronoiFVM.SparseSolutionArray, Any, Any}","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.dofs-Tuple{VoronoiFVM.SparseSolutionArray}","page":"Solution objects","title":"VoronoiFVM.dofs","text":"dofs(a)\n\n\nVector of degrees of freedom in sparse solution array.\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.solutionarray-Union{Tuple{SparseArrays.SparseMatrixCSC{Tv, Ti}}, Tuple{Ti}, Tuple{Tv}} where {Tv, Ti}","page":"Solution objects","title":"VoronoiFVM.solutionarray","text":"solutionarray(a::SparseMatrixCSC)\n\n\n\n\n\n","category":"method"},{"location":"solutions/#VoronoiFVM.unknown_indices-Tuple{VoronoiFVM.SparseSolutionArray}","page":"Solution objects","title":"VoronoiFVM.unknown_indices","text":"unknown_indices(a)\n\n\nReturn indices for 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::Union{VoronoiFVM.DiffEqHistory, 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/#Base.append!","page":"Solution objects","title":"Base.append!","text":"append!(tsol::AbstractTransientSolution, t, sol)\n\nAppend time step solution sol as solution at time t to tsol. sol will be directly references in tsol. This method does not copy sol. If during a time steping process it is the same vector, a copy should appended.\n\nDefined in VoronoiFVM.jl.\n\n\n\n\n\n","category":"function"},{"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 = simplexgrid(X, Y)\n\n k = 1.0\n eps::Float64 = 1.0\n physics = VoronoiFVM.Physics(;\n breaction = function (f, u, node, data)\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 return nothing\n end, bstorage = function (f, u, node, data)\n if node.region == 2\n f[3] = u[3]\n end\n return nothing\n end, flux = function (f, u, edge, data)\n f[1] = eps * (u[1, 1] - u[1, 2])\n f[2] = eps * (u[2, 1] - u[2, 2])\n return nothing\n end, source = function (f, node, data)\n x1 = node[1] - 0.5\n x2 = node[2] - 0.5\n f[1] = exp(-20.0 * (x1^2 + x2^2))\n return nothing\n end, storage = function (f, u, node, data)\n f[1] = u[1]\n f[2] = u[2]\n return nothing\n end\n )\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 return 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 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\n return nothing\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":"plutostatichtml_examples/heterogeneous-catalysis/","page":"Coupling with Catalyst.jl","title":"Coupling with Catalyst.jl","text":"\n\n\n\n
begin\n    using SciMLBase: ODEProblem, solve\n    using OrdinaryDiffEqLowOrderRK: DP5\n    using OrdinaryDiffEqRosenbrock: Rosenbrock23\n    using OrdinaryDiffEqTsit5: Tsit5\n    using Catalyst\n    using VoronoiFVM: VoronoiFVM, enable_species!, enable_boundary_species!\n    using VoronoiFVM: ramp, boundary_dirichlet!\n    using ExtendableGrids: simplexgrid\n    using GridVisualize: GridVisualize, GridVisualizer, reveal, scalarplot!, gridplot, available_kwargs\n    if doplots # defined in the Appendix\n        using Plots: Plots, plot, theme\n        using PlotThemes\n        Plots.gr()\n        Plots.theme(:dark)\n        GridVisualize.default_plotter!(Plots)\n    end\n    import PlutoUI\n    using Test\n    PlutoUI.TableOfContents(; depth = 4)\nend
\n\n\n\n

Towards Heterogeneous Catalysis

\n\n\n

How to model and simulate heterogeneous catalysis with Catalyst.jl and VoronoiFVM.jl.

\n\n","category":"page"},{"location":"plutostatichtml_examples/heterogeneous-catalysis/#Mass-action-kinetics","page":"Coupling with Catalyst.jl","title":"Mass action kinetics","text":"","category":"section"},{"location":"plutostatichtml_examples/heterogeneous-catalysis/","page":"Coupling with Catalyst.jl","title":"Coupling with Catalyst.jl","text":"
\n
\n\n\n\n\n\n

Assume \\(j=1 … M\\) reversible reactions of educt (substrate) species to product species

$$\tα_1^j S_1 + α_2^j S_2 + … + α_n^jS_n \\underset{k_j^+}{\\stackrel{k_j^-}{\\longrightleftharpoons}} β_1^jS_1 + β_2^j S_2 + … + β_n^j S_n$$

or equivalently,

$$∑_{i=1}^n α_{i}^j S_i \\underset{k_j^+}{\\stackrel{k_j^-}{\\longrightleftharpoons}} ∑_{i=1}^n β_i^j S_i $$

The rate of these reactions depend on the concentrations \\([S_i]\\) of the species. Within \\(Catalyst.jl\\), due to consistency with the derivation from stochastic approaches, the default \"combinatoric rate law\" is

$$ r_j=k_j^+ ∏_{i=1}^n \\frac{[S_i]^{α_i^j}}{α_i^j!} - k_j^- ∏_{i=1}^n \\frac{[S_i]^{β_i^j}}{β_i^j!} $$

while it appears that in most textbooks, the rate law is

$$ r_j=k_j^+ ∏_{i=1}^n [S_i]^{α_i^j} - k_j^- ∏_{i=1}^n [S_i]^{β_i^j}.$$

See the corresponding remark in the Catalyst.jl docs and the github issue. We will stick to the secobd version which can be achieved by setting combinatoric_ratelaws to false at appropriate places.

Later in the project we will see that instead of the concentrations, we need to work with so called activities.

\n\n\n

The numbers \\(σ_i^j=α_i^j-β_i^j\\) are the net stoichiometric coefficients of the system.

\n\n\n

The rate differential equations then are (TODO: check this)

$$\t∂_t [S_i] + \\sum_{j=1}^M \\sigma_i^jr_j = 0$$

These assume that the reactions take place in a continuously stirred tank reactor (CSTR) which means that we have complete mixing, and species concentrations are spatially constant, and we have just one concentration value for each species at a given point of time.

\n\n\n

Example 1: A \\(\\longrightleftharpoons\\) B

$$\\begin{aligned}\n A& \\underset{k_1^+}{\\stackrel{k_1^-}{\\longrightleftharpoons}} B\\\\\n r_1&= k_1^+ [A] - k_1^- [B]\\\\\n \\partial_t[A] &= -r_1\\\\\n \\partial_t[B] &= r_1\n\\end{aligned} $$

\n\n\n

Solution via plain ODE problem using OrdinaryDiffEq.jl:

\n\n\n

Set the parameters such that the forward reaction is faster then the backward reaction:

\n\n
p1 = (k_p = 1, k_m = 0.1)
\n
(k_p = 1, k_m = 0.1)
\n\n\n

Define an ODE function describing the right hand side of the ODE System:

\n\n
function example1(du, u, p, t)\n    (; k_p, k_m) = p\n    r1 = k_p * u[1] - k_m * u[2]\n    du[1] = -r1\n    return du[2] = r1\nend
\n
example1 (generic function with 1 method)
\n\n\n

Define some initial value:

\n\n
u1_ini = [1.0, 0.0]
\n
2-element Vector{Float64}:\n 1.0\n 0.0
\n\n\n

Create an solve the corresponding ODEProblem

\n\n
prob1 = ODEProblem(example1, u1_ini, (0, 10), p1)
\n
ODEProblem with uType Vector{Float64} and tType Int64. In-place: true\ntimespan: (0, 10)\nu0: 2-element Vector{Float64}:\n 1.0\n 0.0
\n\n
sol1 = solve(prob1, DP5())
\n
timestampvalue1value2
10.01.00.0
20.0009990010.9990020.000998452
30.0109890.9890770.0109229
40.1108890.8956070.104393
50.4188180.6644020.335598
60.8599810.4439120.556088
71.471250.2711230.728877
82.180130.1735570.826443
92.977590.1253020.874698
103.853570.1040430.895957
114.836140.09537550.904625
125.967130.09220440.907796
137.312950.0912110.908789
148.970330.09096420.909036
1510.00.09092690.909073
\n\n
doplots && plot(sol1; size = (600, 200))
\n\n\n\n

Mass conservation: adding the two reaction eqauations results in

$$\t\\partial_t ([A]+[B]) = 0,$$

therefore \\([A]+[B]\\) must be constant:

\n\n
all(s -> isapprox(s, sum(u1_ini)), sum(sol1; dims = 1))
\n
true
\n\n\n

Catalyst.@reaction_network

\n\n\n

Catalyst.jl provides a convenient way to define a reaction network, and the resulting reaction system. So we use this to build the same system:

\n\n
rn1 = @reaction_network rn1 begin\n    @combinatoric_ratelaws false\n    k_p, A --> B\n    k_m, B --> A\nend
\n

$$\\begin{align*}\n\\mathrm{A} &\\xrightleftharpoons[k_{m}]{k_{p}} \\mathrm{B} \n \\end{align*}$$

\n\n\n

The corresponding ODE system is:

\n\n
convert(ODESystem, rn1)
\n

$$\\begin{align}\n\\frac{\\mathrm{d} A\\left( t \\right)}{\\mathrm{d}t} &= k_{m} B\\left( t \\right) - k_{p} A\\left( t \\right) \\\\\n\\frac{\\mathrm{d} B\\left( t \\right)}{\\mathrm{d}t} &= - k_{m} B\\left( t \\right) + k_{p} A\\left( t \\right)\n\\end{align}$$

\n\n\n

Catalyst.jl adds a new method to the ODEProblem constructor which allows to pass a reaction nerwork:

\n\n
prob1n = ODEProblem(rn1, u1_ini, (0, 10.0), Dict(pairs(p1)))
\n
ODEProblem with uType Vector{Float64} and tType Float64. In-place: true\ntimespan: (0.0, 10.0)\nu0: 2-element Vector{Float64}:\n 1.0\n 0.0
\n\n
sol1n = solve(prob1n, Rosenbrock23())
\n
timestampA(t)B(t)
10.01.00.0
20.0001133860.9998870.000113379
30.001247250.9987540.0012464
40.008102660.9919330.00806667
50.01833760.9818460.0181539
60.03991150.9609510.0390486
70.06953730.9330540.066946
80.1171840.8900480.109952
90.1778980.8384120.161588
100.2614260.7727690.227231
...
\n\n
doplots && plot(sol1n; idxs = [rn1.A, rn1.B, rn1.A + rn1.B], size = (600, 200))
\n\n\n\n

Unraveling @reaction_network

Let us try to look behind the macro voodoo - this is necessary to build networks programmatically and is behind the translation from python to Julia in CatmapInterface.jl.

\n\n\n

It is interesting if there is a \"macro - less\" way to define variables, parameters and species.

\n\n
@variables t
\n

$$\\begin{equation}\n\\left[\n\\begin{array}{c}\nt \\\\\n\\end{array}\n\\right]\n\\end{equation}$$

\n\n
@parameters k_p k_m
\n

$$\\begin{equation}\n\\left[\n\\begin{array}{c}\nk_{p} \\\\\nk_{m} \\\\\n\\end{array}\n\\right]\n\\end{equation}$$

\n\n
@species A(t) B(t)
\n

$$\\begin{equation}\n\\left[\n\\begin{array}{c}\nA\\left( t \\right) \\\\\nB\\left( t \\right) \\\\\n\\end{array}\n\\right]\n\\end{equation}$$

\n\n\n

A reaction network can be combined from several reactions:

\n\n
r1p = Reaction(k_p, [A], [B], [1], [1])
\n
k_p, A --> B
\n\n
r1m = Reaction(k_m, [B], [A], [1], [1])
\n
k_m, B --> A
\n\n
rn1x = complete(ReactionSystem([r1p, r1m], t; name = :example1))
\n

$$\\begin{align*}\n\\mathrm{A} &\\xrightleftharpoons[k_{m}]{k_{p}} \\mathrm{B} \n \\end{align*}$$

\n\n\n

Once we are here, the rest remains the same.

\n\n
convert(ODESystem, rn1x)
\n

$$\\begin{align}\n\\frac{\\mathrm{d} A\\left( t \\right)}{\\mathrm{d}t} &= k_{m} B\\left( t \\right) - k_{p} A\\left( t \\right) \\\\\n\\frac{\\mathrm{d} B\\left( t \\right)}{\\mathrm{d}t} &= - k_{m} B\\left( t \\right) + k_{p} A\\left( t \\right)\n\\end{align}$$

\n\n
prob1x = ODEProblem(rn1x, u1_ini, (0, 10.0), Dict(pairs(p1)))
\n
ODEProblem with uType Vector{Float64} and tType Float64. In-place: true\ntimespan: (0.0, 10.0)\nu0: 2-element Vector{Float64}:\n 1.0\n 0.0
\n\n
sol1x = solve(prob1x, Rosenbrock23())
\n
timestampA(t)B(t)
10.01.00.0
20.0001133860.9998870.000113379
30.001247250.9987540.0012464
40.008102660.9919330.00806667
50.01833760.9818460.0181539
60.03991150.9609510.0390486
70.06953730.9330540.066946
80.1171840.8900480.109952
90.1778980.8384120.161588
100.2614260.7727690.227231
...
\n\n
doplots && plot(sol1x; size = (600, 200))
\n\n\n\n

Example 2: A + 2B \\(\\longrightleftharpoons\\) AB_2

\n\n
rn2 = @reaction_network rn2 begin\n    @combinatoric_ratelaws false\n    k_0A, ∅ --> A\n    k_0B, ∅ --> B\n    (k_1p, k_1m), A + 2B <--> AB_2\nend
\n

$$\\begin{align*}\n\\varnothing &\\xrightarrow{k_{0A}} \\mathrm{A} \\\\\n\\varnothing &\\xrightarrow{k_{0B}} \\mathrm{B} \\\\\n\\mathrm{A} + 2 \\mathrm{B} &\\xrightleftharpoons[k_{1m}]{k_{1p}} \\mathrm{AB_{2}} \n \\end{align*}$$

\n\n
convert(ODESystem, rn2)
\n

$$\\begin{align}\n\\frac{\\mathrm{d} A\\left( t \\right)}{\\mathrm{d}t} &= k_{0A} + k_{1m} \\mathrm{AB}_{2}\\left( t \\right) - \\left( B\\left( t \\right) \\right)^{2} k_{1p} A\\left( t \\right) \\\\\n\\frac{\\mathrm{d} B\\left( t \\right)}{\\mathrm{d}t} &= k_{0B} + 2 k_{1m} \\mathrm{AB}_{2}\\left( t \\right) - 2 \\left( B\\left( t \\right) \\right)^{2} k_{1p} A\\left( t \\right) \\\\\n\\frac{\\mathrm{d} \\mathrm{AB}_{2}\\left( t \\right)}{\\mathrm{d}t} &= - k_{1m} \\mathrm{AB}_{2}\\left( t \\right) + \\left( B\\left( t \\right) \\right)^{2} k_{1p} A\\left( t \\right)\n\\end{align}$$

\n\n
p2 = (k_0A = 0.5, k_0B = 1, k_1p = 0.1, k_1m = 1.0e-5)
\n
(k_0A = 0.5, k_0B = 1, k_1p = 0.1, k_1m = 1.0e-5)
\n\n
u2_ini = (A = 0, B = 0, AB_2 = 0)
\n
(A = 0, B = 0, AB_2 = 0)
\n\n
prob2 = ODEProblem(rn2, Dict(pairs(u2_ini)), (0, 20.0), Dict(pairs(p2)))
\n
ODEProblem with uType Vector{Float64} and tType Float64. In-place: true\ntimespan: (0.0, 20.0)\nu0: 3-element Vector{Float64}:\n 0.0\n 0.0\n 0.0
\n\n
sol2 = solve(prob2, Rosenbrock23())
\n
timestampA(t)B(t)AB_2(t)
10.00.00.00.0
20.00015.0e-50.00016.25e-19
30.00110.000550.00111.08006e-14
40.01110.005550.01111.13501e-10
50.0584360.02921790.05843589.95852e-8
60.08245190.04122540.08245095.19335e-7
70.1417660.07087850.1417574.69768e-6
80.1787550.08936510.178731.23077e-5
90.2419570.1209370.2418744.17015e-5
100.2876190.1437260.2874518.40283e-5
...
\n\n
doplots && plot(sol2; legend = :topleft, size = (600, 300))
\n\n\n\n

Example 3: Catalysis for A + 2B \\(\\rightleftharpoons\\) AB_2

\n\n\n

The same reaction as in example 2, but now with a catalyst C.

The reaction between A and B takes places when A and B are bound (adsorbed) to the catalyst. So we have adsorption reactions, reactions at the catalyst, and desorption. The overall of free and bound catalyst needs to be constant over time.

\n\n
rn3 = @reaction_network rn3 begin\n    @combinatoric_ratelaws false\n    k_0A, ∅ --> A\n    k_0B, ∅ --> B\n    (k_1p, k_1m), A + C <--> CA\n    (k_2p, k_2m), B + C <--> CB\n    (k_3p, k_3m), CA + 2CB <--> CAB2 + 2C\n    (k_4p, k_4m), CAB2 <--> AB2 + C\nend
\n

$$\\begin{align*}\n\\varnothing &\\xrightarrow{k_{0A}} \\mathrm{A} \\\\\n\\varnothing &\\xrightarrow{k_{0B}} \\mathrm{B} \\\\\n\\mathrm{A} + \\mathrm{C} &\\xrightleftharpoons[k_{1m}]{k_{1p}} \\mathrm{CA} \\\\\n\\mathrm{B} + \\mathrm{C} &\\xrightleftharpoons[k_{2m}]{k_{2p}} \\mathrm{CB} \\\\\n\\mathrm{CA} + 2 \\mathrm{CB} &\\xrightleftharpoons[k_{3m}]{k_{3p}} \\mathrm{CAB2} + 2 \\mathrm{C} \\\\\n\\mathrm{CAB2} &\\xrightleftharpoons[k_{4m}]{k_{4p}} \\mathrm{AB2} + \\mathrm{C} \n \\end{align*}$$

\n\n
convert(ODESystem, rn3)
\n

$$\\begin{align}\n\\frac{\\mathrm{d} A\\left( t \\right)}{\\mathrm{d}t} &= k_{0A} + k_{1m} \\mathrm{CA}\\left( t \\right) - k_{1p} A\\left( t \\right) C\\left( t \\right) \\\\\n\\frac{\\mathrm{d} B\\left( t \\right)}{\\mathrm{d}t} &= k_{0B} + k_{2m} \\mathrm{CB}\\left( t \\right) - k_{2p} B\\left( t \\right) C\\left( t \\right) \\\\\n\\frac{\\mathrm{d} C\\left( t \\right)}{\\mathrm{d}t} &= k_{1m} \\mathrm{CA}\\left( t \\right) + k_{2m} \\mathrm{CB}\\left( t \\right) + k_{4p} \\mathrm{CAB2}\\left( t \\right) - k_{1p} A\\left( t \\right) C\\left( t \\right) - k_{2p} B\\left( t \\right) C\\left( t \\right) - k_{4m} C\\left( t \\right) \\mathrm{AB2}\\left( t \\right) - 2 \\left( C\\left( t \\right) \\right)^{2} k_{3m} \\mathrm{CAB2}\\left( t \\right) + 2 \\left( \\mathrm{CB}\\left( t \\right) \\right)^{2} k_{3p} \\mathrm{CA}\\left( t \\right) \\\\\n\\frac{\\mathrm{d} \\mathrm{CA}\\left( t \\right)}{\\mathrm{d}t} &= - k_{1m} \\mathrm{CA}\\left( t \\right) + k_{1p} A\\left( t \\right) C\\left( t \\right) + \\left( C\\left( t \\right) \\right)^{2} k_{3m} \\mathrm{CAB2}\\left( t \\right) - \\left( \\mathrm{CB}\\left( t \\right) \\right)^{2} k_{3p} \\mathrm{CA}\\left( t \\right) \\\\\n\\frac{\\mathrm{d} \\mathrm{CB}\\left( t \\right)}{\\mathrm{d}t} &= - k_{2m} \\mathrm{CB}\\left( t \\right) + k_{2p} B\\left( t \\right) C\\left( t \\right) + 2 \\left( C\\left( t \\right) \\right)^{2} k_{3m} \\mathrm{CAB2}\\left( t \\right) - 2 \\left( \\mathrm{CB}\\left( t \\right) \\right)^{2} k_{3p} \\mathrm{CA}\\left( t \\right) \\\\\n\\frac{\\mathrm{d} \\mathrm{CAB2}\\left( t \\right)}{\\mathrm{d}t} &= - k_{4p} \\mathrm{CAB2}\\left( t \\right) + k_{4m} C\\left( t \\right) \\mathrm{AB2}\\left( t \\right) - \\left( C\\left( t \\right) \\right)^{2} k_{3m} \\mathrm{CAB2}\\left( t \\right) + \\left( \\mathrm{CB}\\left( t \\right) \\right)^{2} k_{3p} \\mathrm{CA}\\left( t \\right) \\\\\n\\frac{\\mathrm{d} \\mathrm{AB2}\\left( t \\right)}{\\mathrm{d}t} &= k_{4p} \\mathrm{CAB2}\\left( t \\right) - k_{4m} C\\left( t \\right) \\mathrm{AB2}\\left( t \\right)\n\\end{align}$$

\n\n
p3 = (\n    k_0A = 0.5, k_0B = 1,\n    k_1p = 10, k_1m = 0.1,\n    k_2p = 10, k_2m = 0.1,\n    k_3p = 10, k_3m = 0.1,\n    k_4p = 10, k_4m = 0.1,\n)
\n
(k_0A = 0.5, k_0B = 1, k_1p = 10, k_1m = 0.1, k_2p = 10, k_2m = 0.1, k_3p = 10, k_3m = 0.1, k_4p = 10, k_4m = 0.1)
\n\n
Cini = 40
\n
40
\n\n
u3_ini = (A = 0, B = 0, CA = 0, CB = 0, CAB2 = 0, AB2 = 0, C = Cini)
\n
(A = 0, B = 0, CA = 0, CB = 0, CAB2 = 0, AB2 = 0, C = 40)
\n\n
t3end = 200
\n
200
\n\n
prob3 = ODEProblem(rn3, Dict(pairs(u3_ini)), (0, t3end), Dict(pairs(p3)))
\n
ODEProblem with uType Vector{Float64} and tType Int64. In-place: true\ntimespan: (0, 200)\nu0: 7-element Vector{Float64}:\n  0.0\n  0.0\n 40.0\n  0.0\n  0.0\n  0.0\n  0.0
\n\n
sol3 = solve(prob3, Rosenbrock23())
\n
timestampA(t)B(t)C(t)CA(t)CB(t)CAB2(t)AB2(t)
10.00.00.040.00.00.00.00.0
26.46784e-63.22974e-66.45949e-640.04.17882e-98.35764e-94.74654e-318.99173e-36
37.11463e-53.50726e-57.01452e-540.05.00556e-71.00111e-61.20711e-232.28831e-27
40.0001750168.45196e-50.00016903940.02.9886e-65.9772e-61.52686e-205.17114e-24
50.00033990.000158920.0003178440.01.10301e-52.20603e-51.86355e-181.10544e-21
60.0005593470.0002506380.00050127739.99992.9035e-55.807e-56.46331e-175.76826e-20
70.000853060.0003614740.00072294939.99986.50556e-50.0001301111.1845e-151.55464e-18
80.001210110.0004798250.0009596539.99960.0001252320.0002504641.26499e-142.27535e-17
90.001650390.0006043420.0012086839.99930.0002208530.0004417069.78351e-142.37856e-16
100.002167790.0007252540.0014505139.99890.0003586380.0007172775.66062e-131.79861e-15
...
\n\n
doplots && plot(sol3; legend = :topleft, size = (600, 300))
\n\n\n
ctotal = rn3.C + rn3.CA + rn3.CB + rn3.CAB2
\n

$$\\begin{equation}\n\\mathrm{CAB2}\\left( t \\right) + \\mathrm{CA}\\left( t \\right) + \\mathrm{CB}\\left( t \\right) + C\\left( t \\right)\n\\end{equation}$$

\n\n
sol3[ctotal]
\n
148-element Vector{Float64}:\n 40.0\n 40.0\n 40.0\n 39.99999999999999\n 39.99999999999999\n 39.99999999999999\n 39.99999999999999\n  ⋮\n 39.99999999999902\n 39.99999999999902\n 39.99999999999902\n 39.99999999999902\n 39.99999999999902\n 39.99999999999926
\n\n
@test sol3[ctotal] ≈ fill(Cini, length(sol3))
\n
Test Passed
\n\n","category":"page"},{"location":"plutostatichtml_examples/heterogeneous-catalysis/#Example-4:-Heterogeneous-catalysis","page":"Coupling with Catalyst.jl","title":"Example 4: Heterogeneous catalysis","text":"","category":"section"},{"location":"plutostatichtml_examples/heterogeneous-catalysis/","page":"Coupling with Catalyst.jl","title":"Coupling with Catalyst.jl","text":"
\n

Heterogeneous catalysis assumes that the catalytic reaction takes place at surface. This means that reacting species need to be transported towards or away from the surface, and one has to model coupled transport and surface reaction.

Here we use VoronoiFVM.jl to model transport and Catalyst.jl to create the surface reaction network.

Problem specification

Assume \\(\\Omega=(0,1)\\) where a catalytic reaction takes place at \\(x=0\\). We assume that the educts A, B, and the product AB2 are bulk species transported to the domain. At \\(x=1\\) we set Dirichlet boundary conditions providing A,B and removing AB2.

A, B can adsorb at the catalyst at \\(x=0\\) and react to AB2 while adsorbed. The product desorbs and is released to the bulk. So we have

  • Mass transport in the interior of \\(\\Omega\\):

$$\\begin{aligned}\n\\partial_t c_A + \\nabla \\cdot D_A \\nabla c_A &=0\\\\\n\\partial_t c_B + \\nabla \\cdot D_B \\nabla c_B &=0\\\\\n\\partial_t c_{AB2} + \\nabla \\cdot D_{AB2} \\nabla c_{AB2} &=0\n\\end{aligned}$$

  • Coupled nonlinear robin boundary conditions at \\(x=0\\):

$$\\begin{aligned}\nD_A\\partial_n c_A + r_1 &= 0\\\\\nD_B\\partial_n c_A + r_2 &= 0\\\\\nD_{AB2}\\partial_n c_{AB2} - r_4 &= 0\\\\\n\\end{aligned}$$

  • \\(r_1, r_2\\) and \\(r_4\\) are asorption/desorption reactions:

$$\\begin{aligned}\n r_1&=k_{1p}c_A c_C - k_{1m}c_{CA}\\\\\n r_2&=k_{2p}c_B c_C - k_{2m}c_{CB}\\\\\n r_4&=k_{4p}c_{AB2} - k_{4m}c_{C_C}c_{C_{AB2}}\\\\\n\\end{aligned}$$

\n\n\n
  • The free catalyst sites C and the catalyst coverages CA, CB, CAB2 behave according to:

\n\n\n\n\n\n

$$\\begin{equation}\n\\frac{\\mathrm{d} C\\left( t \\right)}{\\mathrm{d}t} = k_{1m} \\mathrm{CA}\\left( t \\right) + k_{2m} \\mathrm{CB}\\left( t \\right) + k_{4p} \\mathrm{CAB2}\\left( t \\right) - k_{1p} A\\left( t \\right) C\\left( t \\right) - k_{2p} B\\left( t \\right) C\\left( t \\right) - k_{4m} C\\left( t \\right) \\mathrm{AB2}\\left( t \\right) - 2 \\left( C\\left( t \\right) \\right)^{2} k_{3m} \\mathrm{CAB2}\\left( t \\right) + 2 \\left( \\mathrm{CB}\\left( t \\right) \\right)^{2} k_{3p} \\mathrm{CA}\\left( t \\right)\n\\end{equation}$$

\n\n\n

$$\\begin{equation}\n\\frac{\\mathrm{d} \\mathrm{CA}\\left( t \\right)}{\\mathrm{d}t} = - k_{1m} \\mathrm{CA}\\left( t \\right) + k_{1p} A\\left( t \\right) C\\left( t \\right) + \\left( C\\left( t \\right) \\right)^{2} k_{3m} \\mathrm{CAB2}\\left( t \\right) - \\left( \\mathrm{CB}\\left( t \\right) \\right)^{2} k_{3p} \\mathrm{CA}\\left( t \\right)\n\\end{equation}$$

\n\n\n

$$\\begin{equation}\n\\frac{\\mathrm{d} \\mathrm{CB}\\left( t \\right)}{\\mathrm{d}t} = - k_{2m} \\mathrm{CB}\\left( t \\right) + k_{2p} B\\left( t \\right) C\\left( t \\right) + 2 \\left( C\\left( t \\right) \\right)^{2} k_{3m} \\mathrm{CAB2}\\left( t \\right) - 2 \\left( \\mathrm{CB}\\left( t \\right) \\right)^{2} k_{3p} \\mathrm{CA}\\left( t \\right)\n\\end{equation}$$

\n\n\n

$$\\begin{equation}\n\\frac{\\mathrm{d} \\mathrm{CAB2}\\left( t \\right)}{\\mathrm{d}t} = - k_{4p} \\mathrm{CAB2}\\left( t \\right) + k_{4m} C\\left( t \\right) \\mathrm{AB2}\\left( t \\right) - \\left( C\\left( t \\right) \\right)^{2} k_{3m} \\mathrm{CAB2}\\left( t \\right) + \\left( \\mathrm{CB}\\left( t \\right) \\right)^{2} k_{3p} \\mathrm{CA}\\left( t \\right)\n\\end{equation}$$

\n\n\n
  • Dirichlet boundary conditions at \\(x=1\\) :

$$\\begin{aligned}\n\tc_A&=1\\\\\n c_B&=1\\\\\n\tc_{AB2}&=0\n\\end{aligned}$$

\n\n\n

Finally, we set all initial concentrations to zero besides of the catalyst concenration (number of catalyst sites) \\(c_C|_{t=0}=C_0=1\\).

\n\n\n

Implementation

\n\n\n

Surface reaction network

\n\n\n

Define a reaction network under the assumption that the supply of A and B comes from the transport and does not need to be specified.

\n\n
rnv = @reaction_network rnv begin\n    @combinatoric_ratelaws false\n    (k_1p, k_1m), A + C <--> CA\n    (k_2p, k_2m), B + C <--> CB\n    (k_3p, k_3m), CA + 2CB <--> CAB2 + 2C\n    (k_4p, k_4m), CAB2 <--> AB2 + C\nend
\n

$$\\begin{align*}\n\\mathrm{A} + \\mathrm{C} &\\xrightleftharpoons[k_{1m}]{k_{1p}} \\mathrm{CA} \\\\\n\\mathrm{B} + \\mathrm{C} &\\xrightleftharpoons[k_{2m}]{k_{2p}} \\mathrm{CB} \\\\\n\\mathrm{CA} + 2 \\mathrm{CB} &\\xrightleftharpoons[k_{3m}]{k_{3p}} \\mathrm{CAB2} + 2 \\mathrm{C} \\\\\n\\mathrm{CAB2} &\\xrightleftharpoons[k_{4m}]{k_{4p}} \\mathrm{AB2} + \\mathrm{C} \n \\end{align*}$$

\n\n
odesys = convert(ODESystem, rnv)
\n

$$\\begin{align}\n\\frac{\\mathrm{d} A\\left( t \\right)}{\\mathrm{d}t} &= k_{1m} \\mathrm{CA}\\left( t \\right) - k_{1p} A\\left( t \\right) C\\left( t \\right) \\\\\n\\frac{\\mathrm{d} C\\left( t \\right)}{\\mathrm{d}t} &= k_{1m} \\mathrm{CA}\\left( t \\right) + k_{2m} \\mathrm{CB}\\left( t \\right) + k_{4p} \\mathrm{CAB2}\\left( t \\right) - k_{1p} A\\left( t \\right) C\\left( t \\right) - k_{2p} B\\left( t \\right) C\\left( t \\right) - k_{4m} C\\left( t \\right) \\mathrm{AB2}\\left( t \\right) - 2 \\left( C\\left( t \\right) \\right)^{2} k_{3m} \\mathrm{CAB2}\\left( t \\right) + 2 \\left( \\mathrm{CB}\\left( t \\right) \\right)^{2} k_{3p} \\mathrm{CA}\\left( t \\right) \\\\\n\\frac{\\mathrm{d} \\mathrm{CA}\\left( t \\right)}{\\mathrm{d}t} &= - k_{1m} \\mathrm{CA}\\left( t \\right) + k_{1p} A\\left( t \\right) C\\left( t \\right) + \\left( C\\left( t \\right) \\right)^{2} k_{3m} \\mathrm{CAB2}\\left( t \\right) - \\left( \\mathrm{CB}\\left( t \\right) \\right)^{2} k_{3p} \\mathrm{CA}\\left( t \\right) \\\\\n\\frac{\\mathrm{d} B\\left( t \\right)}{\\mathrm{d}t} &= k_{2m} \\mathrm{CB}\\left( t \\right) - k_{2p} B\\left( t \\right) C\\left( t \\right) \\\\\n\\frac{\\mathrm{d} \\mathrm{CB}\\left( t \\right)}{\\mathrm{d}t} &= - k_{2m} \\mathrm{CB}\\left( t \\right) + k_{2p} B\\left( t \\right) C\\left( t \\right) + 2 \\left( C\\left( t \\right) \\right)^{2} k_{3m} \\mathrm{CAB2}\\left( t \\right) - 2 \\left( \\mathrm{CB}\\left( t \\right) \\right)^{2} k_{3p} \\mathrm{CA}\\left( t \\right) \\\\\n\\frac{\\mathrm{d} \\mathrm{CAB2}\\left( t \\right)}{\\mathrm{d}t} &= - k_{4p} \\mathrm{CAB2}\\left( t \\right) + k_{4m} C\\left( t \\right) \\mathrm{AB2}\\left( t \\right) - \\left( C\\left( t \\right) \\right)^{2} k_{3m} \\mathrm{CAB2}\\left( t \\right) + \\left( \\mathrm{CB}\\left( t \\right) \\right)^{2} k_{3p} \\mathrm{CA}\\left( t \\right) \\\\\n\\frac{\\mathrm{d} \\mathrm{AB2}\\left( t \\right)}{\\mathrm{d}t} &= k_{4p} \\mathrm{CAB2}\\left( t \\right) - k_{4m} C\\left( t \\right) \\mathrm{AB2}\\left( t \\right)\n\\end{align}$$

\n\n\n

For coupling with VoronoiFVM we need species numbers which need to correspond to the species in our network:

\n\n
begin\n    smap = speciesmap(rnv)\n    const iA = smap[rnv.A]\n    const iB = smap[rnv.B]\n    const iC = smap[rnv.C]\n    const iCA = smap[rnv.CA]\n    const iCB = smap[rnv.CB]\n    const iCAB2 = smap[rnv.CAB2]\n    const iAB2 = smap[rnv.AB2]\nend;
\n\n\n\n

Grid:

\n\n
grid = simplexgrid(0:0.01:1)
\n
ExtendableGrids.ExtendableGrid{Float64, Int32}\n      dim =       1\n   nnodes =     101\n   ncells =     100\n  nbfaces =       2
\n\n
gridplot(grid, size = (600, 100))
\n\n\n\n

The grid has two boundary regions: region 1 at x=0 and region 2 at x=1.

\n\n\n

Reaction parameters:

\n\n
pcat = (\n    k_1p = 50, k_1m = 0.1,\n    k_2p = 50, k_2m = 0.1,\n    k_3p = 10, k_3m = 0.1,\n    k_4p = 50, k_4m = 0.1,\n)
\n
(k_1p = 50, k_1m = 0.1, k_2p = 50, k_2m = 0.1, k_3p = 10, k_3m = 0.1, k_4p = 50, k_4m = 0.1)
\n\n\n

Parameters for the VoronoiFVM system:

\n\n
params = (\n    D_A = 1.0,\n    D_B = 1.0,\n    D_AB2 = 1.0,\n    pcat = pcat,\n)
\n
(D_A = 1.0, D_B = 1.0, D_AB2 = 1.0, pcat = (k_1p = 50, k_1m = 0.1, k_2p = 50, k_2m = 0.1, k_3p = 10, k_3m = 0.1, k_4p = 50, k_4m = 0.1))
\n\n\n

Initial values for the reaction network (needed only for the definition of the ODE problem)

\n\n
C0 = 1.0
\n
1.0
\n\n
uv_ini = (A = 0, B = 0, CA = 0, CB = 0, CAB2 = 0, AB2 = 0, C = C0)
\n
(A = 0, B = 0, CA = 0, CB = 0, CAB2 = 0, AB2 = 0, C = 1.0)
\n\n
tvend = 200.0
\n
200.0
\n\n
const probv = ODEProblem(rnv, Dict(pairs(uv_ini)), (0, tvend), Dict(pairs(pcat)))
\n
ODEProblem with uType Vector{Float64} and tType Float64. In-place: true\ntimespan: (0.0, 200.0)\nu0: 7-element Vector{Float64}:\n 0.0\n 1.0\n 0.0\n 0.0\n 0.0\n 0.0\n 0.0
\n\n\n

Callback functions for VoronoiFVM

\n\n\n

First, define flux and storage functions for the bulk process:

\n\n
function storage(y, u, node, p)\n    y[iA] = u[iA]\n    y[iB] = u[iB]\n    return y[iAB2] = u[iAB2]\nend
\n
storage (generic function with 1 method)
\n\n
function flux(y, u, edge, p)\n    (; D_A, D_B, D_AB2) = p\n    y[iA] = D_A * (u[iA, 1] - u[iA, 2])\n    y[iB] = D_B * (u[iB, 1] - u[iB, 2])\n    return y[iAB2] = D_A * (u[iAB2, 1] - u[iAB2, 2])\nend
\n
flux (generic function with 1 method)
\n\n\n

Storage term for the surface reaction:

\n\n
function bstorage(y, u, bnode, p)\n    y[iC] = u[iC]\n    y[iCA] = u[iCA]\n    y[iCB] = u[iCB]\n    return y[iCAB2] = u[iCAB2]\nend
\n
bstorage (generic function with 1 method)
\n\n\n

Catalytic reaction. Here we use the right hand side function of the ODE problem generated above. In VoronoiFVM, reaction term are a the left hand side, so we need to multiply by -1.

Note that we need to pass the parameter record as generated for the ODE problem instead of pcat.

\n\n
function catreaction(f, u, bnode, p)\n    probv.f(f, u, probv.p, bnode.time)\n    for i in 1:length(f)\n        f[i] = -f[i]\n    end\n    return\nend
\n
catreaction (generic function with 1 method)
\n\n\n

Define the Dirichlet boundary condition at x=1 (region 2):

\n\n
function bulkbc(f, u, bnode, p)\n    v = ramp(bnode.time; du = (0.0, 1.0), dt = (0.0, 0.01))\n    boundary_dirichlet!(f, u, bnode; species = iA, value = v, region = 2)\n    boundary_dirichlet!(f, u, bnode; species = iB, value = v, region = 2)\n    return boundary_dirichlet!(f, u, bnode; species = iAB2, value = 0, region = 2)\nend
\n
bulkbc (generic function with 1 method)
\n\n\n

Dispatch the boundary conditions

\n\n
function breaction(f, u, bnode, p)\n    return if bnode.region == 1\n        catreaction(f, u, bnode, p)\n    else\n        bulkbc(f, u, bnode, p)\n    end\nend
\n
breaction (generic function with 1 method)
\n\n\n

Coupled transport-reaction system

\n\n\n

Define a VoronoiFVM system from grid, params and the callback functions and enable the bulk and boundary species. unknown_storage = :sparse means that the solution is stored as a nspecies x nnodes sparse matrix in order to take into account that the surface species are non-existent in the bulk. unknown_storage = :dense would store a full matrix and solve dummy equations for the surface species values in the bulk.

\n\n
begin\n    sys = VoronoiFVM.System(\n        grid; data = params,\n        flux, breaction, bstorage, storage,\n        unknown_storage = :sparse\n    )\n    enable_species!(sys, iA, [1])\n    enable_species!(sys, iB, [1])\n    enable_species!(sys, iAB2, [1])\n\n    enable_boundary_species!(sys, iC, [1])\n    enable_boundary_species!(sys, iCA, [1])\n    enable_boundary_species!(sys, iCB, [1])\n    enable_boundary_species!(sys, iCAB2, [1])\nend;
\n\n\n\n

Define an initial value for sys:

\n\n
begin\n    u0 = VoronoiFVM.unknowns(sys; inival = 0)\n    u0[iC, 1] = C0\nend;
\n\n\n\n

Solution

\n\n\n

Solve the time evolution

\n\n
tsol = solve(sys; inival = u0, times = (1.0e-4, tvend));
\n\n\n\n

t:

\n\n
let\n    t_plot = round(10^log_t_plot; sigdigits = 3)\n    vis = GridVisualizer(; size = (600, 300), flimits = (0, 1), title = \"Bulk concentrations: t=$t_plot\", legend = :lt)\n    sol = tsol(t_plot)\n    scalarplot!(vis, grid, sol[iA, :]; color = :red, label = \"A\")\n    scalarplot!(vis, grid, sol[iB, :]; color = :green, label = \"B\", clear = false)\n    scalarplot!(vis, grid, sol[iAB2, :]; color = :blue, label = \"AB2\", clear = false)\n    reveal(vis)\nend
\n\n\n
Ctotalv = tsol[iC, 1, :] + tsol[iCA, 1, :] + tsol[iCB, 1, :] + tsol[iCAB2, 1, :]
\n
248-element Vector{Float64}:\n 1.0\n 1.0\n 0.9999999999999999\n 1.0\n 1.0\n 0.9999999999999999\n 0.9999999999999999\n ⋮\n 1.0000000000000002\n 1.0000000000000002\n 1.0000000000000002\n 1.0000000000000002\n 1.0000000000000002\n 1.0000000000000002
\n\n
@test Ctotalv ≈ ones(length(tsol))
\n
Test Passed
\n\n
let\n    vis = GridVisualizer(;\n        size = (600, 300),\n        xlabel = \"t\",\n        flimits = (0, 1), xlimits = (1.0e-3, tvend),\n        legend = :lt, title = \"Concentrations at x=0\", xscale = :log10\n    )\n    t = tsol.t\n    scalarplot!(vis, t, tsol[iA, 1, :]; color = :darkred, label = \"A\")\n    scalarplot!(vis, t, tsol[iCA, 1, :]; color = :red, label = \"CA\")\n    scalarplot!(vis, t, tsol[iB, 1, :]; color = :darkgreen, label = \"B\")\n    scalarplot!(vis, t, tsol[iCB, 1, :]; color = :green, label = \"CB\")\n    scalarplot!(vis, t, tsol[iAB2, 1, :]; color = :darkblue, label = \"AB2\")\n    scalarplot!(vis, t, tsol[iCAB2, 1, :]; color = :blue, label = \"CAB2\")\n    scalarplot!(vis, t, tsol[iC, 1, :] / C0; color = :orange, label = \"C/C0\")\n    scalarplot!(vis, t, Ctotalv / C0; color = :darkorange, label = \"Ctot/C0\")\n\n    reveal(vis)\nend
\n\n\n","category":"page"},{"location":"plutostatichtml_examples/heterogeneous-catalysis/#Appendix","page":"Coupling with Catalyst.jl","title":"Appendix","text":"","category":"section"},{"location":"plutostatichtml_examples/heterogeneous-catalysis/","page":"Coupling with Catalyst.jl","title":"Coupling with Catalyst.jl","text":"
\n
\n\n
# Some tricks helping to run the notebook during VoronoiFVM.jl CI\nbegin\n    doplots = !haskey(ENV, \"VORONOIFVM_RUNTESTS\")\n    import Pkg as _Pkg\n    haskey(ENV, \"PLUTO_PROJECT\") && _Pkg.activate(ENV[\"PLUTO_PROJECT\"])\n    using Revise\nend
\n\n
\n

Built with Julia 1.11.1 and

\n\n
\n\n","category":"page"},{"location":"plutostatichtml_examples/heterogeneous-catalysis/","page":"Coupling with Catalyst.jl","title":"Coupling with Catalyst.jl","text":"EditURL = \"https://github.com/WIAS-PDELib/VoronoiFVM.jl/blob/master/nothing\"","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\n\nusing ExtendableGrids\nusing GridVisualize\nusing LinearAlgebra\nusing AMGCLWrap\nusing Random\nusing StrideArraysCore: @gc_preserve, StrideArray, StaticInt, PtrArray\nusing LinearSolve, ExtendableSparse\nusing ExtendableSparse: ILUZeroPreconBuilder\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 in 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 in 1:nspec(data)\n for jspec in 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 in 1:nspec(data)\n f[ispec] = du[ispec]\n end\n return\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 in 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 in 1:nspec(data)\n for jspec in 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 in 1:nspec(data)\n f[ispec] = du[ispec]\n end\n return nothing\nend\n\nfunction bcondition(f, u, node, data)\n for species in 1:nspec(data)\n boundary_dirichlet!(\n f, u, node; species, region = data.diribc[1],\n value = species % 2\n )\n boundary_dirichlet!(\n f, u, node; species, region = data.diribc[2],\n value = 1 - species % 2\n )\n end\n return nothing\nend\n\nfunction main(;\n 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 )\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 in 1:nspec\n DBinary[ispec, ispec] = 0\n end\n\n DKnudsen = fill(1.0, nspec)\n\n if dim == 1\n grid = simplexgrid(X)\n diribc = [1, 2]\n elseif dim == 2\n grid = simplexgrid(X, X)\n diribc = [4, 2]\n else\n grid = simplexgrid(X, X, X)\n diribc = [4, 2]\n end\n\n function storage(f, u, node, data)\n f .= u\n return nothing\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 if verbose\n @info \"Strategy: $(strategy)\"\n end\n if !isnothing(strategy) && hasproperty(strategy, :precs)\n if isa(strategy.precs, BlockPreconBuilder)\n strategy.precs.partitioning = A -> partitioning(sys, Equationwise())\n end\n if isa(strategy.precs, ILUZeroPreconBuilder) && strategy.precs.blocksize != 1\n strategy.precs.blocksize = nspec\n end\n end\n control = SolverControl(method_linear = strategy)\n control.maxiters = 500\n if verbose\n @info control.method_linear\n end\n u = solve(sys; verbose, control, log = true)\n if verbose\n @show norm(u)\n end\n return norm(u)\nend\n\nusing Test\nfunction runtests()\n strategies = [\n UMFPACKFactorization(),\n KrylovJL_GMRES(precs = LinearSolvePreconBuilder(UMFPACKFactorization())),\n KrylovJL_GMRES(precs = BlockPreconBuilder(precs = LinearSolvePreconBuilder(UMFPACKFactorization()))),\n KrylovJL_GMRES(precs = BlockPreconBuilder(precs = AMGPreconBuilder())),\n KrylovJL_BICGSTAB(precs = BlockPreconBuilder(precs = AMGPreconBuilder())),\n KrylovJL_GMRES(precs = ILUZeroPreconBuilder()),\n KrylovJL_GMRES(precs = BlockPreconBuilder(precs = ILUZeroPreconBuilder())),\n KrylovJL_GMRES(precs = ILUZeroPreconBuilder(blocksize = 5)),\n ]\n\n val1D = 4.788926530387466\n val2D = 15.883072449873742\n val3D = 52.67819183426213\n\n\n @test main(; dim = 1, assembly = :cellwise) ≈ val1D\n @test main(; dim = 2, assembly = :cellwise) ≈ val2D\n @test main(; dim = 3, assembly = :cellwise) ≈ val3D\n @test main(; dim = 1, flux = :flux_marray, assembly = :cellwise) ≈ val1D\n @test main(; dim = 2, flux = :flux_marray, assembly = :cellwise) ≈ val2D\n @test main(; dim = 3, flux = :flux_marray, assembly = :cellwise) ≈ val3D\n @test all(map(strategy -> main(; dim = 2, flux = :flux_marray, strategy) ≈ val2D, strategies))\n return nothing\nend\n\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 in 1:(N - 1)\n xcoord = glue(xcoord, XX .+ i)\n end\n grid2 = simplexgrid(xcoord)\n for i in 1:N\n cellmask!(grid2, [i - 1], [i], i)\n end\n for i in 1:(N - 1)\n bfacemask!(grid2, [i], [i], i + 2)\n end\n\n params = zeros(2, num_cellregions(grid2))\n for i in 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 in 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 in carrierList\n params2[icc] = 2\n end\n\n for i in 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 in 1:num_cellregions(grid2)\n @assert params[cspec, i] == i\n @assert params[dspec, i] == 10 * i\n end\n\n for i in 1:num_cellregions(grid2)\n params[cspec, i] = -i\n params[dspec, i] = -10 * i\n end\n\n for i in 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, data)\n f[dspec] = u[dspec, 1] - u[dspec, 2]\n f[cspec] = u[cspec, 1] - u[cspec, 2]\n return nothing\n end\n\n d1 = 1\n q1 = 0.2\n\n function breaction(f, u, bnode, data)","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 return nothing\n end\n\n physics!(\n system, VoronoiFVM.Physics(;\n flux = flux,\n breaction = breaction\n )\n )\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!(\n vis, subgrids[i], dvws[i]; flimits = (-0.5, 1.5), clear = false,\n color = :red\n )\n scalarplot!(\n vis, subgrids[i], cvws[i]; flimits = (-0.5, 1.5), clear = false,\n color = :green\n )\n end\n reveal(vis)\n I = integrate(system, system.physics.storage, U)\n return 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\n return nothing\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 n = 10,\n Plotter = nothing,\n verbose = false,\n tend = 1,\n unknown_storage = :sparse,\n autodetect_sparsity = true\n )\n h = 1.0 / convert(Float64, n)\n X = collect(0.0:h:1.0)\n N = length(X)\n\n grid = simplexgrid(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, data)\n f[iA] = D_A * (u[iA, 1] - u[iA, 2])\n f[iB] = D_B * (u[iB, 1] - u[iB, 2])\n return nothing\n end\n\n # Storage term of species A and B\n function storage!(f, u, node, data)\n f[iA] = u[iA]\n f[iB] = u[iB]\n return nothing\n end\n\n # Source term for species a around 0.5\n function source!(f, node, data)\n x1 = node[1] - 0.5\n f[iA] = exp(-100 * x1^2)\n return nothing\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, data)\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 return nothing\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 return nothing\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 return sparsity\n end\n\n if autodetect_sparsity\n physics = VoronoiFVM.Physics(;\n breaction = breaction!,\n generic = generic_operator!,\n flux = flux!,\n storage = storage!,\n source = source!\n )\n else\n physics = VoronoiFVM.Physics(;\n breaction = breaction!,\n generic = generic_operator!,\n generic_sparsity = generic_operator_sparsity,\n flux = flux!,\n storage = storage!,\n source = source!\n )\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!(\n p[1, 1], grid, U[iA, :]; label = \"[A]\",\n title = @sprintf(\n \"max_A=%.5f max_B=%.5f u_C=%.5f\", maximum(U[iA, :]),\n maximum(U[iB, :]), u_C[end]\n ), color = :red\n )\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\n return nothing\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 = \"png\", 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, data) = y[1] = u[1, 1] - u[1, 2];
\n\n\n
function reaction(y, u, node, data)\n    eplus = exp(u[1])\n    eminus = 1 / eplus\n    y[1] = eplus - eminus\n    return nothing\nend
\n
reaction (generic function with 1 method)
\n\n
function bc(y, u, node, data)\n    boundary_dirichlet!(y, u, node; region = 1, value = 100)\n    boundary_dirichlet!(y, u, node; region = 2, value = 0.0)\n    return nothing\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(sol)\nend;
\n\n\n
scalarplot(\n    system,\n    sol;\n    resolution = (500, 200),\n    xlabel = \"x\",\n    ylable = \"y\",\n    title = \"solution\"\n)
\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(sol).

\n\n\n

The history can be plotted:

\n\n
function plothistory(h)\n    return scalarplot(\n        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\"\n    )\nend;
\n\n\n
plothistory(hist)
\n\n\n\n

History can be summarized:

\n\n
summary(hist)
\n
(seconds = 14.1, tasm = 11.7, tlinsolve = 0.721, 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_initial = 0.15, damp_growth = 1.5)\n    hist1 = history(sol1)\nend
\n
28-element NewtonSolverHistory:\n 97.81584868964667\n  9.162760369739365\n  0.9999999998511512\n  0.9999999997401952\n  0.999999999441547\n  0.9999999983981809\n  0.9999999941837444\n  ⋮\n  0.7769510871365355\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.312, tasm = 0.291, tlinsolve = 0.0212, 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, data)\n    boundary_dirichlet!(y, u, node; region = 1, value = 100 * embedparam(node))\n    boundary_dirichlet!(y, u, node; region = 2, value = 0)\n    return nothing\nend;
\n\n\n
system2 = VoronoiFVM.System(\n    X;\n    flux = flux,\n    reaction = function (y, u, node, data)\n        reaction(y, u, node, data)\n\n        y[1] = y[1] * embedparam(node)\n        return nothing\n    end,\n    bcondition = pbc,\n    species = 1,\n);
\n\n\n
begin\n    sol2 = solve(\n        system2;\n        inival = 0,\n        log = true,\n        embed = (0, 1),\n        Δp = 0.1,\n        Δp_grow = 1.2,\n        Δu_opt = 15\n    )\n    history2 = history(sol2)\nend
\n
9-element TransientSolverHistory:\n [0.0]\n [9.989343055885163, 0.98148159332474, 0.8053267053718508, 0.34118506593594755, 0.03588566608807058, 0.00030501510006535184, 2.0656145341786982e-8, 3.5058194770979925e-15]\n [11.34146393762356, 0.9997315206431725, 0.9990808258671514, 0.9966384496135858, 0.9877041414138586, 0.9368309534589466, 0.7284028335581785, 0.3000679588018384, 0.034624159753369424, 0.00039224137194873325, 5.021311921932754e-8, 1.3972756883275171e-15]\n [1.5085724152572166, 0.4205506561491683, 0.10826945171253802, 0.005760884131907183, 1.5240635820497445e-5, 1.0633964457291663e-10]\n [0.32730498752976783, 0.04842614744099976, 0.001078162227433007, 4.82292409015689e-7, 8.958093218077431e-14]\n [0.18959174175719817, 0.01947525749984668, 0.00017208751333215453, 1.2283470243608807e-8, 2.6864715379034067e-15]\n [0.22151709368816527, 0.01350594149309612, 8.295254108143132e-5, 2.8747465562928216e-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.9999999995955116, 0.9999999983507291, 0.9999999940224222, 0.9999999796890737, 0.9999999337470156, 0.9999997898900223, 0.9999993472708903, 0.9999980039123316, 0.9999939712056518  …  0.9888382056759235, 0.9682851805189645, 0.912237194563469, 0.7725313381228643, 0.49505826421414995, 0.16481352791231269, 0.014468420882723345, 0.00010072395527063066, 4.834945474226937e-9, 1.0894463251846006e-15]
\n\n
summary(history2)
\n
(seconds = 1.06, tasm = 0.906, tlinsolve = 0.147, 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.u[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.u[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/#SolverControl","page":"Nonlinear solver control","title":"SolverControl","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...)

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.

  • 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:

    • UMFPACKFactorization() for sparse matrices with Float64

    • SparspakFactorization() for sparse matrices with general number types.

    • Defaults from LinearSolve.jl for tridiagonal and banded matrices

    Users should experiment with what works best for their problem.

    For an overeview on possible alternatives, see 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 ? Translates to reuse_precs=!keepcurrent_linear for LinearSolve.

  • Δ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 embedding 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.11.1 and

\nCairoMakie 0.12.16
\nExtendableGrids 1.9.2
\nGridVisualize 1.7.0
\nLinearAlgebra 1.11.0
\nPkg 1.11.0
\nPlutoUI 0.7.60
\nRevise 3.5.18
\nTest 1.11.0
\nVoronoiFVM 1.25.1\n
\n\n","category":"page"},{"location":"plutostatichtml_examples/nonlinear-solvers/","page":"Nonlinear solver control","title":"Nonlinear solver control","text":"EditURL = \"https://github.com/WIAS-PDELib/VoronoiFVM.jl/blob/master/nothing\"","category":"page"},{"location":"physics/#Physics-and-special-functions","page":"Physics & special functions","title":"Physics & special functions","text":"","category":"section"},{"location":"physics/#Physics-callbacks","page":"Physics & special functions","title":"Physics callbacks","text":"","category":"section"},{"location":"physics/","page":"Physics & special functions","title":"Physics & special functions","text":"VoronoiFVM.AbstractPhysics\nVoronoiFVM.Physics\nVoronoiFVM.Physics(;kwargs...)\nVoronoiFVM.generic_operator_sparsity!\nBase.show(io::IO,physics::VoronoiFVM.AbstractPhysics)\nVoronoiFVM.AbstractData\nBase.show(::IO, ::MIME{Symbol(\"text/plain\")}, ::VoronoiFVM.AbstractData)\nBase.copy!(::VoronoiFVM.AbstractData{Tv}, ::VoronoiFVM.AbstractData{Tu}) where {Tv,Tu}","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,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,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,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,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,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,data)\n\nbreaction::Function: Boundary reaction term: 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,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,data) Similar to storage, but restricted to the inner or outer boundaries.\n\nboutflow::Function: Outflow boundary term 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 boundary 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. Meaningless & deprecated.\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/#VoronoiFVM.generic_operator_sparsity!","page":"Physics & special functions","title":"VoronoiFVM.generic_operator_sparsity!","text":"generic_operator_sparsity!(system, sparsematrix)\n\n\nSet generic operator sparsity, in the case where a generic operator has been defined in physics.\n\n\n\n\n\n","category":"function"},{"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/#VoronoiFVM.AbstractData","page":"Physics & special functions","title":"VoronoiFVM.AbstractData","text":"abstract type AbstractData{Tv}\n\nAbstract type for user data.\n\nIt is possible but not necessary to make user data a subtype of AbstractData and get a prettyprinting show method.\n\n\n\n\n\n","category":"type"},{"location":"physics/#Base.show-Tuple{IO, MIME{Symbol(\"text/plain\")}, VoronoiFVM.AbstractData}","page":"Physics & special functions","title":"Base.show","text":"show(\n io::IO,\n _::MIME{Symbol(\"text/plain\")},\n this::VoronoiFVM.AbstractData\n)\n\n\nPretty print AbstractData\n\n\n\n\n\n","category":"method"},{"location":"physics/#Base.copy!-Union{Tuple{Tu}, Tuple{Tv}, Tuple{VoronoiFVM.AbstractData{Tv}, VoronoiFVM.AbstractData{Tu}}} where {Tv, Tu}","page":"Physics & special functions","title":"Base.copy!","text":"copy!(to::AbstractData, from::AbstractData)\n\nCopy AbstractData.\n\n\n\n\n\n","category":"method"},{"location":"physics/#Handling-boundary-conditions","page":"Physics & special functions","title":"Handling boundary conditions","text":"","category":"section"},{"location":"physics/","page":"Physics & special functions","title":"Physics & special functions","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":"physics/","page":"Physics & special functions","title":"Physics & special functions","text":"boundary_dirichlet!(y,u,bnode::AbstractGeometryItem,ispec,ireg,val)\nboundary_dirichlet!(y,u,bnode::AbstractGeometryItem;kwargs...)\nboundary_neumann!(y,u,bnode::AbstractGeometryItem,ispec,ireg,val)\nboundary_neumann!(y,u,bnode::AbstractGeometryItem;kwargs...)\nboundary_robin!(y,u,bnode::AbstractGeometryItem,ispec,ireg,fac,val)\nboundary_robin!(y,u,bnode::AbstractGeometryItem;kwargs...)\nramp","category":"page"},{"location":"physics/#VoronoiFVM.boundary_dirichlet!-Tuple{Any, Any, AbstractGeometryItem, Any, Any, Any}","page":"Physics & special functions","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":"physics/#VoronoiFVM.boundary_dirichlet!-Tuple{Any, Any, AbstractGeometryItem}","page":"Physics & special functions","title":"VoronoiFVM.boundary_dirichlet!","text":" boundary_dirichlet!(y,u,bnode; 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":"physics/#VoronoiFVM.boundary_neumann!-Tuple{Any, Any, AbstractGeometryItem, Any, Any, Any}","page":"Physics & special functions","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":"physics/#VoronoiFVM.boundary_neumann!-Tuple{Any, Any, AbstractGeometryItem}","page":"Physics & special functions","title":"VoronoiFVM.boundary_neumann!","text":" boundary_neumann!(y,u,bnode; 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":"physics/#VoronoiFVM.boundary_robin!-Tuple{Any, Any, AbstractGeometryItem, Vararg{Any, 4}}","page":"Physics & special functions","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":"physics/#VoronoiFVM.boundary_robin!-Tuple{Any, Any, AbstractGeometryItem}","page":"Physics & special functions","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":"physics/#VoronoiFVM.ramp","page":"Physics & special functions","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":"physics/#Outflow-boundary-conditions","page":"Physics & special functions","title":"Outflow boundary conditions","text":"","category":"section"},{"location":"physics/","page":"Physics & special functions","title":"Physics & special functions","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":"physics/","page":"Physics & special functions","title":"Physics & special functions","text":"hasoutflownode\nisoutflownode\noutflownode\ncalc_divergences","category":"page"},{"location":"physics/#VoronoiFVM.hasoutflownode","page":"Physics & special functions","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":"physics/#VoronoiFVM.isoutflownode","page":"Physics & special functions","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":"physics/#VoronoiFVM.outflownode","page":"Physics & special functions","title":"VoronoiFVM.outflownode","text":"outflownode(edge)\n\nReturn outflow node of edge (1 or 2).\n\n\n\n\n\n","category":"function"},{"location":"physics/#VoronoiFVM.calc_divergences","page":"Physics & special functions","title":"VoronoiFVM.calc_divergences","text":"calc_divergences(sys, evelo, bfvelo)\n\n\nCalculate for each Voronoi cell associated with a node of sys.grid the divergence of the velocity field used to obtain evelo and bfvelo via edgevelocities and bfacevelocities by means of summing all evelos and bfvelos per Voronoi cell.\n\n\n\n\n\n","category":"function"},{"location":"physics/#Coupling-to-flow","page":"Physics & special functions","title":"Coupling to flow","text":"","category":"section"},{"location":"physics/","page":"Physics & special functions","title":"Physics & special functions","text":"edgevelocities\nbfacevelocities\nbfacenodefactors","category":"page"},{"location":"physics/#VoronoiFVM.edgevelocities","page":"Physics & special functions","title":"VoronoiFVM.edgevelocities","text":"edgevelocities(grid, velofunc; kwargs...)\n\n\nProject velocity onto grid edges. That is, we compute the path integrals of the given velofunc along the Voronoi cell edges as provided by integrate.\n\n\n\n\n\nedgevelocities(grid, vel; kwargs...)\n\n\nCompute VoronoiFVM.edgevelocities for a finite element flow field computed by ExtendableFEM.\n\n\n\n\n\n","category":"function"},{"location":"physics/#VoronoiFVM.bfacevelocities","page":"Physics & special functions","title":"VoronoiFVM.bfacevelocities","text":"bfacevelocities(grid, velofunc; kwargs...)\n\n\nSimilar to edgevelocities, but for boundary faces.\n\n\n\n\n\nbfacevelocities(grid, vel; kwargs...)\n\n\nCompute VoronoiFVM.bfacevelocities for a finite element flow field computed by ExtendableFEM.\n\n\n\n\n\n","category":"function"},{"location":"physics/#VoronoiFVM.bfacenodefactors","page":"Physics & special functions","title":"VoronoiFVM.bfacenodefactors","text":"bfacenodefactors(sys)\n\n\nNode form factors per boundary face\n\n\n\n\n\n","category":"function"},{"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\nVoronoiFVM.time\nVoronoiFVM.region\nVoronoiFVM.parameters\nVoronoiFVM.embedparam\nVoronoiFVM.project\nVoronoiFVM.edgelength\nVoronoiFVM.meas","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 (deprecated)\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: Parameters (deprecated)\n\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: Parameters (deprecated)\n\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: Parameters (deprecated)\n\nfac::Float64\n\n\n\n\n\n","category":"type"},{"location":"physics/#VoronoiFVM.time","page":"Physics & special functions","title":"VoronoiFVM.time","text":"time(edge_or_node)\n\nReturn actual simulation time stored in node or edge\n\n\n\n\n\n","category":"function"},{"location":"physics/#VoronoiFVM.region","page":"Physics & special functions","title":"VoronoiFVM.region","text":"region(edgeornode)\n\nReturn region number node or edge is belonging to\n\n\n\n\n\n","category":"function"},{"location":"physics/#VoronoiFVM.parameters","page":"Physics & special functions","title":"VoronoiFVM.parameters","text":"parameters(edge_or_node)\n\nReturn abstract vector of parameters passed via vector of unknowns. This allows differentiation with respect to these parameters.\n\n\n\n\n\n","category":"function"},{"location":"physics/#VoronoiFVM.embedparam","page":"Physics & special functions","title":"VoronoiFVM.embedparam","text":"embedparam(edge_or_node)\n\nReturn embedding parameter stored in node or edge\n\n\n\n\n\n","category":"function"},{"location":"physics/#VoronoiFVM.project","page":"Physics & special functions","title":"VoronoiFVM.project","text":"project(edge, vector)\n\nProject d-vector onto d-dimensional vector, i.e. calculate the dot product of vector with the difference of the edge end coordinates.\n\n\n\n\n\n","category":"function"},{"location":"physics/#VoronoiFVM.edgelength","page":"Physics & special functions","title":"VoronoiFVM.edgelength","text":"edgelength(edge)\n\nReturn length of edge\n\n\n\n\n\n","category":"function"},{"location":"physics/#VoronoiFVM.meas","page":"Physics & special functions","title":"VoronoiFVM.meas","text":"meas(edge::VoronoiFVM.AbstractEdge) -> Any\n\n\nCalculate the length of an edge. \n\n\n\n\n\n","category":"function"},{"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\nWhile x/expm1(x) appears to be sufficiently accurate for all x!=0, it's derivative calculated via ForwardDiff.jl is not, so we use the polynomial approximation in the interval (-bernoulli_small_threshold, bernoulli_small_threshold). \n\nAlso, see the discussion in #117.\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\nFor the general approach to the implementation, see the discussion in fbernoulli.\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\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\nusing OrdinaryDiffEqRosenbrock\nusing SciMLBase: NoInit\n\nfunction main(;\n n = 10, Plotter = nothing, verbose = false, tend = 1,\n unknown_storage = :sparse, assembly = :edgewise,\n diffeq = false\n )\n h = 1.0 / convert(Float64, n)\n X = collect(0.0:h:1.0)\n N = length(X)\n\n grid = simplexgrid(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, data)\n f[iA] = D_A * (u[iA, 1] - u[iA, 2])\n f[iB] = D_B * (u[iB, 1] - u[iB, 2])\n return nothing\n end\n\n # Storage term of species A and B\n function storage!(f, u, node, data)\n f[iA] = u[iA]\n f[iB] = u[iB]\n return nothing\n end\n\n # Source term for species a around 0.5\n function source!(f, node, data)\n x1 = node[1] - 0.5\n f[iA] = exp(-100 * x1^2)\n return nothing\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, data)\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 return nothing\n end\n\n # This is for the term \\partial_t u_C at the boundary\n function bstorage!(f, u, node, data)\n if node.region == 1\n f[iC] = u[iC]\n end\n return nothing\n end\n\n physics = VoronoiFVM.Physics(;\n breaction = breaction!,\n bstorage = bstorage!,\n flux = flux!,\n storage = storage!,\n source = source!\n )\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 if diffeq\n inival = unknowns(sys, inival = 0)\n problem = ODEProblem(sys, inival, (0, tend))\n # use fixed timesteps just for the purpose of CI\n odesol = solve(problem, Rosenbrock23(); initializealg = NoInit(), dt = tstep, adaptive = false)\n tsol = reshape(odesol, sys)\n else\n control = fixed_timesteps!(VoronoiFVM.NewtonControl(), tstep)\n tsol = solve(sys; inival, times = [0, tend], control, verbose = verbose)\n end\n\n p = GridVisualizer(; Plotter = Plotter, layout = (3, 1), fast = true)\n for it in 1:length(tsol)\n time = tsol.t[it]\n scalarplot!(\n p[1, 1], grid, tsol[iA, :, it]; clear = true,\n title = @sprintf(\"[A]: (%.3f,%.3f)\", extrema(tsol[iA, :, it])...)\n )\n scalarplot!(\n p[2, 1], grid, tsol[iB, :, it]; clear = true,\n title = @sprintf(\"[B]: (%.3f,%.3f)\", extrema(tsol[iB, :, it])...)\n )\n scalarplot!(\n p[3, 1], tsol.t[1:it], tsol[iC, 1, 1:it]; title = @sprintf(\"[C]\"),\n clear = true, show = true\n )\n end\n\n return tsol[iC, 1, end]\nend\n\nusing Test\nfunction runtests()\n testval = 0.87544440641274\n testvaldiffeq = 0.8891082547874963\n @test isapprox(main(; unknown_storage = :sparse, assembly = :edgewise), testval; rtol = 1.0e-12)\n @test isapprox(main(; unknown_storage = :dense, assembly = :edgewise), testval; rtol = 1.0e-12)\n @test isapprox(main(; unknown_storage = :sparse, assembly = :cellwise), testval; rtol = 1.0e-12)\n @test isapprox(main(; unknown_storage = :dense, assembly = :cellwise), testval; rtol = 1.0e-12)\n\n @test isapprox(main(; diffeq = true, unknown_storage = :sparse, assembly = :edgewise), testvaldiffeq; rtol = 1.0e-12)\n @test isapprox(main(; diffeq = true, unknown_storage = :dense, assembly = :edgewise), testvaldiffeq; rtol = 1.0e-12)\n @test isapprox(main(; diffeq = true, unknown_storage = :sparse, assembly = :cellwise), testvaldiffeq; rtol = 1.0e-12)\n @test isapprox(main(; diffeq = true, unknown_storage = :dense, assembly = :cellwise), testvaldiffeq; rtol = 1.0e-12)\n return nothing\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 in 1:(N - 1)\n xcoord = glue(xcoord, XX .+ i)\n end\n grid = simplexgrid(xcoord)\n for i in 1:N\n cellmask!(grid, [i - 1], [i], i)\n end\n for i in 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 return nothing\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 return nothing\n end\n\n physics!(\n sysQ, VoronoiFVM.Physics(;\n data = data,\n flux = fluxQ,\n breaction = breactionQ\n )\n )\n\n ##########################################################\n icc = 1 # for system without AbstractQuantities\n\n function flux!(f, u, edge, data) # analogous as for other system\n f[icc] = u[icc, 1] - u[icc, 2]\n return nothing\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(\n grid; flux = flux!, species = icc,\n unknown_storage = unknown_storage\n )\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!(\n 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 )\n scalarplot!(\n vis[1, 1], subgrids[i], cvws[i]; label = \"cont quantity\",\n clear = false, color = :green\n )\n end\n scalarplot!(\n vis[1, 1], grid, U[icc, :]; label = \"without quantity\", clear = false,\n linestyle = :dot, color = :blue\n )\n\n scalarplot!(\n vis[2, 1], biasval, Idspec; clear = false,\n title = @sprintf(\"IV with rate=%.2f\", data.rate),\n label = \"discont quantity\", color = :red\n )\n scalarplot!(\n vis[2, 1], biasval, Icspec; clear = false, title = \"Current\",\n label = \"cont quantity\", color = :green\n )\n scalarplot!(\n vis[2, 1], biasval, Iicc; clear = false, label = \"discont quantity\",\n linestyle = :dot, color = :blue, show = true\n )\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\n return nothing\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 for systems:","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/#Grid-verification","page":"Postprocessing","title":"Grid verification","text":"","category":"section"},{"location":"post/","page":"Postprocessing","title":"Postprocessing","text":"VoronoiFVM.nondelaunay","category":"page"},{"location":"post/#VoronoiFVM.nondelaunay","page":"Postprocessing","title":"VoronoiFVM.nondelaunay","text":"nondelaunay(grid;tol=1.0e-14, verbose=true)\n\nReturn non-Delaunay edges. Returns a vector of tuples: Each tuple consists of (node1, node2, edge factor, region)\n\nIf the vector has length 0, the grid is boundary conforming Delaunay with respect to each cell region. This means that up to tol, all edge form factors are nonnegative.\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 weighted 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":"VoronoiFVM.integrate(system::VoronoiFVM.AbstractSystem, tf::Vector, U::AbstractMatrix; kwargs...)\nVoronoiFVM.integrate(system::VoronoiFVM.AbstractSystem{Tv, Tc, Ti, Tm}, F::Tf, U::AbstractMatrix{Tu}; boundary = false, data = system.physics.data) where {Tu, Tv, Tc, Ti, Tm, Tf}\nVoronoiFVM.integrate(system::VoronoiFVM.AbstractSystem, U::AbstractMatrix; kwargs...)\nVoronoiFVM.integrate(system::VoronoiFVM.AbstractSystem, F, U::AbstractMatrix; boundary, data)\nVoronoiFVM.edgeintegrate","category":"page"},{"location":"post/#VoronoiFVM.integrate-Tuple{VoronoiFVM.AbstractSystem, Vector, 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 matrix.\n\n\n\n\n\nintegrate(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{Tf}, Tuple{Tm}, Tuple{Ti}, Tuple{Tc}, Tuple{Tv}, Tuple{Tu}, Tuple{VoronoiFVM.AbstractSystem{Tv, Tc, Ti, Tm}, Tf, AbstractMatrix{Tu}}} where {Tu, Tv, Tc, Ti, Tm, Tf}","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 matrix.\n\n\n\n\n\n","category":"method"},{"location":"post/#VoronoiFVM.integrate-Tuple{VoronoiFVM.AbstractSystem, AbstractMatrix}","page":"Postprocessing","title":"VoronoiFVM.integrate","text":"integrate(system,U; boundary=false)\n\nIntegrate solution vector region-wise over domain or boundary. The result is an nspec x nregion matrix.\n\n\n\n\n\n","category":"method"},{"location":"post/#VoronoiFVM.integrate-Tuple{VoronoiFVM.AbstractSystem, Any, 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 matrix.\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 matrix.\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, F,U; data)\n\nReconstruction of an edge function 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. Gallouët, R. Herbin, IMA Journal of Numerical Analysis (2006) 26, 326−353, Lemma 2.4 (also: hal.science/hal-00004840 ).\n\nThe return value is a dim x nspec x nnodes vector.\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\nnodeflux(system, U; data)\n\nReconstruction of the edge flux as vector function on the nodes of the triangulation. See nodeflux(system,F,U;data).\n\nThe 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\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{Tu, Tv}\n\nData structure containing DenseSystem used to calculate test functions for boundary flux calculations.\n\nType parameters:\n\nTu: value type of test functions\nTv: Default value type of system\nsystem::VoronoiFVM.AbstractSystem{Tv} where Tv: Original system\n\nstate::VoronoiFVM.SystemState{Tu, Tp, TMatrix, TSolArray} where {Tu, Tp, TMatrix<:AbstractMatrix{Tu}, TSolArray<:AbstractMatrix{Tu}}: Test function system state\n\ncontrol::SolverControl: Solver control\n\n\n\n\n\n","category":"type"},{"location":"post/#VoronoiFVM.TestFunctionFactory-Union{Tuple{VoronoiFVM.AbstractSystem{Tv}}, Tuple{Tv}} where Tv","page":"Postprocessing","title":"VoronoiFVM.TestFunctionFactory","text":"TestFunctionFactory(\n system::VoronoiFVM.AbstractSystem{Tv};\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, data)\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; data)\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; data)\n\n\nCalculate transient part of test function integral.\n\n\n\n\n\n","category":"method"},{"location":"post/#VoronoiFVM.testfunction-Union{Tuple{Tv}, Tuple{TestFunctionFactory{Tv}, Any, Any}} where Tv","page":"Postprocessing","title":"VoronoiFVM.testfunction","text":"testfunction(\n factory::TestFunctionFactory{Tv},\n bc0,\n bc1\n) -> 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.freqdomain_impedance-Tuple{VoronoiFVM.ImpedanceSystem, Vararg{Any, 7}}","page":"Postprocessing","title":"VoronoiFVM.freqdomain_impedance","text":"freqdomain_impedance(\n impedance_system,\n ω,\n U0,\n excited_spec,\n excited_bc,\n excited_bcval,\n dmeas_stdy,\n dmeas_tran\n)\n\n\nCalculate reciprocal value of impedance.\n\nexcitedspec,excitedbc,excited_bcval are ignored.\n\nwarning: Warning\n\n\nThis is deprecated: use impedance.\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 = simplexgrid(collect(0:h:1))\n\n eps::Vector{Float64} = [1, 1.0e-1]\n\n physics = VoronoiFVM.Physics(\n ; reaction = function (f, u, node, data)\n f[1] = 10 * (u[1] - u[2])\n f[2] = 10 * (u[2] - u[1])\n return nothing\n end, flux = function (f, u, edge, data)\n f[1] = eps[1] * (u[1, 1] - u[1, 2])\n f[2] = eps[2] * (u[2, 1] - u[2, 2])\n return nothing\n end, storage = function (f, u, node, data)\n f[1] = u[1]\n f[2] = u[2]\n return nothing\n end\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 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 @test main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval\n @test main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval\n @test main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval\n return nothing\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, data)\n for ispec in 1:nspec\n f[ispec] = u[ispec, 1] - u[ispec, 2]\n end\n return nothing\n end\n physics = VoronoiFVM.Physics(; flux = flux)\n sys = VoronoiFVM.System(grid, physics; unknown_storage = unknown_storage, assembly = assembly)\n for ispec in 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 return 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\n return nothing\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 return scalarplot!(vis[2, 1], grid, exact; title = \"exact\", show = true)\nend\n\nfunction flux(f, u, edge, data)\n f[1] = u[1, 1] - u[1, 2]\n return nothing\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 = simplexgrid(R)\n circular_symmetric!(grid)\n source(f, node, data) = 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 return 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 )\n h = 0.1 * 2.0^(-nref)\n R = collect(0:h:r2)\n Z = collect(z1:h:z2)\n grid = simplexgrid(R, Z)\n circular_symmetric!(grid)\n source(f, node, data) = 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 return 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 )\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, data) = 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 return 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 = simplexgrid(R)\n circular_symmetric!(grid)\n source(f, node, data) = 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 return 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 )\n h = 0.1 * 2.0^(-nref)\n R = collect(r1:h:r2)\n Z = collect(z1:h:z2)\n grid = simplexgrid(R, Z)\n circular_symmetric!(grid)\n source(f, node, data) = 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 return 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 = simplexgrid(R)\n spherical_symmetric!(grid)\n source(f, node, data) = 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 return 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 )\n h = 0.1 * 2.0^(-nref)\n R = collect(r1:h:r2)\n grid = simplexgrid(R)\n spherical_symmetric!(grid)\n source(f, node, data) = 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 return 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)\n return nothing\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 environment 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 .\nDuring CI tests, they are run as scripts. For this purpose they are wrapped into temporary modules, and @test macros can be 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]\n return nothing\nend\n\nfunction outflow!(f, u, node, data)\n if node.region == 2\n f[1] = data.v[1] * u[1]\n end\n return nothing\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 = simplexgrid(0:h:1)\n\n data = (v = [v], D = D)\n\n sys = VoronoiFVM.System(\n grid,\n VoronoiFVM.Physics(;\n flux = exponential_flux!, data = data,\n breaction = outflow!\n )\n )\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 in 1:length(tsol.t)\n scalarplot!(\n vis[1, 1], grid, tsol[1, :, i]; flimits = (0, 1),\n title = \"t=$(tsol.t[i])\", show = true\n )\n sleep(0.01)\n end\n return tsol\nend\n\nusing Test\nfunction runtests()\n tsol = main()\n @test maximum(tsol) <= 1.0 && maximum(tsol.u[end]) < 1.0e-20\n return nothing\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: geomspace, simplexgrid\nusing GridVisualize\n\nfunction main(;\n 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\n )","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 = 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 = simplexgrid(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 return nothing\n end\n\n storage = function (f, u, node, data)\n f[1] = data.C * u[1]\n return nothing\n end\n\n reaction = function (f, u, node, data)\n f[1] = data.R * u[1]\n return nothing\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(;\n data = data,\n flux = flux,\n storage = storage,\n reaction = reaction\n )","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 return 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 return 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!(\n p, real(allIxL), imag(allIxL); label = \"exact\", color = :red,\n linestyle = :dot\n )\n scalarplot!(\n p, real(allIL), imag(allIL); label = \"calc\", show = true, clear = false,\n color = :blue, linestyle = :solid\n )\n\n return 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\n return nothing\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 = \"png\", 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, data)\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\n    return nothing\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 in 1:2\n        boundary_dirichlet!(sys, ispec, 1, g_1)\n        boundary_dirichlet!(sys, ispec, 2, g_2)\n    end\n    return 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    return 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!(\n        vis,\n        subgrid1,\n        U1;\n        clear = false,\n        show = false,\n        color = :green,\n        label = \"u1\"\n    )\n    return scalarplot!(\n        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)\n    )\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, data)\n    return nothing\nend
\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, data)\n    if node.region == 3\n        react = k1 * u[1] - k2 * u[2]\n        f[1] = react\n        f[2] = -react\n    end\n    return nothing\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,\n  Int32}}(  \n  grid = ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=1, nnodes=75, ncells=74,\n  nbfaces=3),  \n  physics = Physics(flux=flux!, storage=default_storage, breaction=mal_reaction, ),  \n  num_species = 2)
\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, data)\n    if node.region == 3\n        react = 1.0e10 * (u[1] - u[2])\n        f[1] = react\n        f[2] = -react\n    end\n    return nothing\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, data)\n    if node.region == 3\n        react = 1.0e10 * (u[1] - u[2] - jump)\n        f[1] = react\n        f[2] = -react\n    end\n    return nothing\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, data)\n    if node.region == 3\n        react = k_r * (u[1] * u[2])\n        f[1] = react\n        f[2] = react\n    end\n    return nothing\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, data)\n    if node.region == 3\n        react = (u[1] - u[2]) / d\n        f[1] = react\n        f[2] = -react\n    end\n    return nothing\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 in 1:(N - 1)\n        xcoord = glue(xcoord, XX .+ i)\n    end\n    grid2 = simplexgrid(xcoord)\n    for i in 1:N\n        cellmask!(grid2, [i - 1], [i], i)\n    end\n    for i in 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}}(\n  grid = ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=1, nnodes=61, ncells=60,\n  nbfaces=7),  \n  physics = Physics(storage=default_storage, ),  \n  num_species = 0)
\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 in 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, data)\n    f[dspec] = u[dspec, 1] - u[dspec, 2]\n    return 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, data)\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\n    return nothing\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}(dim=1, nnodes=11, ncells=10, nbfaces=2)\n ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=1, nnodes=11, ncells=10, nbfaces=2)\n ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=1, nnodes=11, ncells=10, nbfaces=2)\n ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=1, nnodes=11, ncells=10, nbfaces=2)\n ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=1, nnodes=11, ncells=10, nbfaces=2)\n ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=1, nnodes=11, ncells=10, nbfaces=2)
\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!(\n        vis,\n        allsubgrids,\n        grid2,\n        dvws;\n        flimits = (-0.5, 1.5),\n        clear = false,\n        color = :red,\n        label = \"discontinuous species\"\n    )\n    scalarplot!(\n        vis,\n        allsubgrids,\n        grid2,\n        cvws;\n        flimits = (-0.5, 1.5),\n        clear = false,\n        color = :green,\n        label = \"continuous species\"\n    )\n    return 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.11.1 and

\nCairoMakie 0.12.16
\nExtendableGrids 1.9.2
\nGridVisualize 1.7.0
\nHypertextLiteral 0.9.5
\nLinearAlgebra 1.11.0
\nLinearSolve 2.34.0
\nPkg 1.11.0
\nPlutoUI 0.7.60
\nRevise 3.5.18
\nTest 1.11.0
\nVoronoiFVM 1.25.1\n
\n\n","category":"page"},{"location":"plutostatichtml_examples/interfaces1d/","page":"Internal interfaces (1D)","title":"Internal interfaces (1D)","text":"EditURL = \"https://github.com/WIAS-PDELib/VoronoiFVM.jl/blob/master/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\n    using Revise\n    using Printf\n    using VoronoiFVM\n    using SciMLBase: NoInit\n    using OrdinaryDiffEqBDF\n    using OrdinaryDiffEqRosenbrock\n    using OrdinaryDiffEqSDIRK\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}\n      dim =       1\n   nnodes =      51\n   ncells =      50\n  nbfaces =       2
\n\n\n

Direct implementation with VoronoiFVM

\n\n
function flux!(f, u, edge, data)\n    f[1] = u[1, 1] - u[1, 2]\n    return nothing\nend
\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, data)\n    f[1] = (ε + u[1])^(1.0 / m)\n    return nothing\nend
\n
storage! (generic function with 1 method)
\n\n
begin\n    physics = VoronoiFVM.Physics(\n        flux = flux!,\n        storage = storage!\n    )\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(tsol.history)\nend
\n
(seconds = 2.92, tasm = 2.48, tlinsolve = 0.42, steps = 731, iters = 2920, maxabsnorm = 1.73e-12, maxrelnorm = 1.75e-11, maxroundoff = 9.06e-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, data)\n    y[1] = u[2]\n    return nothing\nend
\n
dae_storage! (generic function with 1 method)
\n\n
function dae_reaction!(y, u, node, data)\n    y[2] = u[2]^m - u[1]\n    return nothing\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_tsol.history)\nend
\n
(seconds = 2.44, tasm = 2.02, tlinsolve = 0.407, 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(\n        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    return reveal(vis)\nend
\n
plotsolutions (generic function with 1 method)
\n
\n

Built with Julia 1.11.1 and

\n\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/WIAS-PDELib/VoronoiFVM.jl/blob/master/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 n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse, assembly = :edgewise,\n tend = 100\n )\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 = simplexgrid(X, Y)\n\n eps = 1.0e-2\n physics = VoronoiFVM.Physics(;\n breaction = function (f, u, node, data)\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 return nothing\n end, flux = function (f, u, edge, data)\n f[1] = eps * (u[1, 1] - u[1, 2])\n f[2] = eps * (u[2, 1] - u[2, 2])\n return nothing\n end, storage = function (f, u, node, data)\n f[1] = u[1]\n f[2] = u[2]\n return nothing\n end\n )\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\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!(\n p[1, 1], grid, U[1, :];\n title = @sprintf(\"U1: %.3g U1+U2:%8.3g\", I[1, 1], Uall),\n flimits = (0, 1)\n )\n scalarplot!(\n p[2, 1], grid, U[2, :]; title = @sprintf(\"U2: %.3g\", I[2, 1]),\n flimits = (0, 1)\n )\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\n return nothing\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 = simplexgrid(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 in 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 return nothing\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(\n grid, solution[1, :]; title = \"Nonlinear Poisson\", Plotter = Plotter,\n resolution = (300, 300)\n )\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\n return nothing\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

\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 = \"png\", 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 = [\n            point!(builder, center[1] + radius * sin(t), center[2] + radius * cos(t))\n                for t in range(0, 2π; length = n)\n        ]\n        for i in 1:(n - 1)\n            facet!(builder, points[i], points[i + 1])\n        end\n        facet!(builder, points[end], points[1])\n        return 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 = [\n        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\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 in 1:length(radii)\n        facetregion!(builder, i + 1)\n        circlehole!(builder, holes[:, i], radii[i])\n    end\n\n    return simplexgrid(builder)\nend
\n
swiss_cheese_2d (generic function with 1 method)
\n\n
grid = swiss_cheese_2d()
\n
ExtendableGrids.ExtendableGrid{Float64, Int32}\n      dim =       2\n   nnodes =    1434\n   ncells =    2443\n  nbfaces =     459
\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)\n    return nothing\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}}(\n  grid = ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=2, nnodes=1434, ncells=2443,\n  nbfaces=459),  \n  physics = Physics(data=Main.var\"workspace#3\".Params, flux=flux,\n  storage=default_storage, breaction=bc, ),  \n  num_species = 1)
\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, 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, data) = y[1] = src(node[1])
\n
source1d (generic function with 1 method)
\n\n
flux1d(y, u, edge, data) = y[1] = u[1, 1]^2 - u[1, 2]^2
\n
flux1d (generic function with 1 method)
\n\n
function bc1d(y, u, bnode, data)\n    boundary_dirichlet!(y, u, bnode; region = 1, value = 0.01)\n    boundary_dirichlet!(y, u, bnode; region = 2, value = 0.01)\n    return nothing\nend
\n
bc1d (generic function with 1 method)
\n\n
grid1d = simplexgrid(-1:0.01:1)
\n
ExtendableGrids.ExtendableGrid{Float64, Int32}\n      dim =       1\n   nnodes =     201\n   ncells =     200\n  nbfaces =       2
\n\n
sys1d = VoronoiFVM.System(\n    grid1d;\n    flux = flux1d,\n    bcondition = bc1d,\n    source = source1d,\n    species = [1],\n)
\n
VoronoiFVM.System{Float64, Float64, Int32, Int64, Matrix{Int32}}(\n  grid = ExtendableGrids.ExtendableGrid{Float64, Int32}(dim=1, nnodes=201, ncells=200,\n  nbfaces=2),  \n  physics = Physics(flux=flux1d, storage=default_storage, source=source1d,\n  breaction=bc1d, ),  \n  num_species = 1)
\n\n
sol1d = solve(sys1d; inival = 0.1)
\n
1×201 VoronoiFVM.DenseSolutionArray{Float64, 2}:\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.11.1 and

\nCairoMakie 0.12.16
\nExtendableGrids 1.9.2
\nGridVisualize 1.7.0
\nPkg 1.11.0
\nPlutoUI 0.7.60
\nRevise 3.5.18
\nSimplexGridFactory 2.2.1
\nTest 1.11.0
\nTriangulate 2.3.4
\nVoronoiFVM 1.25.1\n
\n\n","category":"page"},{"location":"plutostatichtml_examples/flux-reconstruction/","page":"Obtaining vector fields","title":"Obtaining vector fields","text":"EditURL = \"https://github.com/WIAS-PDELib/VoronoiFVM.jl/blob/master/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":"VoronoiFVM\n\nVoronoiFVM.jl\n\n(Image: Build status) (Image: ) (Image: ) (Image: DOI) (Image: Zulip Chat) (Image: code style: runic)\n\nSolver 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.\n\nJuliaCon 2024 Lightning Talk: abstract, video\n\nRecent changes\n\nPlease look up the list of recent changes\n\nAccompanying packages\n\nExtendableSparse.jl: convenient and efficient sparse matrix assembly\nExtendableGrids.jl: unstructured grid management library\nSimplexGridFactory.jl: unified high level mesh generator interface\nTriangulate.jl: Julia wrapper for the Triangle triangle mesh generator by J. Shewchuk\nTetGen.jl: Julia wrapper for the TetGen tetrahedral mesh generator by H. Si.\nGridVisualize.jl: grid and function visualization related to ExtendableGrids.jl\nPlutoVista.jl: backend for GridVisualize.jl for use in Pluto notebooks.\n\nVoronoiFVM.jl and most of these packages are part of the meta package PDELib.jl.\n\nSome alternatives\n\nExtendableFEM.jl: finite element library implementing gradient robust FEM from the same package base by Ch. Merdon\nSkeelBerzins.jl: a Julian variation on Matlab's pdepe API\nTrixi.jl: numerical simulation framework for hyperbolic conservation laws\nGridAP.jl Grid-based approximation of partial differential equations in Julia\nFerrite.jl Finite element toolbox for Julia\nFinEtools.jl Finite element tools for Julia\nFiniteVolumeMethod.jl Finite volumes with Donald boxes\n\nSome projects and packages using VoronoiFVM.jl\n\nChargeTransport.jl: Drift diffusion simulator for semiconductor devices\nLiquidElectrolytes.jl: Generalized Nernst-Planck-Poisson model for liquid electrolytes\nMultiComponentReactiveMixtureProject: Model for heat and multi-component, reactive gas phase transport in porous media.\nRfbScFVM: Performance prediction of flow battery cells\nMosLab.jl: From semiconductor to transistor level modeling in Julia\n\nCitation\n\nIf you use this package in your work, please cite it according to CITATION.cff.\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, data)\n y[1] = u[1]\n return nothing\n end\n\n function flux!(y, u, edge, data)\n y[1] = u[1, 1] - u[1, 2]\n return nothing\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, data)\n y[1] = -1\n return nothing\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, data)\n h = meas(edge)\n y[1] = -1 * h^2 / (2 * dim)\n return nothing\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, data)\n ϕK = ϕ[edge.node[1]]\n ϕL = ϕ[edge.node[2]]\n y[1] = -(ϕK - ϕL) * (ϕK - ϕL) / 2\n return nothing\n end\n\n if case == :compare_max\n function bcondition!(y, u, node, data)\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 return nothing\n end\n\n sys_noderea = VoronoiFVM.System(\n grid; bcondition = bcondition!, flux = flux!,\n reaction = reaction!, storage = storage!,\n species = [1], is_linear = true, assembly\n )\n sys_edgerea = VoronoiFVM.System(\n grid; bcondition = bcondition!, flux = flux!,\n edgereaction = edgereaction!, storage = storage!,\n species = [1], is_linear = true, assembly\n )\n sys_edgerea2 = VoronoiFVM.System(\n grid; bcondition = bcondition!, flux = flux!,\n edgereaction = edgereaction2!, storage = storage!,\n species = [1], is_linear = true, assembly\n )\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!(\n vis[1, 1], grid, sol_noderea[1, :]; title = \"node reaction\",\n colormap = :hot\n )\n scalarplot!(vis[2, 1], grid, sol_edgerea[1, :]; title = \"edgerea1\", colormap = :hot)\n scalarplot!(\n vis[1, 2], grid, sol_edgerea2[1, :]; title = \"edgerea2\",\n colormap = :hot\n )\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, data)\n boundary_dirichlet!(y, u, node; species = 1, region = i1, value = 0)\n return nothing\n end\n\n sys2_noderea = VoronoiFVM.System(\n grid; bcondition = bcondition2!, flux = flux!,\n reaction = reaction!, storage = storage!,\n species = [1], is_linear = true\n )\n sys2_edgerea = VoronoiFVM.System(\n grid; bcondition = bcondition2!, flux = flux!,\n edgereaction = edgereaction!, storage = storage!,\n species = [1], is_linear = true\n )\n sys2_edgerea2 = VoronoiFVM.System(\n grid; bcondition = bcondition2!, flux = flux!,\n edgereaction = edgereaction2!, storage = storage!,\n species = [1], is_linear = true\n )\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!(\n vis[1, 1], grid, sol2_noderea[1, :]; title = \"node reaction\",\n colormap = :hot\n )\n scalarplot!(\n vis[2, 1], grid, sol2_edgerea[1, :]; title = \"edgerea1\",\n colormap = :hot\n )\n scalarplot!(\n vis[1, 2], grid, sol2_edgerea2[1, :]; title = \"edgerea2\",\n colormap = :hot\n )\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 in 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 in 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\n return nothing\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 n = 5, Plotter = nothing, tend = 20.0, unknown_storage = :sparse,\n reactionN = 5.0e0, reactionP = 5.0e0, assembly = :edgewise\n )\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, data)\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 return nothing\n end\n\n function reaction!(f, u, node, data)\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 return nothing\n end\n\n function flux!(f, u, node, data)\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 return nothing\n end\n\n function breaction!(f, u, bnode, data)\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 return nothing\n end\n\n function bstorage!(f, u, bnode, data)\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 return nothing\n end\n\n physics!(\n sys,\n VoronoiFVM.Physics(;\n flux = flux!,\n storage = storage!,\n reaction = reaction!,\n breaction = breaction!,\n bstorage = bstorage!\n )\n )\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 in 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 in 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\n return nothing\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":"module_examples/Example240_FiniteElementVelocities/#240:-2D-Convection-in-quadratic-stagnation-flow-velocity-field","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"","category":"section"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"(source code)","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"Solve the equation","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"-nabla cdot ( D nabla u - mathbfv u) = 0","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"in Omega=(0L)times (0H) with a homogeneous Neumann boundary condition at x=0, an outflow boundary condition at x=L, a Dirichlet inflow condition at y=H, and a homogeneous Dirichlet boundary condition on part of y=0.","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"The analytical expression for the (quadratic variant of the) velocity field is mathbfv(xy)=(x^2-2xy) in cartesian coordinates and (for the linear variant) mathbfv(rz)=(r-2z) in cylindrical coordinates, i.e. where the system is solved on Omega to represent a solution on the solid of revolution arising from rotating Omega around x=0.","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"We compute the solution u in both coordinate systems where mathbfv is given as an analytical expression and as a finite element interpolation onto the grid of Omega.","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"module Example240_FiniteElementVelocities\nusing Printf\nusing ExtendableFEMBase\nusing ExtendableFEM\nusing VoronoiFVM\nusing ExtendableGrids\nusing GridVisualize\nusing LinearAlgebra\n\nfunction stagnation_flow_cartesian(x, y)\n return (x^2, -2x * y)\nend","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"In the cylindrical case: since the reconstruction space mathttHDIVBDM2 is only quadratic, but we have to reconstruct r mathbfv(rz) for a (reconstructed) divergence-free solution, we can only resolve at most the linear case exactly.","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"function stagnation_flow_cylindrical(r, z)\n return (r, -2 * z)\nend\n\nfunction inflow_cylindrical(u, qpinfo)\n x = qpinfo.x\n u .= stagnation_flow_cylindrical(x[1], x[2])\n return nothing\nend\n\nfunction inflow_cartesian(u, qpinfo)\n x = qpinfo.x\n u .= stagnation_flow_cartesian(x[1], x[2])\n return nothing\nend\n\nfunction flux!(f, u, edge, data)\n vd = data.evelo[edge.index] / data.D\n bp = fbernoulli(vd)\n bm = fbernoulli(-vd)\n f[1] = data.D * (bp * u[1] - bm * u[2])\n return nothing\nend\n\nfunction bconditions!(f, u, node, data)\n # catalytic Dirichlet condition at y=0\n if node.region == 5\n boundary_dirichlet!(f, u, node, 1, node.region, 0.0)\n end\n\n # outflow condition at x = L\n if node.region == 2\n f[1] = data.bfvelo[node.ibnode, node.ibface] * u[1]\n end\n\n # inflow condition at y = H\n if node.region == 3\n boundary_dirichlet!(f, u, node, 1, node.region, data.cin)\n end\n return nothing\nend\n\nmutable struct Data\n D::Float64\n cin::Float64\n evelo::Vector{Float64}\n bfvelo::Matrix{Float64}\n\n Data() = new()\nend","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"Calculate the analytical or FEM solution to the stagnation point flow field mathbfv and use this as input to solve for the species concentration u of the corresponding convection-diffusion system.","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"The passed flags regulate the following behavior:","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"cylindrical_grid: if true, calculates both the velocity field mathbfv(rz) and the species concentration u(rz) in cylindrical coordinates, assuming rotationally symmetric solutions for both.\nusefem: if true, calculates the velocity field mathbfv using the finite element method provided by ExtendableFEM.\nreconst: if true, interpolates the FEM-calculated velocity field onto a \"reconstruction\" finite element space that provides an exactly divergence-free solution. In a cylindrical grid, this returns not mathbfv(rz), but r mathbfv(rz) as the velocity.\nuse_different_grids: if true, calculates the FEM solution of the velocity field on a uniform flowgrid and the species concentration on an adaptively refined chemgrid while still interpolating the calculated velocity correctly onto the chemgrid.","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"function main(;\n cylindrical_grid = false, usefem = true, reconst = cylindrical_grid, use_different_grids = false, nref = 1,\n Plotter = nothing, μ = 1.0e-2, D = 0.01, cin = 1.0, assembly = :edgewise,\n interpolation_eps = 1.0e-9\n )\n H = 1.0\n L = 5.0\n\n if cylindrical_grid\n coord_system = Cylindrical2D\n else\n coord_system = Cartesian2D\n end\n\n flowgrid = simplexgrid(\n range(0, L; length = 20 * 2^nref),\n range(0, H; length = 5 * 2^nref)\n )\n\n if use_different_grids\n h_fine = 1.0e-1\n X_bottom = geomspace(0.0, L / 2, 5.0e-1, h_fine)\n X_cat = range(L / 2, L; step = h_fine)\n chemgrid = simplexgrid(\n [X_bottom; X_cat[2:end]],\n geomspace(0.0, H, 1.0e-3, 1.0e-1)\n )\n bfacemask!(chemgrid, [L / 2, 0.0], [3 * L / 4, 0.0], 5)\n else\n chemgrid = deepcopy(flowgrid)\n bfacemask!(chemgrid, [L / 2, 0.0], [3 * L / 4, 0.0], 5)\n end\n\n if usefem\n velocity = compute_velocity(\n flowgrid, cylindrical_grid, reconst, μ; interpolation_eps\n )\n DivIntegrator = L2NormIntegrator([div(1)]; quadorder = 2 * 2, resultdim = 1)\n div_v = sqrt(sum(evaluate(DivIntegrator, [velocity])))\n @info \"||div(R(v))||_2 = $(div_v)\"\n else\n if cylindrical_grid\n velocity = stagnation_flow_cylindrical\n else\n velocity = stagnation_flow_cartesian\n end\n end\n\n if cylindrical_grid\n analytical_velocity = stagnation_flow_cylindrical\n else\n analytical_velocity = stagnation_flow_cartesian\n end\n\n # only the chemgrid needs its CoordinateSystem adjusted\n # since the velocity calculation works by adjusting\n # the kernels for the Stokes operator directly while\n # the finite volume machinery relies upon the CoordinateSystem\n # for selecting the correct geometrical factors for the\n # Voronoi cell contributions\n if cylindrical_grid\n chemgrid[CoordinateSystem] = Cylindrical2D\n end\n\n data = Data()\n data.D = D\n data.cin = cin\n\n evelo = edgevelocities(chemgrid, velocity; reconst)\n bfvelo = bfacevelocities(chemgrid, velocity; reconst)\n\n data.evelo = evelo\n data.bfvelo = bfvelo\n\n physics = VoronoiFVM.Physics(; flux = flux!, breaction = bconditions!, data)\n sys = VoronoiFVM.System(chemgrid, physics; assembly)\n enable_species!(sys, 1, [1])\n\n sol = solve(sys; inival = 0.0)\n\n fvm_divs = VoronoiFVM.calc_divergences(sys, evelo, bfvelo)\n @info \"||div(v)||_∞ = $(norm(fvm_divs, Inf))\"\n\n vis = GridVisualizer(; Plotter = Plotter)\n\n scalarplot!(\n vis[1, 1], chemgrid, sol[1, :]; flimits = (0, cin + 1.0e-5),\n show = true\n )\n\n minmax = extrema(sol)\n @info \"Minimal/maximal values of concentration: $(minmax)\"\n\n return sol, evelo, bfvelo, minmax\nend\n\nusing Test\nfunction runtests()\n cin = 1.0\n for cylindrical_grid in [false, true]\n sol_analytical, evelo_analytical, bfvelo_analytical, minmax_analytical = main(;\n cylindrical_grid, cin, usefem = false\n )\n sol_fem, evelo_fem, bfvelo_fem, minmax_fem = main(;\n cylindrical_grid, cin, usefem = true\n )\n @test norm(evelo_analytical .- evelo_fem, Inf) ≤ 1.0e-11\n @test norm(bfvelo_analytical .- bfvelo_fem, Inf) ≤ 1.0e-9\n @test norm(sol_analytical .- sol_fem, Inf) ≤ 1.0e-10\n @test norm(minmax_analytical .- [0.0, cin], Inf) ≤ 1.0e-15\n @test norm(minmax_fem .- [0.0, cin], Inf) ≤ 1.0e-11\n end\n return nothing\nend\n\nfunction compute_velocity(\n flowgrid, cylindrical_grid, reconst, μ = 1.0e-2; interpolation_eps = 1.0e-10\n )\n # define finite element spaces\n FE_v, FE_p = H1P2B{2, 2}, L2P1{1}\n reconst_FEType = HDIVBDM2{2}\n FES = [FESpace{FE_v}(flowgrid), FESpace{FE_p}(flowgrid; broken = true)]\n\n # describe problem\n Problem = ProblemDescription(\"incompressible Stokes problem\")\n v = Unknown(\"v\"; name = \"velocity\")\n p = Unknown(\"p\"; name = \"pressure\")\n assign_unknown!(Problem, v)\n assign_unknown!(Problem, p)\n\n # assign stokes operator\n assign_operator!(\n Problem,\n BilinearOperator(\n kernel_stokes!, cylindrical_grid ? [id(v), grad(v), id(p)] : [grad(v), id(p)];\n bonus_quadorder = 2, store = false,\n params = [μ, cylindrical_grid]\n )\n )\n\n # assign Dirichlet boundary conditions on all boundary regions to\n # enforce match with analytical solution\n if cylindrical_grid\n assign_operator!(\n Problem, InterpolateBoundaryData(v, inflow_cylindrical; regions = [1, 2, 3, 4])\n )\n else\n assign_operator!(\n Problem, InterpolateBoundaryData(v, inflow_cartesian; regions = [1, 2, 3, 4])\n )\n end\n\n velocity_solution = solve(Problem, FES)\n\n # ensure divergence free solution by projecting onto reconstruction spaces\n FES_reconst = FESpace{reconst_FEType}(flowgrid)\n R = FEVector(FES_reconst)\n if reconst\n if cylindrical_grid\n lazy_interpolate!(\n R[1], velocity_solution, [id(v)]; postprocess = multiply_r,\n bonus_quadorder = 2, eps = interpolation_eps\n )\n else\n lazy_interpolate!(\n R[1], velocity_solution, [id(v)];\n bonus_quadorder = 2, eps = interpolation_eps\n )\n end\n else\n return velocity_solution[1]\n end\n\n return R[1]\nend\n\nfunction kernel_stokes!(result, u_ops, qpinfo)\n μ = qpinfo.params[1]\n cylindrical_grid = qpinfo.params[2]\n if cylindrical_grid > 0\n r = qpinfo.x[1]\n u, ∇u, p = view(u_ops, 1:2), view(u_ops, 3:6), view(u_ops, 7)\n result[1] = μ / r * u[1] - p[1]\n result[2] = 0\n result[3] = μ * r * ∇u[1] - r * p[1]\n result[4] = μ * r * ∇u[2]\n result[5] = μ * r * ∇u[3]\n result[6] = μ * r * ∇u[4] - r * p[1]\n result[7] = -(r * (∇u[1] + ∇u[4]) + u[1])\n else\n ∇u, p = view(u_ops, 1:4), view(u_ops, 5)\n result[1] = μ * ∇u[1] - p[1]\n result[2] = μ * ∇u[2]\n result[3] = μ * ∇u[3]\n result[4] = μ * ∇u[4] - p[1]\n result[5] = -(∇u[1] + ∇u[4])\n end\n return nothing\nend\n\nfunction multiply_r(result, input, qpinfo)\n x = qpinfo.x\n result .= input * x[1]\n return nothing\nend\n\nend","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","text":"","category":"page"},{"location":"module_examples/Example240_FiniteElementVelocities/","page":"240: 2D Convection in quadratic stagnation flow velocity field","title":"240: 2D Convection in quadratic stagnation flow velocity field","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\nSystem state\nLinear solver control\nBlock preconditioning\nHistory handling\nMatrix extraction","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/#VoronoiFVM.fixed_timesteps!","page":"Solvers","title":"VoronoiFVM.fixed_timesteps!","text":"fixed_timesteps!(control,Δt; grow=1.0)\n\nModify control data such that the time steps are fixed to a geometric sequence such that Δtnew=Δtold*grow\n\n\n\n\n\n","category":"function"},{"location":"solver/#System-state","page":"Solvers","title":"System state","text":"","category":"section"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"VoronoiFVM.SystemState\nVoronoiFVM.SystemState(::Type, system::VoronoiFVM.AbstractSystem; data)\nVoronoiFVM.SystemState(system::VoronoiFVM.AbstractSystem; data)\nVoronoiFVM.solve!(state::VoronoiFVM.SystemState; kwargs...)\nBase.similar(state::VoronoiFVM.SystemState; kwargs...)","category":"page"},{"location":"solver/#VoronoiFVM.SystemState","page":"Solvers","title":"VoronoiFVM.SystemState","text":"mutable struct SystemState{Tv, Tp, TMatrix<:AbstractArray{Tv, 2}, TSolArray<:AbstractArray{Tv, 2}, TData}\n\nStructure holding state information for finite volume system.\n\nType parameters:\n\nTv: element type of solution vectors and matrix\nTMatrix: matrix type\nTSolArray: type of solution vector: (Matrix or SparseMatrixCSC)\nTData: type of user data\n\nType fields:\n\nsystem::VoronoiFVM.System: Related finite volume system\n\ndata::Any: User data\n\nsolution::AbstractMatrix: Solution vector\n\nmatrix::AbstractMatrix: Jacobi matrix for nonlinear problem\n\ndudp::Vector{TSolArray} where {Tv, TSolArray<:AbstractMatrix{Tv}}: Parameter derivative (vector of solution arrays)\n\nupdate::AbstractMatrix: Vector holding Newton update\n\nresidual::AbstractMatrix: Vector holding Newton residual\n\nlinear_cache::Union{Nothing, LinearSolve.LinearCache}: Linear solver cache\n\nparams::Vector: Parameter vector\n\nuhash::UInt64: Hash value of latest unknowns vector the assembly was called with (used by differential equation interface)\n\nhistory::Union{Nothing, VoronoiFVM.DiffEqHistory}: History record for solution process (used by differential equation interface)\n\n\n\n\n\n","category":"type"},{"location":"solver/#VoronoiFVM.SystemState-Tuple{Type, VoronoiFVM.AbstractSystem}","page":"Solvers","title":"VoronoiFVM.SystemState","text":"SystemState(Tv, system; data=system.physics.data, matrixtype=system.matrixtype)\n\nCreate state information for finite volume system.\n\nArguments:\n\nTv: value type of unknowns, matrix\nsystem: Finite volume system\n\nKeyword arguments:\n\ndata: User data. Default: data(system)\nmatrixtype. Default: system.matrixtype\n\n\n\n\n\n","category":"method"},{"location":"solver/#VoronoiFVM.SystemState-Tuple{VoronoiFVM.AbstractSystem}","page":"Solvers","title":"VoronoiFVM.SystemState","text":"SystemState(Tv, system; data=system.physics.data, matrixtype=system.matrixtype)\n\nCreate state information for finite volume system.\n\nArguments:\n\nTv: value type of unknowns, matrix\nsystem: Finite volume system\n\nKeyword arguments:\n\ndata: User data. Default: data(system)\nmatrixtype. Default: system.matrixtype\n\n\n\n\n\nSystemState(system; kwargs...)\n\nShortcut for creating state with value type defined by Tv type parameter of system\n\n\n\n\n\n","category":"method"},{"location":"solver/#CommonSolve.solve!-Tuple{VoronoiFVM.SystemState}","page":"Solvers","title":"CommonSolve.solve!","text":"solve!(state; kwargs...)\n\nBuilt-in solution method for VoronoiFVM.System. \n\nSolves finite volume system the satate is belonging to. Mutates the state and returns the solution.\n\nFor the keyword argumentsm see VoronoiFVM.solve.\n\n\n\n\n\n","category":"method"},{"location":"solver/#Base.similar-Tuple{VoronoiFVM.SystemState}","page":"Solvers","title":"Base.similar","text":"similar(state; data=state.data)\n\nCreate a new state of with the same system, different work arrays, and possibly different data. The matrix of the new state initially shares the sparsity structure with state.\n\n\n\n\n\n","category":"method"},{"location":"solver/#Linear-solver-control","page":"Solvers","title":"Linear solver control","text":"","category":"section"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"Linear systems are solved using LinearSolve.jl. Linear solve compatible solver strategies (factorizations, iterative solvers) can be specified wia method_linear keyword argument to LinearSolve (equivalent to the method_linear entry of SolverControl.","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"Currently supported possibilities are documented in the documentation of ExtendableSparse.jl.","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"VoronoiFVM.jl provides partitioning methods for block preconditioners.","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"Equationwise\npartitioning","category":"page"},{"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, mode)\n\nCalculate partitioning of system unknowns to be used in block preconditioners. Possible modes:\n\nEquationwise()\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 returned as history(solution)","category":"page"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"NewtonSolverHistory\nTransientSolverHistory\nVoronoiFVM.DiffEqHistory\nBase.summary(::NewtonSolverHistory)\nBase.summary(::TransientSolverHistory)\ndetails\nhistory\n\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/#VoronoiFVM.DiffEqHistory","page":"Solvers","title":"VoronoiFVM.DiffEqHistory","text":"mutable struct DiffEqHistory\n\nHistory information for DiffEqHistory\n\nnd::Any: number of combined jacobi/rhs evaluations\nnjac::Any: number of combined jacobi evaluations\nnf::Any: number of rhs evaluations\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(sol)\n\nReturn solver history 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(sol)\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(sol)\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\nreshape(::AbstractDiffEqArray,::VoronoiFVM.AbstractSystem)\nSciMLBase.ODEFunction","category":"page"},{"location":"solver/#SciMLBase.ODEProblem","page":"Solvers","title":"SciMLBase.ODEProblem","text":"ODEProblem(state,inival,tspan,callback=SciMLBase.CallbackSet())\n\nCreate an ODEProblem from a system state. See SciMLBase.ODEProblem(sys::VoronoiFVM.System, inival, tspan;kwargs...) for more documentation.\n\nDefined in VoronoiFVM.jl.\n\n\n\n\n\nODEProblem(system,inival,tspan,callback=SciMLBase.CallbackSet())\n\nCreate an ODEProblem in mass matrix form which can be handled 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\nDefined in VoronoiFVM.jl.\n\n\n\n\n\n","category":"type"},{"location":"solver/#Base.reshape-Tuple{AbstractDiffEqArray, VoronoiFVM.AbstractSystem}","page":"Solvers","title":"Base.reshape","text":"reshape(ode_solution, system; times=nothing, state=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 order) interpolated solution at the given moments of time will be returned.\n\nDefined in VoronoiFVM.jl.\n\n\n\n\n\n","category":"method"},{"location":"solver/#SciMLBase.ODEFunction","page":"Solvers","title":"SciMLBase.ODEFunction","text":" ODEFunction(state,inival=unknowns(system,inival=0),t0=0)\n\nCreate an ODEFunction. For more documentation, see SciMLBase.ODEFunction(state::VoronoiFVM.SystemState; kwargs...)\n\n\n\n\n\n ODEFunction(system,inival=unknowns(system,inival=0),t0=0)\n\nCreate an ODEFunction in mass matrix form to be handled 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":"type"},{"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":"NewtonControl","category":"page"},{"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/#Deprecated-API","page":"Solvers","title":"Deprecated API","text":"","category":"section"},{"location":"solver/","page":"Solvers","title":"Solvers","text":"The methods and struct in this section are deprecated as of version 2.4 and will be removed in version 3.","category":"page"},{"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":"VoronoiFVM.BlockStrategy\nNoBlock\nEquationBlock\nPointBlock","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":"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 OrdinaryDiffEqBDF\n    using OrdinaryDiffEqRosenbrock\n    using OrdinaryDiffEqSDIRK\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, data)\n    f[1] = u[1]\n    f[2] = u[2]\n    return nothing\nend;
\n\n\n
function bruss_diffusion(f, u, edge, data)\n    f[1] = bruss_D_1 * (u[1, 1] - u[1, 2])\n    f[2] = bruss_D_2 * (u[2, 1] - u[2, 2])\n    return nothing\nend;
\n\n\n
function bruss_reaction(f, u, node, data)\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]\n    return nothing\nend;
\n\n\n
begin\n\n    function ODESolver(system, inival, solver)\n        state = VoronoiFVM.SystemState(system)\n        problem = ODEProblem(state, inival, (0, bruss_tend))\n        odesol = solve(\n            problem,\n            solver,\n            dt = 1.0e-5, reltol = 1.0e-4\n        )\n        return reshape(odesol, system; state)\n    end\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)\nelse\n    bruss_X = -1:0.1:1\n    bruss_grid = simplexgrid(bruss_X, bruss_X)\nend;
\n\n\n
bruss_system = VoronoiFVM.System(\n    bruss_grid, species = [1, 2],\n    flux = bruss_diffusion, storage = bruss_storage, reaction = bruss_reaction\n);
\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 in 1:size(inival, 2)\n        inival[1, i] = fpeak(coord[:, i])\n        inival[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.history_details(bruss_tsol)...)
\n
(t_run = 16.681472442, nd = 1925, njac = 855, nf = 2780)
\n\n\n

dim:\\(\\;\\) method: \\(\\;\\) t: 150.0

\n\n\n\n\n\n\n
\n

Built with Julia 1.11.1 and

\n\n
\n\n","category":"page"},{"location":"plutostatichtml_examples/ode-brusselator/","page":"OrdinaryDiffEq.jl brusselator","title":"OrdinaryDiffEq.jl brusselator","text":"EditURL = \"https://github.com/WIAS-PDELib/VoronoiFVM.jl/blob/master/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 = \"png\", 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 implement 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])\n    return nothing\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)\n    return nothing\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)]\n    return nothing\nend
\n
boutflow (generic function with 1 method)
\n\n
function flowtransportsystem(grid; kwargs...)\n    data = FlowTransportData(; kwargs...)\n    return VoronoiFVM.System(\n        grid;\n        flux,\n        bcondition,\n        boutflow,\n        data,\n        outflowboundaries = [data.Γ_out],\n        species = [1, 2],\n    )\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    return (; 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}\n      dim =       1\n   nnodes =      11\n   ncells =      10\n  nbfaces =       2
\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.5000000000000003], out = [-1.0, -0.5000000000000003])
\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}\n      dim =       2\n   nnodes =     121\n   ncells =     200\n  nbfaces =      40
\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 VoronoiFVM.DenseSolutionArray{Float64, 2}:\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.5000000000000004], 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}\n      dim =       3\n   nnodes =    1331\n   ncells =    6000\n  nbfaces =    1200
\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 VoronoiFVM.DenseSolutionArray{Float64, 2}:\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.0000000000000369, 0.5000000000000185], out = [-1.000000000000039, -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.11.1 and

\nCairoMakie 0.12.16
\nExtendableGrids 1.9.2
\nGridVisualize 1.7.0
\nHypertextLiteral 0.9.5
\nPkg 1.11.0
\nPlutoUI 0.7.60
\nRevise 3.5.18
\nSimplexGridFactory 2.2.1
\nTest 1.11.0
\nTriangulate 2.3.4
\nUUIDs 1.11.0
\nVoronoiFVM 1.25.1\n
\n\n","category":"page"},{"location":"plutostatichtml_examples/outflow/","page":"Outflow boundary conditions","title":"Outflow boundary conditions","text":"EditURL = \"https://github.com/WIAS-PDELib/VoronoiFVM.jl/blob/master/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 = simplexgrid(X, Y)\n nn = num_nodes(grid)\n\n eps = 1.0e-2\n\n function reaction(f, u, node, data)\n f[1] = u[1]^2\n return nothing\n end\n\n function flux(f, u, edge, data)\n f[1] = eps * (u[1, 1]^2 - u[1, 2]^2)\n return nothing\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.0 * (x1^2 + x2^2))\n return nothing\n end\n\n function storage(f, u, node, data)\n f[1] = u[1]\n return nothing\n end\n\n function bcondition(f, u, node, data)\n boundary_dirichlet!(\n f,\n u,\n node;\n species = 1,\n region = 2,\n value = ramp(node.time; dt = (0, 0.1), du = (0, 1))\n )\n boundary_dirichlet!(\n f,\n u,\n node;\n species = 1,\n region = 4,\n value = ramp(node.time; dt = (0, 0.1), du = (0, 1))\n )\n return nothing\n end\n\n sys = VoronoiFVM.System(\n grid; reaction, flux, source, storage, bcondition, assembly,\n species = [1]\n )\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(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(),\n precon_linear = ILUZeroPreconditioner(),\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n\n @info \"Krylov-block1\"\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(),\n precon_linear = BlockPreconditioner(;\n partitioning = [1:(nn ÷ 2), (nn ÷ 2 + 1):nn],\n factorization = ILU0Preconditioner()\n ),\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n\n @info \"Krylov-block2\"\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(),\n precon_linear = BlockPreconditioner(;\n partitioning = [1:2:nn, 2:2:nn],\n factorization = UMFPACKFactorization()\n ),\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n\n @info \"Krylov - delayed factorization:\"\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(),\n precon_linear = SparspakFactorization(),\n keepcurrent_linear = false,\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n\n @info \"Krylov - jacobi:\"\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(),\n precon_linear = JacobiPreconditioner(),\n keepcurrent_linear = true,\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n\n @info \"Krylov - SA_AMG:\"\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(),\n precon_linear = SA_AMGPreconditioner(),\n keepcurrent_linear = true,\n kwargs...\n )\n @test norm(sol - umf_sol, Inf) < 1.0e-7\n\n @info \"Krylov - AMGCL_AMG:\"\n sol = solve(\n sys;\n inival = 0.5,\n method_linear = KrylovJL_BICGSTAB(),\n precon_linear = AMGCL_AMGPreconditioner(),\n keepcurrent_linear = true,\n kwargs...\n )\n return @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\n return nothing\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 ExtendableSparse: ILUZeroPreconBuilder\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, data)\n f[1] = (1 + p) * (u[1, 1]^2 - u[1, 2]^2)\n return nothing\n end\n\n function r!(f, u, edge, data)\n f[1] = p * u[1]^5\n return nothing\n end\n\n function bc!(f, u, node, data)\n boundary_dirichlet!(f, u, node, ispec, 1, 0.0)\n boundary_dirichlet!(f, u, node, ispec, 3, p)\n return nothing\n end\n\n X = collect(0:(1.0 / n):1)\n grid = simplexgrid(X, X)\n sys = VoronoiFVM.System(\n grid; valuetype, species = [1], flux = flux!, reaction = r!,\n bcondition = bc!\n )\n tff = VoronoiFVM.TestFunctionFactory(sys)\n tfc = testfunction(tff, [1], [3])\n sol = solve(sys; inival = 0.5)\n return [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 in 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 return sum(DF)\nend\n\nfunction fluxg!(f, u, edge, data)\n f[1] = (1 + data.p) * (u[1, 1]^2 - u[1, 2]^2)\n return nothing\nend\n\nfunction rg!(f, u, edge, data)\n f[1] = data.p * u[1]^5\n return nothing\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)\n return nothing\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 = simplexgrid(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(\n grid; valuetype = Tv, species = [1], flux = fluxg!,\n reaction = rg!, bcondition = bcg!, data,\n unknown_storage = :dense\n )\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)\n return [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 in 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 return sum(DG)\nend\n\n#########################################################################\n\nfunction fluxh!(f, u, edge, data)\n p = parameters(u)[1]\n f[1] = (1 + p) * (u[1, 1]^2 - u[1, 2]^2)\n return nothing\nend\n\nfunction rh!(f, u, edge, data)\n p = parameters(u)[1]\n f[1] = p * u[1]^5\n return nothing\nend\n\nfunction bch!(f, u, node, data)\n p = parameters(u)[1]\n boundary_dirichlet!(f, u, node, 1, 1, 0.0)\n boundary_dirichlet!(f, u, node, 1, 3, p)\n return nothing\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 = simplexgrid(X, X)\n\n sys = VoronoiFVM.System(\n grid; species = [1], flux = fluxh!, reaction = rh!,\n bcondition = bch!, unknown_storage = :dense, nparams = 1\n )\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 return 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 return nothing\n end\n\n dp = 0.05\n P = 0.1:dp:2\n state = VoronoiFVM.SystemState(sys)\n U0 = solve!(state; inival = 0.5, params = [P[1]])\n\n ndof = num_dof(sys)\n colptr = [i for i in 1:(ndof + 1)]\n rowval = [1 for i in 1:ndof]\n nzval = [1.0 for in 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 in P\n params[1] = p\n sol = solve!(state; inival = 0.5, params)\n\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 = state.matrix \\ vec(state.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 return sum(DH)\nend\n\nusing Test\nfunction runtests()\n testval = 489.3432830184927\n @test runf() ≈ testval\n @test rung() ≈ testval\n @test runh() ≈ testval\n return nothing\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 return 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 return 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 return check(u) && check(v) && check(w)\nend\n\nusing Test\nfunction runtests()\n return @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(;\n nref = 0, Plotter = nothing, verbose = false, unknown_storage = :sparse,\n brea = false, assembly = :edgewise\n )\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 = simplexgrid(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, data)\n f[1] = u[1, 1] - u[1, 2]\n return nothing\n end\n function storage!(f, u, node, data)\n f[1] = u[1]\n return nothing\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, data)\n if node.region == 3\n f[1] = -Q\n end\n return nothing\n end\n\n # Create physics\n physics = VoronoiFVM.Physics(;\n flux = flux!,\n storage = storage!,\n breaction = breaction!\n )\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!(\n vis, grid, U[1, :]; title = @sprintf(\"Q=%.2f\", q), clear = true,\n show = true\n )\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\n return nothing\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]\n return nothing\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]\n return nothing\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\n return nothing\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(max(-1 + eps_reg, -u[ic, 1]))\n mu2 = -log1p(max(-1 + eps_reg, -u[ic, 2]))\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]\n return nothing\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)\n return nothing\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 )\n h = 1.0 / convert(Float64, n)\n grid = simplexgrid(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\n sys = VoronoiFVM.System(\n grid,\n physics;\n unknown_storage = unknown_storage,\n species = [1, 2],\n assembly = assembly,\n )\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(\n sys;\n method_linear = UMFPACKFactorization(),\n inival,\n times = [0.0, 10],\n control = control,\n )\n\n vis = GridVisualizer(; Plotter = Plotter, layout = (1, 1), fast = true)\n for log10t in -4:0.025:0\n time = 10^(log10t)\n sol = tsol(time)\n scalarplot!(\n vis[1, 1],\n grid,\n sol[iphi, :];\n label = \"ϕ\",\n title = @sprintf(\"time=%.3g\", time),\n flimits = (0, 5),\n color = :green,\n )\n scalarplot!(\n vis[1, 1],\n grid,\n sol[ic, :];\n label = \"c\",\n flimits = (0, 5),\n clear = false,\n color = :red,\n )\n reveal(vis)\n end\n return sum(tsol.u[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 U .= inival\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!(\n 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 )\n scalarplot!(\n vis[1, 1],\n grid,\n U[ic, :];\n label = \"c\",\n flimits = (0, 5),\n clear = false,\n color = :red,\n )\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!(\n vis[2, 1],\n v,\n c;\n color = :green,\n clear = false,\n title = \"C_dl\",\n )\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(\n main(; unknown_storage = :sparse, dlcap = false, assembly = :edgewise),\n evolval;\n rtol = rtol,\n )\n @test isapprox(\n main(; unknown_storage = :sparse, dlcap = true, assembly = :edgewise),\n dlcapval;\n rtol = rtol,\n )\n @test isapprox(\n main(; unknown_storage = :dense, dlcap = false, assembly = :edgewise),\n evolval;\n rtol = rtol,\n )\n @test isapprox(\n main(; unknown_storage = :dense, dlcap = true, assembly = :edgewise),\n dlcapval;\n rtol = rtol,\n )\n @test isapprox(\n main(; unknown_storage = :sparse, dlcap = false, assembly = :cellwise),\n evolval;\n rtol = rtol,\n )\n @test isapprox(\n main(; unknown_storage = :sparse, dlcap = true, assembly = :cellwise),\n dlcapval;\n rtol = rtol,\n )\n @test isapprox(\n main(; unknown_storage = :dense, dlcap = false, assembly = :cellwise),\n evolval;\n rtol = rtol,\n )\n @test isapprox(\n main(; unknown_storage = :dense, dlcap = true, assembly = :cellwise),\n dlcapval;\n rtol = rtol,\n )\n return nothing\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/dev/solutions/index.html b/dev/solutions/index.html index 9616c2d2f..60754a23f 100644 --- a/dev/solutions/index.html +++ b/dev/solutions/index.html @@ -26,4 +26,4 @@ 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
Base.append!Function
append!(tsol::AbstractTransientSolution, t, sol)

Append time step solution sol as solution at time t to tsol. sol will be directly references in tsol. This method does not copy sol. If during a time steping process it is the same vector, a copy should appended.

Defined in VoronoiFVM.jl.

source
+ 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
Base.append!Function
append!(tsol::AbstractTransientSolution, t, sol)

Append time step solution sol as solution at time t to tsol. sol will be directly references in tsol. This method does not copy sol. If during a time steping process it is the same vector, a copy should appended.

Defined in VoronoiFVM.jl.

source
diff --git a/dev/solver/index.html b/dev/solver/index.html index 0bb22cd9b..378454404 100644 --- a/dev/solver/index.html +++ b/dev/solver/index.html @@ -6,4 +6,4 @@ 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.

SciMLBase.ODEProblemType
ODEProblem(state,inival,tspan,callback=SciMLBase.CallbackSet())

Create an ODEProblem from a system state. See SciMLBase.ODEProblem(sys::VoronoiFVM.System, inival, tspan;kwargs...) for more documentation.

Defined in VoronoiFVM.jl.

source
ODEProblem(system,inival,tspan,callback=SciMLBase.CallbackSet())

Create an ODEProblem in mass matrix form which can be handled by ODE solvers from DifferentialEquations.jl.

Parameters:

  • system: A VoronoiFVM.System
  • inival: Initial value. Consider to pass a stationary solution at tspan[1].
  • tspan: Time interval
  • callback : (optional) callback for ODE solver

The method returns an ODEProblem which can be solved by solve().

Defined in VoronoiFVM.jl.

source
Base.reshapeMethod
reshape(ode_solution, system; times=nothing, state=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 order) interpolated solution at the given moments of time will be returned.

Defined in VoronoiFVM.jl.

source
SciMLBase.ODEFunctionType
 ODEFunction(state,inival=unknowns(system,inival=0),t0=0)

Create an ODEFunction. For more documentation, see SciMLBase.ODEFunction(state::VoronoiFVM.SystemState; kwargs...)

source
 ODEFunction(system,inival=unknowns(system,inival=0),t0=0)

Create an ODEFunction in mass matrix form to be handled 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.

Deprecated API

The methods and struct in this section are deprecated as of version 2.4 and will be removed in version 3.

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

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
+GMRESIteration(factorization; memory=20, restart=true)

GMRES Iteration from Krylov.jl via LinearSolve.jl.

source

Block preconditioning

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
diff --git a/dev/system/index.html b/dev/system/index.html index c935c7861..cdb73e8af 100644 --- a/dev/system/index.html +++ b/dev/system/index.html @@ -36,4 +36,4 @@ edge::VoronoiFVM.AbstractEdge, u ) -> VoronoiFVM.VectorUnknowns -

Solution view on second edge node

source +

Solution view on second edge node

source