Postprocessing
Plotting
Plotting can be performed using the package GridVisualize.jl. This package extends the API with a couple of methods for systems:
GridVisualize.gridplot — Functiongridplot(sys::VoronoiFVM.AbstractSystem; kwargs...)
Plot grid behind system
GridVisualize.gridplot! — Functiongridplot!(
vis,
sys::VoronoiFVM.AbstractSystem;
kwargs...
) -> Any
Plot grid behind system
GridVisualize.scalarplot — Functionscalarplot(
sys::VoronoiFVM.AbstractSystem,
sol::AbstractMatrix;
species,
scale,
kwargs...
) -> Any
Plot one species from solution
scalarplot(
sys::VoronoiFVM.AbstractSystem,
sol::VoronoiFVM.AbstractTransientSolution;
species,
scale,
tscale,
kwargs...
)
Plot one species from transient solution
GridVisualize.scalarplot! — Functionscalarplot!(
vis,
sys::VoronoiFVM.AbstractSystem,
sol::AbstractMatrix;
species,
scale,
kwargs...
) -> Any
Plot one species from solution
scalarplot!(
vis,
sys::VoronoiFVM.AbstractSystem,
sol::VoronoiFVM.AbstractTransientSolution;
species,
scale,
tscale,
tlabel,
kwargs...
) -> Any
Plot one species from transient solution
VoronoiFVM.plothistory — Functionplothistory(tsol;
plots=[:timestepsizes,
:timestepupdates,
:newtonsteps,
:newtonupdates],
size=(700,600),
logmin=1.0e-20,
Plotter=GridVisualize.default_plotter(),
kwargs...)Plot solution history stored in tsol. The plots argument allows to choose which plots are shown.
Grid verification
VoronoiFVM.nondelaunay — Functionnondelaunay(grid;tol=1.0e-14, verbose=true)Return non-Delaunay edges. Returns a vector of tuples: Each tuple consists of (node1, node2, edge factor, region)
If 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.
Norms & volumes
LinearAlgebra.norm — Functionnorm(system, u)
norm(system, u, p)
Calculate Euklidean norm of the degree of freedom vector.
VoronoiFVM.lpnorm — Functionlpnorm(sys, u, p)
lpnorm(sys, u, p, species_weights)
Calculate weighted discrete $L^p$ norm of a solution vector.
VoronoiFVM.l2norm — Functionl2norm(sys, u)
l2norm(sys, u, species_weights)
Calculate weighted discrete $L^2(\Omega)$ norm of a solution vector.
VoronoiFVM.w1pseminorm — Functionw1pseminorm(sys, u, p)
w1pseminorm(sys, u, p, species_weights)
Calculate weighted discrete $W^{1,p}(\Omega)$ seminorm of a solution vector.
VoronoiFVM.h1seminorm — Functionh1seminorm(sys, u)
h1seminorm(sys, u, species_weights)
Calculate weighted discrete $H^1(\Omega)$ seminorm of a solution vector.
VoronoiFVM.w1pnorm — Functionw1pnorm(sys, u, p)
w1pnorm(sys, u, p, species_weights)
Calculate weighted discrete $W^{1,p}(\Omega)$ norm of a solution vector.
VoronoiFVM.h1norm — Functionh1norm(sys, u)
h1norm(sys, u, species_weights)
Calculate weighted discrete $H^1(\Omega)$ norm of a solution vector.
VoronoiFVM.lpw1pseminorm — Functionlpw1pseminorm(sys, u, p)
lpw1pseminorm(sys, u, p, species_weights)
Calculate weighted discrete $L^p([0,T];W^{1,p}(\Omega))$ seminorm of a transient solution.
VoronoiFVM.l2h1seminorm — Functionl2h1seminorm(sys, u)
l2h1seminorm(sys, u, species_weights)
Calculate weighted discrete $L^2([0,T];H^1(\Omega))$ seminorm of a transient solution.
VoronoiFVM.lpw1pnorm — Functionlpw1pnorm(sys, u, p)
lpw1pnorm(sys, u, p, species_weights)
Calculate weighted discrete $L^p([0,T];W^{1,p}(\Omega))$ norm of a transient solution.
VoronoiFVM.l2h1norm — Functionl2h1norm(sys, u)
l2h1norm(sys, u, species_weights)
Calculate weighted discrete $L^2([0,T];H^1(\Omega))$ norm of a transient solution.
VoronoiFVM.nodevolumes — Functionnodevolumes(system)
Calculate volumes of Voronoi cells.
Solution integrals
VoronoiFVM.integrate — Methodintegrate(system,F,U; boundary=false, data=system.physics.data)Integrate node function (same signature as reaction or storage) F of solution vector U region-wise over domain or boundary. The result is an nspec x nregion matrix $\int_{\Omega_j} F_i(U)\, dx$ or $\int_{\Gamma_j} F_i(U)\, ds$.
VoronoiFVM.integrate — Methodintegrate(system,F, Ut; boundary=false, data=system.physics.data)Integrate transient solution vector region-wise over domain or boundary. The result is an nspec x nregion x nsteps DiffEqArray $\int_{\Omega_j} F_i(U_k)\, dx$ or $\int_{\Gamma_j} F_i(U_k)\, ds$.
VoronoiFVM.integrate — Methodintegrate(system,U; boundary=false)Integrate solution vector region-wise over domain or boundary. The result is an nspec x nregion matrix $\int_{\Omega_j} U_i\, dx$ or $\int_{\Gamma_j} U_i\, ds$.
VoronoiFVM.edgeintegrate — Functionedgeintegrate(system,F,U; boundary=false)Integrate edge function (same signature as flux function) F of solution vector region-wise over domain or boundary. The result is an nspec x nregion matrix.
Boundary fluxes
Continuum level motivation
For the calculation of boundary fluxes, we use a method inspired by H. Gajewski, WIAS Report No 6, 1993: "The regularity of the (weak) solutions of the drift-diffusion equations, which are guaranteed by existence statements, is not sufficient to justify a naive calculation of the currents as boundary integrals. Rather, it has also proven to be practical and necessary to reduce contact currents to volume integrals in accordance with the definition of weak solutions using suitable test functions."
We start with the continuous case for a problem
\[ \partial_t s(u) + \nabla\cdot \vec j(u) + r(u) - f =0 \]
defined in a domain $\Omega$ with a boundary $Γ=⋃_{i\in B}Γ_i$ subdivided into non-overlapping parts where the set of boundary regions $B$ is subdivided into $B=B_N\cup B_0 \cup B_1$. Assume that $\vec j\cdot \vec n=0$ on $\Gamma_i$ for $i\in B_N$.
Let $T(x)$ be a smooth test function such that
- $\nabla T \cdot \vec n|_{Γ_i}= 0$ for $i\in B_N$,
- $T|_{Γ_i} = 0$ for $i\in B_0$,
- $T|_{Γ_i} = 1$ for $i\in B_1$.
Here, we obtain it by solving the Laplace equation
\[ -\Delta T =0 \quad \text{in}\; \Omega\]
To obtain as a quantity of interest the flux $Q$ through the boundaries $\Gamma_i$ for $i\in B_1$ of, calculate:
\[ \begin{aligned} Q=Q(t)&=\sum_{i \in B_1}\int\limits_{\Gamma_i} T\vec j(u)\cdot\vec n \,d\gamma\\ &=\sum_{i \in B_1}\int\limits_{\Gamma_i} T\vec j(u)\cdot\vec n \,d\gamma + \sum_{i \in B_N}\int\limits_{\Gamma_i} T\vec j(u)\cdot\vec n \,d\gamma + \sum_{i \in B_0}\int\limits_{\Gamma_l} T\vec j(u)\cdot\vec n \,d\gamma\\ &=\int\limits_{\Gamma} T\vec j(u)\cdot\vec n \,d\gamma\\ &=\int\limits_{\Omega}\nabla\cdot(T\vec j(u))\,d\omega\\ &=\int\limits_{\Omega}\nabla T \cdot \vec{j(u)} \,d\omega + \int\limits_{\Omega}T \nabla\cdot \vec{j(u)} \,d\omega\\ &=\int\limits_{\Omega}\nabla T \cdot \vec{j}(u) \,d\omega - \int\limits_{\Omega}T r(u) \,d\omega + \int\limits_{\Omega}T f \,d\omega - \int\limits_{\Omega}T \partial_ts(u) \,d\omega \end{aligned}\]
Discrete approximations of the integrals
The discete versions of these integrals for evaluation at $t=t^n$ are as follows:
\[ \begin{aligned} \int\limits_{\Omega}T r(u) \,d\omega &\approx \sum_{k\in N} T_kr(u^n_k)|\omega_k| =: I_{func}(r, T,u^n)\\ \int\limits_{\Omega}T f \,d\omega &\approx \sum_{k\in N} T_k f_k|\omega_k| =: I_{src}(f, T)\\ \int\limits_{\Omega}T \partial_ts(u) \,d\omega &\approx \frac{ I_{func}(s,T,u^n) -I_{func}(s,T,u^{n-1})}{t^n-t^{n-1}}\\ \int\limits_{\Omega}\nabla T \cdot \vec{j}(u^n) d\omega &\approx \sum\limits_{\stackrel{k,l}{\partial \omega_k \cup \partial \omega_l\neq\emptyset}} (T_{k}-T_{l})\frac{\sigma_{kl}}{h_{kl}}g(u^n_k, u^n_l) =: I_{flux}(j,T,u) \end{aligned}\]
General flux calculation API
VoronoiFVM.TestFunctionFactory — Typemutable struct TestFunctionFactory{Tu, Tv}Data structure containing DenseSystem used to calculate test functions for boundary flux calculations.
Type parameters:
Tu: value type of test functionsTv: Default value type of system
Fields:
system::VoronoiFVM.AbstractSystem{Tv} where Tv: Original system
state::VoronoiFVM.SystemState{Tu, Tp, TMatrix, TGenericMatrix, TSolArray} where {Tu, Tp, TMatrix<:AbstractMatrix{Tu}, TGenericMatrix, TSolArray<:AbstractMatrix{Tu}}: Test function system state
control::SolverControl: Solver control
VoronoiFVM.TestFunctionFactory — MethodTestFunctionFactory(system; control= SolverControl())Constructor for TestFunctionFactory from system.
VoronoiFVM.testfunction — Functiontestfunction(factory::TestFunctionFactory, B_0, B_1)Create a test function $T$ defined in the domain $Ω$ given by the system behind the factory object. Assume that the boundary $Γ=∂Ω=⋃_{i∈ B}Γ_i$ is subdivided into non-overlapping parts where the set of boundary regions $B$ is subdivided into $B=B_N∪ B_0 ∪ B_1$. Create $T$ by solving
\[ -Δ T =0 \quad \text{in}\; Ω\]
such that
- $∇ T ⋅ \vec n|_{Γ_i}= 0$ for $i∈ B_N$
- $T|_{Γ_i} = 0$ for $i∈ B_0$,
- $T|_{Γ_i} = 1$ for $i∈ B_1$.
Returns a vector representing T.
VoronoiFVM.integrate — Method integrate(system, T, U::AbstractMatrix)Calculate test function integral for a steady state solution $u$ $∫_Γ T \vec j(u) ⋅ \vec n dω ≈ I_{flux}(j, T,u)-I_{func}(r,T,u) + I_{src}(f,T)$, see the definition of test function integral contributions.
The result is a nspec vector giving one value of the integral for each species.
VoronoiFVM.integrate — Methodintegrate(system,T, U::AbstractTransientSolution; rate=true, params, data)Calculate test function integrals for the transient solution $∫_Γ T \vec j(u^n) ⋅ \vec n dω ≈ I_{flux}(j,T,u^n)-I_{func}(r,T,u^n)-\frac{I_{func}(s,T,u^n)-I_{func}(s,T,u^{n-1})}{t^n-t^{n-1}} + I_{src}(f,T)$ for each time interval $(t^{n-1}, t^n)$, see the definition of test function integral contributions.
Keyword arguments:
params: vector of parameters used to calculateU. Default:[].data: user data used to calculateU. Default:data(system)rate: Ifrate=true(default), calculate the flow rate (per second) through the corresponding boundary. Otherwise, calculate the absolute amount per time interval.
The result is a nspec x (nsteps-1) DiffEqArray.
Special purpose cases
VoronoiFVM.integrate — Method integrate(system, T, U::AbstractMatrix, Uold::AbstractMatrix, Δt; kwargs...)Calculate test function integrals for the implicit Euler time step results $u^n≡$ U and $u^{n-1}≡$ Uold as $∫_Γ T \vec j(u^n) ⋅ \vec n dω ≈ I_{flux}(j,T,u^n)-I_{func}(r,T,u^n)-\frac{I_{func}(s,T,u^n)-I_{func}(s,T,u^{n-1})}{Δt} + I_{src}(f,T)$, see the definition of test function integral contributions.
Keyword arguments:
params: vector of parameters used to calculateU. Default:[].data: user data used to calculateU. Default:data(system)
The result is a nspec vector giving one value of the integral for each species.
VoronoiFVM.integrate_TxFunc — Functionintegrate_TxFunc(system, T, f!, U; kwargs...)Calculate $I_{func}(f!,T, U)=∫_Ω T⋅ f!(U) dω$ for a test function T and unknown vector U. The function f! shall have the same signature as a storage or reaction function. See the definition of test function integral contributions.
VoronoiFVM.integrate_TxSrc — Functionintegrate_TxSrc(system, T, f!; kwargs...)Calculate $I_{src}(f!,T)= ∫_Ω T⋅ f! dω$ for a test function T. The functionf!` shall have the same signature as a storage or reaction function.
VoronoiFVM.integrate_∇TxFlux — Function integrate_∇TxFlux(system, T, f!, U; kwargs...)Calculate $I_{flux}(f!,T, U)=∫_Ω ∇T⋅ f!(U) dω$ for a test function T and unknown vector U. The function f! shall have the same signature as a flux function.
integrate_∇TxFlux(system, T, U; kwargs...)Calculate $I_{flux}(f!,T, U)=∫_Ω ∇T⋅ f!(U) dω$ for a test function T and unknown vector U, where flux! is the system flux.
VoronoiFVM.integrate_TxEdgefunc — Function integrate_TxEdgeFunc(system, T, f!, U; kwargs...)Calculate $∫_Ω T⋅ f!(U) dω$ for a test function T and unknown vector U. The function f! shall have the same signature as a flux function, but is assumed to describe a reaction term given on edges.
VoronoiFVM.integrate_stdy — Functionintegrate_stdy(system, T, U; kwargs...)Calculate steady state contribution $I_j(T,u) - I_r(T,u)$ to test function integral $∫_Γ T \vec j(u) ⋅ \vec n dω$ for a given timestep solution. See the time step integrate method.
Used for impedance calculations.
VoronoiFVM.integrate_tran — Functionintegrate_tran(system, T, U; kwargs...)Calculate storage term contribution to test function integral. Used for impedance calculations.
Nodal flux reconstruction
VoronoiFVM.nodeflux — Functionnodeflux(system, F,U; data)Reconstruction 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.
The 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 ).
The return value is a dim x nspec x nnodes vector.
CAVEAT: there is a possible unsolved problem with the values at domain corners in the code. Please see any potential boundary artifacts as a manifestation of this issue and report them.
nodeflux(system, U; data)Reconstruction of the edge flux as vector function on the nodes of the triangulation. See nodeflux(system,F,U;data).
The flux of species i can e.g. plotted via GridVisualize.vectorplot.
Example:
ispec=3
vis=GridVisualizer(Plotter=Plotter)
scalarplot!(vis,grid,solution[ispec,:],clear=true,colormap=:summer)
vectorplot!(vis,grid,nf[:,ispec,:],clear=false)
reveal(vis)Impedance calculatiom
Impedance calculation can be seen as a postprocessing step after the solution of the unexcited stationary system.
VoronoiFVM.AbstractImpedanceSystem — Typeabstract type AbstractImpedanceSystem{Tv<:Number}Abstract type for impedance system.
VoronoiFVM.ImpedanceSystem — Typemutable struct ImpedanceSystem{Tv} <: VoronoiFVM.AbstractImpedanceSystem{Tv}Concrete type for impedance system.
sysnzval::AbstractArray{Complex{Tv}, 1} where Tv: Nonzero pattern of time domain system matrix
storderiv::AbstractMatrix: Derivative of storage term
matrix::AbstractArray{Complex{Tv}, 2} where Tv: Complex matrix of impedance system
F::AbstractArray{Complex{Tv}, 2} where Tv: Right hand side of impedance system
U0::AbstractMatrix: Stationary state
VoronoiFVM.ImpedanceSystem — MethodImpedanceSystem(system, U0, excited_spec, excited_bc)
Construct impedance system from time domain system sys and steady state solution U0 under the assumption of a periodic perturbation of species excited_spec at boundary excited_bc.
VoronoiFVM.ImpedanceSystem — MethodImpedanceSystem(system, U0; λ0)
Construct impedance system from time domain system sys and steady state solution U0
CommonSolve.solve! — Methodsolve!(UZ, impedance_system, ω)
Solve the impedance system for given frequency ω.
VoronoiFVM.freqdomain_impedance — Methodfreqdomain_impedance(
impedance_system,
ω,
U0,
excited_spec,
excited_bc,
excited_bcval,
dmeas_stdy,
dmeas_tran
)
Calculate reciprocal value of impedance.
- excitedspec,excitedbc,excited_bcval are ignored.
This is deprecated: use impedance.
VoronoiFVM.impedance — Methodimpedance(impedance_system,ω, U0 ,
excited_spec, excited_bc, excited_bcval,
dmeas_stdy,
dmeas_tran
)
Calculate impedance.
- ω: frequency
- U0: steady state slution
- dmeas_stdy: Derivative of steady state part of measurement functional
- dmeas_tran Derivative of transient part of the measurement functional
VoronoiFVM.measurement_derivative — Methodmeasurement_derivative(system, measurement_functional, U0)
Calculate the derivative of the scalar measurement functional at steady state U0
Usually, this functional is a test function integral. Initially, we assume that its value depends on all unknowns of the system.
VoronoiFVM.unknowns — Methodunknowns(impedance_system)
Create a vector of unknowns of the impedance system