diff --git a/CHANGELOG.md b/CHANGELOG.md index f70119ba4..bc90d1d88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,18 @@ # Changes ## v3.0 Planned (pending some improvements in LinearSolve) - - use `precs` based linear solver API, see https://github.com/SciML/LinearSolve.jl/pull/514 - - stop re-exporting ForwardDiff.value - - try to remove type piracies - - remove `params` from edge, node structs (appearantly never used) + - Remove VoronoiFVM solver strategies + - Stop re-exporting ForwardDiff.value + - Try to remove type piracies + - Remove `params` from edge, node structs (appearantly never used) + +## v2.4.0 November 11, 2024 + - Use `precs` based linear solver API, see https://github.com/SciML/LinearSolve.jl/pull/514 with ExtendableSparse 1.6 + - Deprecate VoronoiFVM solver strategies + +## v2.3.0 November 5, 2024 + - Allow to use result of time embedding a steady state for impedance + - Move to WIAS-PDELib org ## v2.2.0 October 30, 2024 - Add `params` to SystemState, allow to pass params to ODEProblem diff --git a/docs/src/citations.bib b/CITATIONS.bib similarity index 99% rename from docs/src/citations.bib rename to CITATIONS.bib index c21cf025d..61bf2887a 100644 --- a/docs/src/citations.bib +++ b/CITATIONS.bib @@ -28,7 +28,7 @@ @techreport{qureshi4921855reduced title={Reduced Order Cfd Modeling Approach Based on the Asymptotic Expansion-An Application for Heterogeneous Catalytic Systems}, author={Qureshi, Muhammad Uzair and Matera, Sebastian and Runge, Daniel and Merdon, Christian and Fuhrmann, J{\"u}rgen and Repke, Jens-Uwe and Br{\"o}sigke, Georg}, institution = "SSRN", - url = {https://ssrn.com/abstract=4921855} + url = {https://ssrn.com/abstract=4921855}, year={2024} } diff --git a/Project.toml b/Project.toml index 4eab3986e..6ad8d901f 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "VoronoiFVM" uuid = "82b139dc-5afc-11e9-35da-9b9bdfd336f3" authors = ["Jürgen Fuhrmann ", "Patrick Jaap", "Daniel Runge", "Dilara Abdel", "Jan Weidner", "Alexander Seiler", "Patricio Farrell", "Matthias Liero"] -version = "2.3.0" +version = "2.4.0" [deps] BandedMatrices = "aae01518-5342-5314-be14-df237901396f" @@ -42,13 +42,13 @@ DiffResults = "1" DocStringExtensions = "0.8,0.9" ExtendableFEMBase = "0.7,0.8" ExtendableGrids = "1.10.1" -ExtendableSparse = "1.5.1" +ExtendableSparse = "1.6" ForwardDiff = "0.10.35" GridVisualize = "0.5.2,0.6.1,1" InteractiveUtils = "1.9" JLD2 = "0.4.29, 0.5" LinearAlgebra = "1.9" -LinearSolve = "2.2" +LinearSolve = "2.36" Printf = "1.9" Random = "1.9" RecursiveArrayTools = "2,3" diff --git a/README.md b/README.md index 0dfa6e0d3..7e8d8fb8a 100644 --- a/README.md +++ b/README.md @@ -41,13 +41,13 @@ VoronoiFVM.jl and most of these packages are part of the meta package [PDELib.j ## Some projects and packages using VoronoiFVM.jl +- [ChargeTransport.jl: Drift diffusion simulator for semiconductor devices](https://github.com/PatricioFarrell/ChargeTransport.jl) +- [LiquidElectrolytes.jl: Generalized Nernst-Planck-Poisson model for liquid electrolytes](https://github.com/j-fu/LiquidElectrolytes.jl) - [MultiComponentReactiveMixtureProject: Model for heat and multi-component, reactive gas phase transport in porous media.](https://github.com/DavidBrust/MultiComponentReactiveMixtureProject) - [RfbScFVM: Performance prediction of flow battery cells](https://github.com/Isomorph-Electrochemical-Cells/RfbScFVM) -- [ChargeTransport.jl: Drift diffusion simulator for semiconductor devices](https://github.com/PatricioFarrell/ChargeTransport.jl) - [MosLab.jl: From semiconductor to transistor level modeling in Julia](https://github.com/Rapos0/MOSLab.jl) -- [LiquidElectrolytes.jl: Generalized Nernst-Planck-Poisson model for liquid electrolytes](https://github.com/j-fu/LiquidElectrolytes.jl) ## Citation -If you use this package in your work, please cite it according to [CITATION.cff](https://raw.githubusercontent.com/WIAS-PDELib/VoronoiFVM.jl/master/CITATION.cff) +If you use this package in your work, please cite it according to [CITATION.cff](https://raw.githubusercontent.com/WIAS-PDELib/VoronoiFVM.jl/master/CITATION.cff). diff --git a/docs/make.jl b/docs/make.jl index b46345619..afe5b9160 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -9,7 +9,7 @@ function make(; with_examples = true, with_notebooks = true) bib = CitationBibliography( - joinpath(@__DIR__, "src", "citations.bib"); + joinpath(@__DIR__, "..", "CITATIONS.bib"); style=:numeric ) diff --git a/docs/src/index.md b/docs/src/index.md index 18c9b9138..06330bbd0 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -7,7 +7,7 @@ $(read("../../README.md",String)) # Papers and preprints using this package -Please consider a pull request if you have published work which could be added to this list. +Please consider a pull request updating `CITATION.bib` if you have published work which could be added to this list. ```@bibliography diff --git a/docs/src/solver.md b/docs/src/solver.md index 76caaea6e..2da03a02a 100644 --- a/docs/src/solver.md +++ b/docs/src/solver.md @@ -18,7 +18,7 @@ Overview: - [Solve method](@ref "Solve method") - [Solver control](@ref "Solver control") - [System state](@ref "System state") -- [Linear solver stragies](@ref "Linear solver strategies") +- [Linear solver control](@ref "Linear solver control") - [Block preconditioning](@ref "Block preconditioning") - [History handling](@ref "History handling") - [Matrix extraction](@ref "Matrix extraction") @@ -42,28 +42,22 @@ VoronoiFVM.solve!(state::VoronoiFVM.SystemState; kwargs...) Base.similar(state::VoronoiFVM.SystemState; kwargs...) ``` -### Linear solver strategies -```@docs -VoronoiFVM.LinearSolverStrategy -DirectSolver -CGIteration -BICGstabIteration -GMRESIteration -``` +### Linear solver control +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`](@ref). -### Block preconditioning -This feature is under development as of 1.6. +Currently supported possibilities are documented in the +[documentation of ExtendableSparse.jl](https://wias-pdelib.github.io/ExtendableSparse.jl/stable/linearsolve/#Solving-with-LinearSolve.jl). + +VoronoiFVM.jl provides partitioning methods for block preconditioners. ```@docs -VoronoiFVM.BlockStrategy -NoBlock -EquationBlock -PointBlock Equationwise partitioning ``` - ### History handling If `log` is set to true in `solve`, the history of newton iterations and time/embedding steps is recorded and returned as `history(solution)` @@ -147,5 +141,23 @@ are supported for backward compatibility. NewtonControl ``` +## 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 +```@docs +VoronoiFVM.LinearSolverStrategy +DirectSolver +CGIteration +BICGstabIteration +GMRESIteration +``` +### Block preconditioning + +```@docs +VoronoiFVM.BlockStrategy +NoBlock +EquationBlock +PointBlock +``` diff --git a/examples/Example003_Solvers.jl b/examples/Example003_Solvers.jl new file mode 100644 index 000000000..49a5cc86d --- /dev/null +++ b/examples/Example003_Solvers.jl @@ -0,0 +1,158 @@ +#= +# 003: New linear solver API +([source code](@__SOURCE_URL__)) +=# + +module Example003_Solvers + +## under development + +using Printf +using VoronoiFVM +using ExtendableGrids +using GridVisualize +using LinearSolve +using ExtendableSparse +using ExtendableSparse: ILUZeroPreconBuilder, JacobiPreconBuilder, SmoothedAggregationPreconBuilder +using SparseArrays +using AMGCLWrap +using AlgebraicMultigrid +using LinearAlgebra + + +using Test + + +function main(; n = 10, Plotter = nothing, assembly = :edgwwise, kwargs...) + h = 1.0 / convert(Float64, n) + X = collect(0.0:h:1.0) + Y = collect(0.0:h:1.0) + + grid = simplexgrid(X, Y) + nn = num_nodes(grid) + + eps = 1.0e-2 + + function reaction(f, u, node, data) + f[1] = u[1]^2 + end + + function flux(f, u, edge, data) + f[1] = eps * (u[1, 1]^2 - u[1, 2]^2) + end + + function source(f, node, data) + x1 = node[1] - 0.5 + x2 = node[2] - 0.5 + f[1] = exp(-20.0 * (x1^2 + x2^2)) + end + + function storage(f, u, node, data) + f[1] = u[1] + end + + function bcondition(f, u, node, data) + boundary_dirichlet!(f, + u, + node; + species = 1, + region = 2, + value = ramp(node.time; dt = (0, 0.1), du = (0, 1))) + boundary_dirichlet!(f, + u, + node; + species = 1, + region = 4, + value = ramp(node.time; dt = (0, 0.1), du = (0, 1))) + end + + sys = VoronoiFVM.System(grid; reaction, flux, source, storage, bcondition, assembly, + species = [1]) + @info "UMFPACK:" + umf_sol = solve(sys; inival = 0.5, method_linear = LinearSolve.UMFPACKFactorization(), kwargs...) + + @info "KLU:" + sol = solve(sys; inival = 0.5, method_linear = LinearSolve.KLUFactorization(), kwargs...) + @test norm(sol - umf_sol, Inf)<1.0e-7 + + @info "Sparspak:" + sol = solve(sys; inival = 0.5, method_linear = LinearSolve.SparspakFactorization(), kwargs...) + @test norm(sol - umf_sol, Inf)<1.0e-7 + + @info "Krylov-ilu0:" + sol = solve(sys; + inival = 0.5, + method_linear = KrylovJL_BICGSTAB(precs=ILUZeroPreconBuilder()), + kwargs...) + @test norm(sol - umf_sol, Inf)<1.0e-7 + + @info "Krylov-block1" + precs=BlockPreconBuilder(;precs=ILUZeroPreconBuilder(), partitioning= A-> [1:(size(A,1) ÷ 2), (size(A,1) ÷ 2 + 1):size(A,1)]) + sol = solve(sys; + inival = 0.5, + method_linear = KrylovJL_BICGSTAB(;precs), + kwargs...) + @test norm(sol - umf_sol, Inf)<1.0e-7 + + @info "Krylov-block2" + precs=BlockPreconBuilder(;precs=ILUZeroPreconBuilder(), partitioning= A-> [1:2:size(A,1), 2:2:size(A,1)]) + sol = solve(sys; + inival = 0.5, + method_linear = KrylovJL_BICGSTAB(;precs), + log=true, + kwargs...) + @test norm(sol - umf_sol, Inf)<1.0e-7 + + @info "Krylov - delayed factorization:" + sol = solve(sys; + inival = 0.5, + method_linear = KrylovJL_BICGSTAB(;precs=LinearSolvePreconBuilder(SparspakFactorization())), + keepcurrent_linear =false, + log=true, + kwargs...) + @test norm(sol - umf_sol, Inf)<1.0e-7 + @test summary(history(sol)).factorizations == 1 + + @info "Krylov - jacobi:" + sol = solve(sys; + inival = 0.5, + method_linear = KrylovJL_BICGSTAB(;precs=JacobiPreconBuilder()), + keepcurrent_linear = true, log=true, + kwargs...) + @test norm(sol - umf_sol, Inf)<1.0e-7 + @test summary(history(sol)).factorizations > 1 + + @info "Krylov - SA_AMG:" + sol = solve(sys; + inival = 0.5, + method_linear = KrylovJL_BICGSTAB(;precs=SmoothedAggregationPreconBuilder()), + keepcurrent_linear = true, + kwargs...) + @test norm(sol - umf_sol, Inf)<1.0e-7 + + @info "Krylov - AMGCL_AMG:" + sol = solve(sys; + inival = 0.5, + method_linear = KrylovJL_BICGSTAB(;precs=AMGPreconBuilder()), + keepcurrent_linear = true, + kwargs...) + @test norm(sol - umf_sol, Inf)<1.0e-7 + + @info "Krylov - AMGnCL_RLX:" + sol = solve(sys; + inival = 0.5, + method_linear = KrylovJL_BICGSTAB(;precs=RLXPreconBuilder()), + keepcurrent_linear = true, + kwargs...) + @test norm(sol - umf_sol, Inf)<1.0e-7 +end + +function runtests() + @testset "edgewise" begin + main(; assembly = :edgewise) + end + @testset "cellwise" begin + main(; assembly = :cellwise) + end +end +end diff --git a/examples/Example206_JouleHeat.jl b/examples/Example206_JouleHeat.jl index 49218a9f3..854165c01 100644 --- a/examples/Example206_JouleHeat.jl +++ b/examples/Example206_JouleHeat.jl @@ -93,9 +93,8 @@ function main(; nref = 0, Plotter = nothing, verbose = "and", unknown_storage = species = [iϕ, iT], assembly = assembly) sol = solve(sys; verbose, - method_linear = KrylovJL_BICGSTAB(), - precon_linear = UMFPACKFactorization(), - keepcurrent_linear =false, + method_linear = KrylovJL_BICGSTAB(precs=LinearSolvePreconBuilder(UMFPACKFactorization())), + keepcurrent_linear =false ) vis = GridVisualizer(; Plotter, layout = (2, 1)) diff --git a/examples/Example207_NonlinearPoisson2D.jl b/examples/Example207_NonlinearPoisson2D.jl index 6d1aff1b9..e70e03168 100644 --- a/examples/Example207_NonlinearPoisson2D.jl +++ b/examples/Example207_NonlinearPoisson2D.jl @@ -6,14 +6,13 @@ module Example207_NonlinearPoisson2D using Printf using VoronoiFVM using ExtendableGrids -using ExtendableSparse +using ExtendableSparse: ILUZeroPreconBuilder using GridVisualize using LinearSolve using ILUZero function main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :sparse, - method_linear = nothing, assembly = :edgewise, - precon_linear = A -> VoronoiFVM.Identity()) + method_linear = nothing, assembly = :edgewise) h = 1.0 / convert(Float64, n) X = collect(0.0:h:1.0) Y = collect(0.0:h:1.0) @@ -46,7 +45,6 @@ function main(; n = 10, Plotter = nothing, verbose = false, unknown_storage = :s control.verbose = verbose control.reltol_linear = 1.0e-5 control.method_linear = method_linear - control.precon_linear = precon_linear tstep = 0.01 time = 0.0 u15 = 0 @@ -69,15 +67,15 @@ function runtests() testval = 0.3554284760906605 @test main(; unknown_storage = :sparse, assembly = :edgewise) ≈ testval && main(; unknown_storage = :dense, assembly = :edgewise) ≈ testval && - main(; unknown_storage = :sparse, method_linear = KrylovJL_CG(), precon_linear = ILUZeroPreconditioner, + main(; unknown_storage = :sparse, method_linear = KrylovJL_CG(precs=ILUZeroPreconBuilder()), assembly = :edgewise) ≈ testval && - main(; unknown_storage = :dense, method_linear = KrylovJL_CG(), precon_linear = ILUZeroPreconditioner, + main(; unknown_storage = :dense, method_linear = KrylovJL_CG(precs=ILUZeroPreconBuilder()), assembly = :edgewise) ≈ testval && main(; unknown_storage = :sparse, assembly = :cellwise) ≈ testval && main(; unknown_storage = :dense, assembly = :cellwise) ≈ testval && - main(; unknown_storage = :sparse, method_linear = KrylovJL_CG(), precon_linear = ILUZeroPreconditioner, + main(; unknown_storage = :sparse, method_linear = KrylovJL_CG(precs=ILUZeroPreconBuilder()), assembly = :cellwise) ≈ testval && - main(; unknown_storage = :dense, method_linear = KrylovJL_CG(), precon_linear = ILUZeroPreconditioner, + main(; unknown_storage = :dense, method_linear = KrylovJL_CG(precs=ILUZeroPreconBuilder()), assembly = :cellwise) ≈ testval end end diff --git a/examples/Example430_ParameterDerivativesStationary.jl b/examples/Example430_ParameterDerivativesStationary.jl index 7e73b1b8c..c4f8e3e2c 100644 --- a/examples/Example430_ParameterDerivativesStationary.jl +++ b/examples/Example430_ParameterDerivativesStationary.jl @@ -11,6 +11,7 @@ module Example430_ParameterDerivativesStationary using VoronoiFVM, ExtendableGrids using GridVisualize using ExtendableSparse +using ExtendableSparse: ILUZeroPreconBuilder using ForwardDiff, DiffResults using SparseDiffTools, SparseArrays using ILUZero, LinearSolve @@ -116,7 +117,7 @@ function rung(; Plotter = nothing, method_linear = SparspakFactorization(), n = tfc = testfunction(tff, [1], [3]) end data.p = P[1] - sol = solve(sys; inival = 0.5, method_linear, precon_linear = ILUZeroPreconditioner()) + sol = solve(sys; inival = 0.5, method_linear) [integrate(sys, tfc, sol)[1]] end diff --git a/examples/Example510_Mixture.jl b/examples/Example510_Mixture.jl index 2e0ccb39c..753bb1a3b 100644 --- a/examples/Example510_Mixture.jl +++ b/examples/Example510_Mixture.jl @@ -64,6 +64,7 @@ using AMGCLWrap using Random using StrideArraysCore: @gc_preserve, StrideArray, StaticInt, PtrArray using LinearSolve, ExtendableSparse +using ExtendableSparse: ILUZeroPreconBuilder using StaticArrays using ExtendableSparse @@ -193,7 +194,15 @@ function main(; n = 11, nspec = 5, if verbose @info "Strategy: $(strategy)" end - control = SolverControl(strategy, sys) + if !isnothing(strategy) && hasproperty(strategy,:precs) + if isa(strategy.precs, BlockPreconBuilder) + strategy.precs.partitioning=A->partitioning(sys, Equationwise()) + end + if isa(strategy.precs, ILUZeroPreconBuilder) && strategy.precs.blocksize!=1 + strategy.precs.blocksize=nspec + end + end + control = SolverControl(method_linear=strategy) control.maxiters = 500 if verbose @info control.method_linear @@ -207,15 +216,14 @@ end using Test function runtests() - - strategies = [DirectSolver(UMFPACKFactorization()), - GMRESIteration(UMFPACKFactorization()), - GMRESIteration(UMFPACKFactorization(), EquationBlock()), - GMRESIteration(AMGCL_AMGPreconditioner(),EquationBlock()), - BICGstabIteration(AMGCL_AMGPreconditioner(),EquationBlock()), - GMRESIteration(ILUZeroPreconditioner()), - # GMRESIteration(ILUZeroPreconditioner(), EquationBlock()), - # GMRESIteration(ILUZeroPreconditioner(), PointBlock()) + strategies = [UMFPACKFactorization(), + KrylovJL_GMRES(precs=LinearSolvePreconBuilder(UMFPACKFactorization())), + KrylovJL_GMRES(precs=BlockPreconBuilder(precs=LinearSolvePreconBuilder(UMFPACKFactorization()))), + KrylovJL_GMRES(precs=BlockPreconBuilder(precs=AMGPreconBuilder())), + KrylovJL_BICGSTAB(precs=BlockPreconBuilder(precs=AMGPreconBuilder())), + KrylovJL_GMRES(precs=ILUZeroPreconBuilder()), + KrylovJL_GMRES(precs=BlockPreconBuilder(precs=ILUZeroPreconBuilder())), + KrylovJL_GMRES(precs=ILUZeroPreconBuilder(blocksize=5)), ] val1D = 4.788926530387466 diff --git a/src/VoronoiFVM.jl b/src/VoronoiFVM.jl index 4070ed558..83f4cf924 100644 --- a/src/VoronoiFVM.jl +++ b/src/VoronoiFVM.jl @@ -40,7 +40,7 @@ using JLD2: JLD2, jldopen using LinearAlgebra: LinearAlgebra, Diagonal, I, Tridiagonal, isdiag, ldiv!, norm using LinearSolve: LinearSolve, KrylovJL_BICGSTAB, KrylovJL_CG, KrylovJL_GMRES, LinearProblem, - SparspakFactorization, UMFPACKFactorization, init + SparspakFactorization, UMFPACKFactorization, init, reinit! using Printf: Printf, @printf, @sprintf using Random: Random, AbstractRNG using RecursiveArrayTools: RecursiveArrayTools, AbstractDiffEqArray @@ -146,6 +146,7 @@ export calc_divergences include("vfvm_solvercontrol.jl") export fixed_timesteps!, NewtonControl, SolverControl +include("vfvm_linsolve_deprecated.jl") include("vfvm_linsolve.jl") export DirectSolver, GMRESIteration, CGIteration, BICGstabIteration, NoBlock, EquationBlock, PointBlock diff --git a/src/vfvm_linsolve.jl b/src/vfvm_linsolve.jl index 3c19852d2..76636960e 100644 --- a/src/vfvm_linsolve.jl +++ b/src/vfvm_linsolve.jl @@ -1,200 +1,3 @@ -abstract type AbstractStrategy end - -""" - VoronoiFVM.LinearSolverStrategy - -An linear solver strategy provides the possibility to construct [`SolverControl`](@ref) 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`](@ref) strategy -calculates preconditioners or LU factorization separately for each species equation and combines them -to a block Jacobi preconditioner. The [`PointBlock`](@ref) 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: -- [`DirectSolver`](@ref) -- [`CGIteration`](@ref) -- [`BICGstabIteration`](@ref) -- [`GMRESIteration`](@ref) - -Notable LU Factorizations/direct solvers are: -- [`UMFPACKFactorization`](https://docs.sciml.ai/LinearSolve/stable/solvers/solvers/#SuiteSparse.jl) (`using LinearSolve`) -- [`KLUFactorization`](https://docs.sciml.ai/LinearSolve/stable/solvers/solvers/#SuiteSparse.jl) (`using LinearSolve`) -- [`SparspakFactorization`](https://docs.sciml.ai/LinearSolve/stable/solvers/solvers/#Sparspak.jl) (`using LinearSolve`), [`SparspakLU`](https://WIAS-PDELib.github.io/ExtendableSparse.jl/stable/iter/#ExtendableSparse.SparspakLU) (`using ExtendableSparse,Sparspak`) -- [`MKLPardisoLU`](https://WIAS-PDELib.github.io/ExtendableSparse.jl/stable/iter/#ExtendableSparse.MKLPardisoLU) (`using ExtendableSparse, Pardiso`), openmp parallel -- [`AMGSolver`](https://j-fu.github.io/AMGCLWrap.jl/stable/solvers/#AMGCLWrap.AMGSolver) (`using AMGCLWrap`), openmp parallel -- [`RLXSolver`](https://j-fu.github.io/AMGCLWrap.jl/stable/solvers/#AMGCLWrap.RLXSolver) (`using AMGCLWrap`), openmp parallel - - -Notable incomplete factorizations/preconditioners -- Incomplete LU factorizations written in Julia: - - [`ILUZeroPreconditioner`](https://WIAS-PDELib.github.io/ExtendableSparse.jl/stable/iter/#ExtendableSparse.ILUZeroPreconditioner) - - [`ILUTPrecondidtioner`](https://WIAS-PDELib.github.io/ExtendableSparse.jl/stable/iter/#ExtendableSparse.ILUTPreconditioner) (`using ExtendableSparse, IncompleteLU`) -- Algebraic multigrid written in Julia: (`using ExtendableSparse, AlgebraicMultigrid`) - - [`RS_AMGPreconditioner`](https://WIAS-PDELib.github.io/ExtendableSparse.jl/stable/iter/#ExtendableSparse.RS_AMGPreconditioner) - - [`SA_AMGPreconditioner`](https://WIAS-PDELib.github.io/ExtendableSparse.jl/stable/iter/#ExtendableSparse.SA_AMGPreconditioner) -- AMGCL based preconditioners (`using ExtendableSparse, AMGCLWrap`), openmp parallel - - [`AMGCL_AMGPreconditioner`](https://WIAS-PDELib.github.io/ExtendableSparse.jl/stable/iter/#ExtendableSparse.AMGCL_AMGPreconditioner) - - [`AMGCL_RLXPreconditioner`](https://WIAS-PDELib.github.io/ExtendableSparse.jl/stable/iter/#ExtendableSparse.AMGCL_RLXPreconditioner) - -Blocking strategies are: -- [`NoBlock`](@ref) -- [`EquationBlock`](@ref) -- [`PointBlock`](@ref) - -""" -abstract type LinearSolverStrategy <: AbstractStrategy end - -""" - VoronoiFVM.BlockStrategy - -Abstract supertype for various block preconditioning strategies. -""" -abstract type BlockStrategy <: AbstractStrategy end - -""" - NoBlock() - -No blocking. -""" -struct NoBlock <: BlockStrategy end - -""" - EquationBlock() - -Equation-wise blocking. Can be combined with any preconditioner resp. factorization including direct solvers. -""" -struct EquationBlock <: BlockStrategy end - -""" - PointBlock() - -Point-wise blocking. Currently only together with ILUZeroFactorization. -This requires a system with `unknown_storage=:dense`. -""" -struct PointBlock <: BlockStrategy end - -""" - DirectSolver(;factorization=UMFPACKFactorization()) - -LU Factorization solver. -""" -Base.@kwdef struct DirectSolver <: LinearSolverStrategy - factorization::FactorizationStrategy = UMFPACKFactorization() - blocking::NoBlock = NoBlock() # prevent ambiguity in constructor definition -end - -DirectSolver(factorization::FactorizationStrategy; kwargs...) = DirectSolver(; factorization, kwargs...) - -function VoronoiFVM.SolverControl(strat::DirectSolver, sys; kwargs...) - SolverControl(; method_linear = strat.factorization, kwargs...) -end - -""" - GMRESIteration(;factorization=ILUZeroFactorization(), memory=20, restart=true) - GMRESIteration(factorization; memory=20, restart=true) - -GMRES Iteration from Krylov.jl via LinearSolve.jl. -""" -Base.@kwdef struct GMRESIteration <: LinearSolverStrategy - memory::Int = 20 - restart::Bool = true - factorization::FactorizationStrategy = UMFPACKFactorization() - blocking::BlockStrategy = NoBlock() -end - -function GMRESIteration(factorization::FactorizationStrategy, blocking = NoBlock(); kwargs...) - GMRESIteration(; factorization, blocking, kwargs...) -end - -function VoronoiFVM.SolverControl(strat::GMRESIteration, sys; kwargs...) - SolverControl(; - method_linear = KrylovJL_GMRES(; gmres_restart = strat.memory, - restart = strat.restart), - precon_linear = factorizationstrategy(strat.factorization, strat.blocking, sys), - kwargs...,) -end - -""" - CGIteration(;factorization=UMFPACKFactorization()) - CGIteration(factorization) - -CG Iteration from Krylov.jl via LinearSolve.jl. -""" -Base.@kwdef struct CGIteration <: LinearSolverStrategy - factorization::FactorizationStrategy = UMFPACKFactorization() - blocking::BlockStrategy = NoBlock() -end - -function CGIteration(factorization::FactorizationStrategy, blocking = NoBlock(); kwargs...) - CGIteration(; factorization, blocking, kwargs...) -end - -function VoronoiFVM.SolverControl(strat::CGIteration, sys; kwargs...) - SolverControl(; - method_linear = KrylovJL_CG(), - precon_linear = factorizationstrategy(strat.factorization, strat.blocking, sys), - kwargs...,) -end - -""" - BICGstabIteration(;factorization=UMFPACKFactorization()) - BICGstabIteration(factorization) - -BICGstab Iteration from Krylov.jl via LinearSolve.jl. -""" -Base.@kwdef struct BICGstabIteration <: LinearSolverStrategy - factorization::FactorizationStrategy = UMFPACKFactorization() - blocking::BlockStrategy = NoBlock() -end - -function BICGstabIteration(factorization::FactorizationStrategy, blocking = NoBlock(); kwargs...) - BICGstabIteration(; factorization, blocking, kwargs...) -end - -function VoronoiFVM.SolverControl(strat::BICGstabIteration, sys; kwargs...) - SolverControl(; - method_linear = KrylovJL_BICGSTAB(), - precon_linear = factorizationstrategy(strat.factorization, strat.blocking, sys), - kwargs...,) -end - -""" - factorizationstrategy(preconditioner, blockstratrgy, system) - -Create a factorizations strategy from preconditioner and block information -""" -factorizationstrategy(p::FactorizationStrategy, ::NoBlock, sys) = p - -function factorizationstrategy(strat::FactorizationStrategy, ::EquationBlock, sys) - BlockPreconditioner(; - partitioning = partitioning(sys, Equationwise()), - factorization = factorizationstrategy(strat, NoBlock(), sys),) -end - -function factorizationstrategy(::ExtendableSparse.ILUZeroPreconditioner, ::PointBlock, sys) - !isdensesystem(sys) ? - error("Point block preconditioner needs dense system") : nothing - PointBlockILUZeroPreconditioner(; blocksize = num_species(sys)) -end - -VoronoiFVM.SolverControl(::AbstractStrategy, sys; kwargs...) = SolverControl(; kwargs...) -VoronoiFVM.SolverControl(::Nothing, sys; kwargs...) = SolverControl(; kwargs...) ################################################################ # These are needed to enable iterative solvers to work with dual numbers @@ -205,51 +8,45 @@ function Random.rand(rng::AbstractRNG, ForwardDiff.Dual{T, V, N}(rand(rng, V)) end -""" - Make preconditioner constructors from methods -""" - -function (method::LinearSolve.AbstractFactorization)(A) - pr = LinearProblem(A, zeros(eltype(A), size(A, 1))) - init(pr, method) -end - -function (method::LinearSolve.SciMLLinearSolveAlgorithm)(A) - pr = LinearProblem(SparseMatrixCSC(A), zeros(eltype(A), size(A, 1))) - init(pr, method) -end - -function (f::ExtendableSparse.AbstractFactorization)(A) - factorize!(f, A) -end - -function LinearAlgebra.ldiv!(u, cache::LinearSolve.LinearCache, b) - cache.b = b - sol = solve!(cache) - copyto!(u, sol.u) -end - +# TODO: these may be not anymore neded canonical_matrix(A) = A canonical_matrix(A::AbstractExtendableSparseMatrixCSC) = SparseMatrixCSC(A) function _solve_linear!(u, state, nlhistory, control, method_linear, A, b) if isnothing(state.linear_cache) - Pl = control.precon_linear(canonical_matrix(A)) + if !isa(method_linear, LinearSolve.SciMLLinearSolveAlgorithm) + @warn "use of $(typeof(method_linear)) is deprecated, use an algorithm from LinearSolve" + end + if hasproperty(method_linear, :precs) && !isnothing(method_linear.precs) + Pl=nothing + else + Pl = control.precon_linear(canonical_matrix(A)) + if !isa(Pl, Identity) && isa(method_linear, LinearSolve.AbstractKrylovSubspaceMethod) + @warn "Use of control.precon_linear is deprecated. Use the `precs` API of LinearSolve" + end + end nlhistory.nlu += 1 p = LinearProblem(canonical_matrix(A), b) state.linear_cache = init(p, - method_linear; - abstol = control.abstol_linear, - reltol = control.reltol_linear, - maxiters = control.maxiters_linear, - verbose = doprint(control, 'l'), - Pl,) + method_linear; + abstol = control.abstol_linear, + reltol = control.reltol_linear, + maxiters = control.maxiters_linear, + verbose = doprint(control, 'l'), + Pl,) else - state.linear_cache.A = canonical_matrix(A) - state.linear_cache.b = b - if control.keepcurrent_linear - nlhistory.nlu += 1 - state.linear_cache.Pl = control.precon_linear(canonical_matrix(A)) + if hasproperty(method_linear, :precs) && !isnothing(method_linear.precs) + reinit!(state.linear_cache; A=canonical_matrix(A), b, reuse_precs=!control.keepcurrent_linear) + if control.keepcurrent_linear + nlhistory.nlu += 1 + end + else + state.linear_cache.A = canonical_matrix(A) + state.linear_cache.b = b + if control.keepcurrent_linear + nlhistory.nlu += 1 + state.linear_cache.Pl = control.precon_linear(canonical_matrix(A)) + end end end diff --git a/src/vfvm_linsolve_deprecated.jl b/src/vfvm_linsolve_deprecated.jl new file mode 100644 index 000000000..01364eaad --- /dev/null +++ b/src/vfvm_linsolve_deprecated.jl @@ -0,0 +1,226 @@ +################################################ +# deprecated "homegrown API" to work around the need to specify preconditioners +# instead of preconditioner constructors to LinearSolve.jl pre 2.36. +# TODO: remove with v3.0 + +abstract type AbstractStrategy end + +""" + VoronoiFVM.LinearSolverStrategy + +An linear solver strategy provides the possibility to construct [`SolverControl`](@ref) 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`](@ref) strategy +calculates preconditioners or LU factorization separately for each species equation and combines them +to a block Jacobi preconditioner. The [`PointBlock`](@ref) 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: +- [`DirectSolver`](@ref) +- [`CGIteration`](@ref) +- [`BICGstabIteration`](@ref) +- [`GMRESIteration`](@ref) + +Notable LU Factorizations/direct solvers are: +- [`UMFPACKFactorization`](https://docs.sciml.ai/LinearSolve/stable/solvers/solvers/#SuiteSparse.jl) (`using LinearSolve`) +- [`KLUFactorization`](https://docs.sciml.ai/LinearSolve/stable/solvers/solvers/#SuiteSparse.jl) (`using LinearSolve`) +- [`SparspakFactorization`](https://docs.sciml.ai/LinearSolve/stable/solvers/solvers/#Sparspak.jl) (`using LinearSolve`), [`SparspakLU`](https://WIAS-PDELib.github.io/ExtendableSparse.jl/stable/iter/#ExtendableSparse.SparspakLU) (`using ExtendableSparse,Sparspak`) +- [`MKLPardisoLU`](https://WIAS-PDELib.github.io/ExtendableSparse.jl/stable/iter/#ExtendableSparse.MKLPardisoLU) (`using ExtendableSparse, Pardiso`), openmp parallel +- [`AMGSolver`](https://j-fu.github.io/AMGCLWrap.jl/stable/solvers/#AMGCLWrap.AMGSolver) (`using AMGCLWrap`), openmp parallel +- [`RLXSolver`](https://j-fu.github.io/AMGCLWrap.jl/stable/solvers/#AMGCLWrap.RLXSolver) (`using AMGCLWrap`), openmp parallel + + +Notable incomplete factorizations/preconditioners +- Incomplete LU factorizations written in Julia: + - [`ILUZeroPreconditioner`](https://WIAS-PDELib.github.io/ExtendableSparse.jl/stable/iter/#ExtendableSparse.ILUZeroPreconditioner) + - [`ILUTPrecondidtioner`](https://WIAS-PDELib.github.io/ExtendableSparse.jl/stable/iter/#ExtendableSparse.ILUTPreconditioner) (`using ExtendableSparse, IncompleteLU`) +- Algebraic multigrid written in Julia: (`using ExtendableSparse, AlgebraicMultigrid`) + - [`RS_AMGPreconditioner`](https://WIAS-PDELib.github.io/ExtendableSparse.jl/stable/iter/#ExtendableSparse.RS_AMGPreconditioner) + - [`SA_AMGPreconditioner`](https://WIAS-PDELib.github.io/ExtendableSparse.jl/stable/iter/#ExtendableSparse.SA_AMGPreconditioner) +- AMGCL based preconditioners (`using ExtendableSparse, AMGCLWrap`), openmp parallel + - [`AMGCL_AMGPreconditioner`](https://WIAS-PDELib.github.io/ExtendableSparse.jl/stable/iter/#ExtendableSparse.AMGCL_AMGPreconditioner) + - [`AMGCL_RLXPreconditioner`](https://WIAS-PDELib.github.io/ExtendableSparse.jl/stable/iter/#ExtendableSparse.AMGCL_RLXPreconditioner) + +Blocking strategies are: +- [`NoBlock`](@ref) +- [`EquationBlock`](@ref) +- [`PointBlock`](@ref) + +""" +abstract type LinearSolverStrategy <: AbstractStrategy end + +""" + VoronoiFVM.BlockStrategy + +Abstract supertype for various block preconditioning strategies. +""" +abstract type BlockStrategy <: AbstractStrategy end + +""" + NoBlock() + +No blocking. +""" +struct NoBlock <: BlockStrategy end + +""" + EquationBlock() + +Equation-wise blocking. Can be combined with any preconditioner resp. factorization including direct solvers. +""" +struct EquationBlock <: BlockStrategy end + +""" + PointBlock() + +Point-wise blocking. Currently only together with ILUZeroFactorization. +This requires a system with `unknown_storage=:dense`. +""" +struct PointBlock <: BlockStrategy end + +""" + DirectSolver(;factorization=UMFPACKFactorization()) + +LU Factorization solver. +""" +Base.@kwdef struct DirectSolver <: LinearSolverStrategy + factorization::FactorizationStrategy = UMFPACKFactorization() + blocking::NoBlock = NoBlock() # prevent ambiguity in constructor definition +end + +DirectSolver(factorization::FactorizationStrategy; kwargs...) = DirectSolver(; factorization, kwargs...) + +function VoronoiFVM.SolverControl(strat::DirectSolver, sys; kwargs...) + SolverControl(; method_linear = strat.factorization, kwargs...) +end + +""" + GMRESIteration(;factorization=ILUZeroFactorization(), memory=20, restart=true) + GMRESIteration(factorization; memory=20, restart=true) + +GMRES Iteration from Krylov.jl via LinearSolve.jl. +""" +Base.@kwdef struct GMRESIteration <: LinearSolverStrategy + memory::Int = 20 + restart::Bool = true + factorization::FactorizationStrategy = UMFPACKFactorization() + blocking::BlockStrategy = NoBlock() +end + +function GMRESIteration(factorization::FactorizationStrategy, blocking = NoBlock(); kwargs...) + GMRESIteration(; factorization, blocking, kwargs...) +end + +function VoronoiFVM.SolverControl(strat::GMRESIteration, sys; kwargs...) + SolverControl(; + method_linear = KrylovJL_GMRES(; gmres_restart = strat.memory, + restart = strat.restart), + precon_linear = factorizationstrategy(strat.factorization, strat.blocking, sys), + kwargs...,) +end + +""" + CGIteration(;factorization=UMFPACKFactorization()) + CGIteration(factorization) + +CG Iteration from Krylov.jl via LinearSolve.jl. +""" +Base.@kwdef struct CGIteration <: LinearSolverStrategy + factorization::FactorizationStrategy = UMFPACKFactorization() + blocking::BlockStrategy = NoBlock() +end + +function CGIteration(factorization::FactorizationStrategy, blocking = NoBlock(); kwargs...) + CGIteration(; factorization, blocking, kwargs...) +end + +function VoronoiFVM.SolverControl(strat::CGIteration, sys; kwargs...) + SolverControl(; + method_linear = KrylovJL_CG(), + precon_linear = factorizationstrategy(strat.factorization, strat.blocking, sys), + kwargs...,) +end + +""" + BICGstabIteration(;factorization=UMFPACKFactorization()) + BICGstabIteration(factorization) + +BICGstab Iteration from Krylov.jl via LinearSolve.jl. +""" +Base.@kwdef struct BICGstabIteration <: LinearSolverStrategy + factorization::FactorizationStrategy = UMFPACKFactorization() + blocking::BlockStrategy = NoBlock() +end + +function BICGstabIteration(factorization::FactorizationStrategy, blocking = NoBlock(); kwargs...) + BICGstabIteration(; factorization, blocking, kwargs...) +end + +function VoronoiFVM.SolverControl(strat::BICGstabIteration, sys; kwargs...) + SolverControl(; + method_linear = KrylovJL_BICGSTAB(), + precon_linear = factorizationstrategy(strat.factorization, strat.blocking, sys), + kwargs...,) +end + +""" + factorizationstrategy(preconditioner, blockstratrgy, system) + +Create a factorizations strategy from preconditioner and block information +""" +factorizationstrategy(p::FactorizationStrategy, ::NoBlock, sys) = p + +function factorizationstrategy(strat::FactorizationStrategy, ::EquationBlock, sys) + BlockPreconditioner(; + partitioning = partitioning(sys, Equationwise()), + factorization = factorizationstrategy(strat, NoBlock(), sys),) +end + +function factorizationstrategy(::ExtendableSparse.ILUZeroPreconditioner, ::PointBlock, sys) + !isdensesystem(sys) ? + error("Point block preconditioner needs dense system") : nothing + PointBlockILUZeroPreconditioner(; blocksize = num_species(sys)) +end + +VoronoiFVM.SolverControl(::AbstractStrategy, sys; kwargs...) = SolverControl(; kwargs...) +VoronoiFVM.SolverControl(::Nothing, sys; kwargs...) = SolverControl(; kwargs...) + +""" + Make preconditioner constructors from methods +""" + +function (method::LinearSolve.AbstractFactorization)(A) + pr = LinearProblem(A, zeros(eltype(A), size(A, 1))) + init(pr, method) +end + +function (method::LinearSolve.SciMLLinearSolveAlgorithm)(A) + pr = LinearProblem(SparseMatrixCSC(A), zeros(eltype(A), size(A, 1))) + init(pr, method) +end + +function (f::ExtendableSparse.AbstractFactorization)(A) + factorize!(f, A) +end + +function LinearAlgebra.ldiv!(u, cache::LinearSolve.LinearCache, b) + cache.b = b + sol = solve!(cache) + copyto!(u, sol.u) +end diff --git a/src/vfvm_solvercontrol.jl b/src/vfvm_solvercontrol.jl index ae8979328..efe51761c 100644 --- a/src/vfvm_solvercontrol.jl +++ b/src/vfvm_solvercontrol.jl @@ -1,7 +1,9 @@ -################################################ + +# Deprecated. TODO: remove with v3.0 const FactorizationStrategy = Union{Nothing, Function, Type, ExtendableSparse.AbstractFactorization, LinearSolve.AbstractFactorization, LinearSolve.SciMLLinearSolveAlgorithm} +## Deprecated. TODO: remove with v3.0 struct Identity end Identity(A) = Identity() LinearAlgebra.ldiv!(u, I::Identity, v) = u .= v @@ -10,7 +12,6 @@ LinearAlgebra.ldiv!(I::Identity, u) = nothing """ SolverControl SolverControl(;kwargs...) - SolverControl(linear_solver_strategy, sys; kwargs...) Solver control parameter for time stepping, embedding, Newton method and linear solver control. All field names can be used as keyword arguments for [`solve(system::VoronoiFVM.AbstractSystem; kwargs...)`](@ref) @@ -18,8 +19,6 @@ All field names can be used as keyword arguments for [`solve(system::VoronoiFVM. Newton's method solves ``F(u)=0`` by the iterative procedure ``u_{i+1}=u_{i} - d_i F'(u_i)^{-1}F(u_i)`` starting with some initial value ``u_0``, where ``d_i`` is a damping parameter. -For linear solver strategies, see [`VoronoiFVM.LinearSolverStrategy`](@ref). - $(TYPEDFIELDS) """ Base.@kwdef mutable struct SolverControl @@ -139,6 +138,7 @@ Base.@kwdef mutable struct SolverControl """ Update preconditioner in each Newton step ? + Translates to `reuse_precs=!keepcurrent_linear` for LinearSolve. """ keepcurrent_linear::Bool = false diff --git a/src/vfvm_system.jl b/src/vfvm_system.jl index d8ebe3470..7e3e71600 100644 --- a/src/vfvm_system.jl +++ b/src/vfvm_system.jl @@ -1070,9 +1070,11 @@ Equationwise partitioning mode. struct Equationwise end """ - $(SIGNATURES) + partitioning(system, mode) -Calculate partitioning of system unknowns. +Calculate partitioning of system unknowns to be used in block preconditioners. +Possible modes: +- [`Equationwise`](@ref)() """ function partitioning(system::DenseSystem, ::Equationwise) len = length(system.node_dof)