diff --git a/.github/workflows/Format-check.yml b/.github/workflows/Format-check.yml index 71c8bd06..cc9ac9f5 100644 --- a/.github/workflows/Format-check.yml +++ b/.github/workflows/Format-check.yml @@ -25,7 +25,7 @@ jobs: - name: Install JuliaFormatter and format run: | julia -e 'using Pkg; Pkg.add(PackageSpec(name = "JuliaFormatter", version="1.0.45"))' - julia -e 'using JuliaFormatter; format(["src", "test", "examples"], verbose = true)' + julia -e 'using JuliaFormatter; format(["src", "test", "examples", "docs"], verbose = true)' - name: Format check run: | julia -e ' diff --git a/Project.toml b/Project.toml index 3a06c452..1a80c008 100644 --- a/Project.toml +++ b/Project.toml @@ -18,6 +18,7 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" SummationByPartsOperators = "9f78cca6-572e-554e-b819-917d2f1cf240" TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" +TrixiBase = "9a0f1c46-06d5-4909-a5a3-ce25d3fa3284" [compat] DiffEqBase = "6.128" @@ -34,4 +35,5 @@ SparseArrays = "1" StaticArrays = "1" SummationByPartsOperators = "0.5.41" TimerOutputs = "0.5.7" +TrixiBase = "0.1.1" julia = "1.9" diff --git a/docs/Project.toml b/docs/Project.toml index b7023255..a7b1e702 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -3,9 +3,11 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" SummationByPartsOperators = "9f78cca6-572e-554e-b819-917d2f1cf240" +TrixiBase = "9a0f1c46-06d5-4909-a5a3-ce25d3fa3284" [compat] Documenter = "1" OrdinaryDiffEq = "6.49.1" Plots = "1.9" SummationByPartsOperators = "0.5.41" +TrixiBase = "0.1.1" diff --git a/docs/make.jl b/docs/make.jl index fa2b03c9..282eb04b 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,12 +1,14 @@ -using DispersiveShallowWater using Documenter +using DispersiveShallowWater +using TrixiBase # Define module-wide setups such that the respective modules are available in doctests DocMeta.setdocmeta!(DispersiveShallowWater, :DocTestSetup, :(using DispersiveShallowWater); recursive = true) +DocMeta.setdocmeta!(TrixiBase, :DocTestSetup, :(using TrixiBase); recursive = true) makedocs(; - modules = [DispersiveShallowWater], + modules = [DispersiveShallowWater, TrixiBase], authors = "Joshua Lampert ", repo = Remotes.GitHub("JoshuaLampert", "DispersiveShallowWater.jl"), sitename = "DispersiveShallowWater.jl", @@ -18,7 +20,10 @@ makedocs(; pages = [ "Home" => "index.md", "Overview" => "overview.md", - "Reference" => "ref.md", + "Reference" => [ + "TrixiBase" => "ref-trixibase.md", + "DispersiveShallowWater" => "ref.md", + ], "License" => "license.md", ]) diff --git a/docs/src/ref-trixibase.md b/docs/src/ref-trixibase.md new file mode 100644 index 00000000..c7a970f8 --- /dev/null +++ b/docs/src/ref-trixibase.md @@ -0,0 +1,9 @@ +# TrixiBase.jl API + +```@meta +CurrentModule = TrixiBase +``` + +```@autodocs +Modules = [TrixiBase] +``` diff --git a/src/DispersiveShallowWater.jl b/src/DispersiveShallowWater.jl index 1cd8e0cd..cff382c0 100644 --- a/src/DispersiveShallowWater.jl +++ b/src/DispersiveShallowWater.jl @@ -22,6 +22,7 @@ using SummationByPartsOperators: AbstractDerivativeOperator, derivative_order, integrate import SummationByPartsOperators: grid, xmin, xmax using TimerOutputs: TimerOutputs, TimerOutput, @timeit, print_timer, reset_timer! +@reexport using TrixiBase: TrixiBase, trixi_include include("boundary_conditions.jl") include("mesh.jl") @@ -32,7 +33,7 @@ include("callbacks_step/callbacks_step.jl") include("visualization.jl") include("util.jl") -export examples_dir, get_examples, default_example, trixi_include, convergence_test +export examples_dir, get_examples, default_example, convergence_test export BBMBBMEquations1D, BBMBBMVariableEquations1D, SvärdKalischEquations1D, SvaerdKalischEquations1D diff --git a/src/util.jl b/src/util.jl index 46014d53..52adb42a 100644 --- a/src/util.jl +++ b/src/util.jl @@ -48,53 +48,6 @@ function default_example() "bbm_bbm_variable_bathymetry_1d_basic.jl") end -# Note: We can't call the method below `DispersiveShallowWater.include` since that is created automatically -# inside `module DispersiveShallowWater` to `include` source files and evaluate them within the global scope -# of `DispersiveShallowWater`. However, users will want to evaluate in the global scope of `Main` or something -# similar to manage dependencies on their own. -""" - trixi_include([mod::Module=Main,] example::AbstractString; kwargs...) - -`include` the file `example` and evaluate its content in the global scope of module `mod`. -You can override specific assignments in `example` by supplying keyword arguments. -It's basic purpose is to make it easier to modify some parameters while running DispersiveShallowWater from the -REPL. Additionally, this is used in tests to reduce the computational burden for CI while still -providing examples with sensible default values for users. - -Before replacing assignments in `example`, the keyword argument `maxiters` is inserted -into calls to `solve` and `DispersiveShallowWater.solve` with it's default value used in the SciML ecosystem -for ODEs, see the "Miscellaneous" section of the -[documentation](https://docs.sciml.ai/DiffEqDocs/stable/basics/common_solver_opts/). - -Copied from [Trixi.jl](https://github.com/trixi-framework/Trixi.jl). - -# Examples - -```jldoctest -julia> redirect_stdout(devnull) do - trixi_include(@__MODULE__, joinpath(examples_dir(), "bbm_bbm_1d", "bbm_bbm_1d_basic.jl"), - tspan=(0.0, 0.1)) - sol.t[end] - end -0.1 -``` -""" -function trixi_include(mod::Module, example::AbstractString; kwargs...) - # Check that all kwargs exist as assignments - code = read(example, String) - expr = Meta.parse("begin \n$code \nend") - expr = insert_maxiters(expr) - - for (key, val) in kwargs - # This will throw an error when `key` is not found - find_assignment(expr, key) - end - - Base.include(ex -> replace_assignments(insert_maxiters(ex); kwargs...), mod, example) -end - -trixi_include(example::AbstractString; kwargs...) = trixi_include(Main, example; kwargs...) - """ convergence_test([mod::Module=Main,] example::AbstractString, iterations; kwargs...) @@ -214,90 +167,6 @@ function convergence_test(example::AbstractString, iterations; kwargs...) convergence_test(Main, example::AbstractString, iterations; kwargs...) end -# Helper methods used in the functions defined above, also copied from Trixi.jl - -# Apply the function `f` to `expr` and all sub-expressions recursively. -walkexpr(f, expr::Expr) = f(Expr(expr.head, (walkexpr(f, arg) for arg in expr.args)...)) -walkexpr(f, x) = f(x) - -# Insert the keyword argument `maxiters` into calls to `solve` and `DispersiveShallowWater.solve` -# with default value `10^5` if it is not already present. -function insert_maxiters(expr) - maxiters_default = 10^5 - - expr = walkexpr(expr) do x - if x isa Expr - is_plain_solve = x.head === Symbol("call") && x.args[1] === Symbol("solve") - is_trixi_solve = (x.head === Symbol("call") && x.args[1] isa Expr && - x.args[1].head === Symbol(".") && - x.args[1].args[1] === Symbol("DispersiveShallowWater") && - x.args[1].args[2] isa QuoteNode && - x.args[1].args[2].value === Symbol("solve")) - - if is_plain_solve || is_trixi_solve - # Do nothing if `maxiters` is already set as keyword argument... - for arg in x.args - if arg isa Expr && arg.head === Symbol("kw") && - arg.args[1] === Symbol("maxiters") - return x - end - end - - # ...and insert it otherwise. - push!(x.args, Expr(Symbol("kw"), Symbol("maxiters"), maxiters_default)) - end - end - return x - end - - return expr -end - -# Replace assignments to `key` in `expr` by `key = val` for all `(key,val)` in `kwargs`. -function replace_assignments(expr; kwargs...) - # replace explicit and keyword assignments - expr = walkexpr(expr) do x - if x isa Expr - for (key, val) in kwargs - if (x.head === Symbol("=") || x.head === :kw) && x.args[1] === Symbol(key) - x.args[2] = :($val) - # dump(x) - end - end - end - return x - end - - return expr -end - -# find a (keyword or common) assignment to `destination` in `expr` -# and return the assigned value -function find_assignment(expr, destination) - # declare result to be able to assign to it in the closure - local result - found = false - - # find explicit and keyword assignments - walkexpr(expr) do x - if x isa Expr - if (x.head === Symbol("=") || x.head === :kw) && - x.args[1] === Symbol(destination) - result = x.args[2] - found = true - # dump(x) - end - end - return x - end - - if !found - throw(ArgumentError("assignment `$destination` not found in expression")) - end - - result -end - function extract_initial_N(example, kwargs) code = read(example, String) expr = Meta.parse("begin \n$code \nend") @@ -306,7 +175,7 @@ function extract_initial_N(example, kwargs) return kwargs[:N] else # get N from the example - N = find_assignment(expr, :N) + N = TrixiBase.find_assignment(expr, :N) return N end end diff --git a/test/test_unit.jl b/test/test_unit.jl index d00a7228..17130564 100644 --- a/test/test_unit.jl +++ b/test/test_unit.jl @@ -222,7 +222,6 @@ using SparseArrays: sparse, SparseMatrixCSC @testset "util" begin @test_nowarn get_examples() - @test_nowarn trixi_include(default_example(), tspan = (0.0, 0.1)) accuracy_orders = [2, 4, 6] for accuracy_order in accuracy_orders diff --git a/test/test_util.jl b/test/test_util.jl index 81027d90..fc40501a 100644 --- a/test/test_util.jl +++ b/test/test_util.jl @@ -52,7 +52,7 @@ macro test_trixi_include(example, args...) println($example) # evaluate examples in the scope of the module they're called from - @test_nowarn trixi_include(@__MODULE__, $example; $kwargs...) + @test_nowarn_mod trixi_include(@__MODULE__, $example; $kwargs...) # if present, compare l2, linf and conservation errors against reference values if !isnothing($l2) || !isnothing($linf) || !isnothing($cons_error) @@ -189,3 +189,45 @@ macro trixi_testset(name, expr) nothing end end + +# Copied from TrixiBase.jl. See https://github.com/trixi-framework/TrixiBase.jl/issues/9. +""" + @test_nowarn_mod expr +Modified version of `@test_nowarn expr` that prints the content of `stderr` when +it is not empty and ignores some common info statements printed in DispersiveShallowWater.jl +uses. +""" +macro test_nowarn_mod(expr, additional_ignore_content = String[]) + quote + let fname = tempname() + try + ret = open(fname, "w") do f + redirect_stderr(f) do + $(esc(expr)) + end + end + stderr_content = read(fname, String) + if !isempty(stderr_content) + println("Content of `stderr`:\n", stderr_content) + end + + # Patterns matching the following ones will be ignored. Additional patterns + # passed as arguments can also be regular expressions, so we just use the + # type `Any` for `ignore_content`. + ignore_content = Any["[ Info: You just called `trixi_include`. Julia may now compile the code, please be patient.\n"] + append!(ignore_content, $additional_ignore_content) + for pattern in ignore_content + stderr_content = replace(stderr_content, pattern => "") + end + + # We also ignore simple module redefinitions for convenience. Thus, we + # check whether every line of `stderr_content` is of the form of a + # module replacement warning. + @test occursin(r"^(WARNING: replacing module .+\.\n)*$", stderr_content) + ret + finally + rm(fname, force = true) + end + end + end +end