From 4369c1c00b3e44de43bba95adeedfb37fd09551c Mon Sep 17 00:00:00 2001 From: Hendrik Ranocha Date: Wed, 14 Feb 2024 07:56:08 +0100 Subject: [PATCH 1/5] Fix formatter to older version (#1843) * Fix formatter to older version * fix JuliaFormatter version also in utils folder * Update utils/trixi-format-file.jl Co-authored-by: Arpit Babbar * Update utils/trixi-format.jl Co-authored-by: Arpit Babbar * Apply suggestions from code review Co-authored-by: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com> * format --------- Co-authored-by: Arpit Babbar Co-authored-by: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com> --- .github/workflows/FormatCheck.yml | 2 +- utils/trixi-format-file.jl | 3 ++- utils/trixi-format.jl | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/FormatCheck.yml b/.github/workflows/FormatCheck.yml index a733cb7cc21..7297f1c3ff5 100644 --- a/.github/workflows/FormatCheck.yml +++ b/.github/workflows/FormatCheck.yml @@ -29,7 +29,7 @@ jobs: # TODO: Change the call below to # format(".") run: | - julia -e 'using Pkg; Pkg.add(PackageSpec(name = "JuliaFormatter"))' + julia -e 'using Pkg; Pkg.add(PackageSpec(name = "JuliaFormatter", version="1.0.45"))' julia -e 'using JuliaFormatter; format(["benchmark", "examples", "ext", "src", "test", "utils"])' - name: Format check run: | diff --git a/utils/trixi-format-file.jl b/utils/trixi-format-file.jl index c4d8e7c9032..9b9a0e4949c 100755 --- a/utils/trixi-format-file.jl +++ b/utils/trixi-format-file.jl @@ -2,7 +2,8 @@ using Pkg Pkg.activate(; temp = true, io = devnull) -Pkg.add("JuliaFormatter"; preserve = PRESERVE_ALL, io = devnull) +Pkg.add(PackageSpec(name = "JuliaFormatter", version = "1.0.45"); preserve = PRESERVE_ALL, + io = devnull) using JuliaFormatter: format_file diff --git a/utils/trixi-format.jl b/utils/trixi-format.jl index d1e7efa656a..63f14078807 100755 --- a/utils/trixi-format.jl +++ b/utils/trixi-format.jl @@ -2,7 +2,8 @@ using Pkg Pkg.activate(; temp = true, io = devnull) -Pkg.add("JuliaFormatter"; preserve = PRESERVE_ALL, io = devnull) +Pkg.add(PackageSpec(name = "JuliaFormatter", version = "1.0.45"); preserve = PRESERVE_ALL, + io = devnull) using JuliaFormatter: format From 4f33837e3c95b5af21057850c30ef603a9191d86 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Thu, 15 Feb 2024 08:50:30 +0100 Subject: [PATCH 2/5] Use `trixi_include` from TrixiBase.jl (#1832) * Use `trixi_include` from TrixiBase.jl * Add compat entry for TrixiBase.jl * Remove unused functions * Use `TrixiBase.walkexpr` * Fix docs * Add TrixiBase.jl API reference * Add TrixiBase to docs dependencies * Import `TrixiBase` * Remove `find_assignment` * Add TrixiBase to makedocs modules * Apply suggestions from code review Co-authored-by: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com> --------- Co-authored-by: Hendrik Ranocha Co-authored-by: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com> --- Project.toml | 2 + docs/Project.toml | 2 + docs/make.jl | 4 +- docs/src/conventions.md | 10 +- docs/src/reference-trixibase.md | 9 ++ src/Trixi.jl | 3 +- src/auxiliary/precompile.jl | 3 - src/auxiliary/special_elixirs.jl | 158 +------------------------------ src/callbacks_step/trivial.jl | 4 +- test/test_unit.jl | 97 ------------------- 10 files changed, 31 insertions(+), 261 deletions(-) create mode 100644 docs/src/reference-trixibase.md diff --git a/Project.toml b/Project.toml index 9b624c7733c..b4a06a70688 100644 --- a/Project.toml +++ b/Project.toml @@ -45,6 +45,7 @@ TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" Triangulate = "f7e6ffb2-c36d-4f8f-a77e-16e897189344" TriplotBase = "981d1d27-644d-49a2-9326-4793e63143c3" TriplotRecipes = "808ab39a-a642-4abf-81ff-4cb34ebbffa3" +TrixiBase = "9a0f1c46-06d5-4909-a5a3-ce25d3fa3284" [weakdeps] Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" @@ -95,6 +96,7 @@ TimerOutputs = "0.5.7" Triangulate = "2.0" TriplotBase = "0.1" TriplotRecipes = "0.1" +TrixiBase = "0.1.1" julia = "1.8" [extras] diff --git a/docs/Project.toml b/docs/Project.toml index cc48aeb8ed9..3b8d169fdb8 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -10,6 +10,7 @@ OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Trixi2Vtk = "bc1476a1-1ca6-4cc3-950b-c312b255ff95" +TrixiBase = "9a0f1c46-06d5-4909-a5a3-ce25d3fa3284" [compat] CairoMakie = "0.6, 0.7, 0.8, 0.9, 0.10, 0.11" @@ -23,3 +24,4 @@ OrdinaryDiffEq = "6.49.1" Plots = "1.9" Test = "1" Trixi2Vtk = "0.3" +TrixiBase = "0.1.1" diff --git a/docs/make.jl b/docs/make.jl index 584f151b5f3..946b803b71e 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -8,6 +8,7 @@ end using Trixi using Trixi2Vtk +using TrixiBase # Get Trixi.jl root directory trixi_root_dir = dirname(@__DIR__) @@ -82,7 +83,7 @@ tutorials = create_tutorials(files) # Make documentation makedocs( # Specify modules for which docstrings should be shown - modules = [Trixi, Trixi2Vtk], + modules = [Trixi, TrixiBase, Trixi2Vtk], # Set sitename to Trixi.jl sitename = "Trixi.jl", # Provide additional formatting options @@ -128,6 +129,7 @@ makedocs( "Troubleshooting and FAQ" => "troubleshooting.md", "Reference" => [ "Trixi.jl" => "reference-trixi.md", + "TrixiBase.jl" => "reference-trixibase.md", "Trixi2Vtk.jl" => "reference-trixi2vtk.md" ], "Authors" => "authors.md", diff --git a/docs/src/conventions.md b/docs/src/conventions.md index dab1b8533a5..4f9e0ec4e67 100644 --- a/docs/src/conventions.md +++ b/docs/src/conventions.md @@ -47,10 +47,12 @@ Trixi.jl is distributed with several examples in the form of elixirs, small Julia scripts containing everything to set up and run a simulation. Working interactively from the Julia REPL with these scripts can be quite convenient while for exploratory research and development of Trixi.jl. For example, you -can use the convenience function [`trixi_include`](@ref) to `include` an elixir -with some modified arguments. To enable this, it is helpful to use a consistent -naming scheme in elixirs, since [`trixi_include`](@ref) can only perform simple -replacements. Some standard variables names are +can use the convenience function +[`trixi_include`](@ref) +to `include` an elixir with some modified arguments. To enable this, it is +helpful to use a consistent naming scheme in elixirs, since +[`trixi_include`](@ref) +can only perform simple replacements. Some standard variables names are - `polydeg` for the polynomial degree of a solver - `surface_flux` for the numerical flux at surfaces diff --git a/docs/src/reference-trixibase.md b/docs/src/reference-trixibase.md new file mode 100644 index 00000000000..c7a970f88ec --- /dev/null +++ b/docs/src/reference-trixibase.md @@ -0,0 +1,9 @@ +# TrixiBase.jl API + +```@meta +CurrentModule = TrixiBase +``` + +```@autodocs +Modules = [TrixiBase] +``` diff --git a/src/Trixi.jl b/src/Trixi.jl index 8d74fbc9736..ea72fbc915f 100644 --- a/src/Trixi.jl +++ b/src/Trixi.jl @@ -70,6 +70,7 @@ using Triangulate: Triangulate, TriangulateIO, triangulate export TriangulateIO # for type parameter in DGMultiMesh using TriplotBase: TriplotBase using TriplotRecipes: DGTriPseudocolor +@reexport using TrixiBase: TrixiBase, trixi_include @reexport using SimpleUnPack: @unpack using SimpleUnPack: @pack! using DataStructures: BinaryHeap, FasterForward, extract_all! @@ -129,7 +130,7 @@ include("callbacks_step/callbacks_step.jl") include("callbacks_stage/callbacks_stage.jl") include("semidiscretization/semidiscretization_euler_gravity.jl") -# `trixi_include` and special elixirs such as `convergence_test` +# Special elixirs such as `convergence_test` include("auxiliary/special_elixirs.jl") # Plot recipes and conversion functions to visualize results with Plots.jl diff --git a/src/auxiliary/precompile.jl b/src/auxiliary/precompile.jl index 9cec502f6cb..4d5399b5ba3 100644 --- a/src/auxiliary/precompile.jl +++ b/src/auxiliary/precompile.jl @@ -577,9 +577,6 @@ function _precompile_manual_() @assert Base.precompile(Tuple{typeof(show), Base.TTY, lbm_collision_callback_type}) @assert Base.precompile(Tuple{typeof(show), IOContext{Base.TTY}, MIME"text/plain", lbm_collision_callback_type}) - - # infrastructure, special elixirs - @assert Base.precompile(Tuple{typeof(trixi_include), String}) end @assert Base.precompile(Tuple{typeof(init_mpi)}) diff --git a/src/auxiliary/special_elixirs.jl b/src/auxiliary/special_elixirs.jl index 5fdd9aea0c5..d71a27aa96a 100644 --- a/src/auxiliary/special_elixirs.jl +++ b/src/auxiliary/special_elixirs.jl @@ -5,58 +5,6 @@ @muladd begin #! format: noindent -# Note: We can't call the method below `Trixi.include` since that is created automatically -# inside `module Trixi` to `include` source files and evaluate them within the global scope -# of `Trixi`. 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,] elixir::AbstractString; kwargs...) - -`include` the file `elixir` and evaluate its content in the global scope of module `mod`. -You can override specific assignments in `elixir` by supplying keyword arguments. -It's basic purpose is to make it easier to modify some parameters while running Trixi.jl 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 `elixir`, the keyword argument `maxiters` is inserted -into calls to `solve` and `Trixi.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/). - -# Examples - -```jldoctest -julia> redirect_stdout(devnull) do - trixi_include(@__MODULE__, joinpath(examples_dir(), "tree_1d_dgsem", "elixir_advection_extended.jl"), - tspan=(0.0, 0.1)) - sol.t[end] - end -[ Info: You just called `trixi_include`. Julia may now compile the code, please be patient. -0.1 -``` -""" -function trixi_include(mod::Module, elixir::AbstractString; kwargs...) - # Check that all kwargs exist as assignments - code = read(elixir, 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 - - # Print information on potential wait time only in non-parallel case - if !mpi_isparallel() - @info "You just called `trixi_include`. Julia may now compile the code, please be patient." - end - Base.include(ex -> replace_assignments(insert_maxiters(ex); kwargs...), mod, elixir) -end - -function trixi_include(elixir::AbstractString; kwargs...) - trixi_include(Main, elixir; kwargs...) -end - """ convergence_test([mod::Module=Main,] elixir::AbstractString, iterations; kwargs...) @@ -177,112 +125,15 @@ end # Helper methods used in the functions defined above -# 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 `Trixi.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("Trixi") && - 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 - # This detects the case where `maxiters` is set as keyword argument - # without or before a semicolon - if (arg isa Expr && arg.head === Symbol("kw") && - arg.args[1] === Symbol("maxiters")) - return x - end - - # This detects the case where maxiters is set as keyword argument - # after a semicolon - if (arg isa Expr && arg.head === Symbol("parameters")) - # We need to check each keyword argument listed here - for nested_arg in arg.args - if (nested_arg isa Expr && - nested_arg.head === Symbol("kw") && - nested_arg.args[1] === Symbol("maxiters")) - return x - end - end - 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 - -# searches the parameter that specifies the mesh reslution in the elixir +# Searches for the assignment that specifies the mesh resolution in the elixir function extract_initial_resolution(elixir, kwargs) code = read(elixir, String) expr = Meta.parse("begin \n$code \nend") try # get the initial_refinement_level from the elixir - initial_refinement_level = find_assignment(expr, :initial_refinement_level) + initial_refinement_level = TrixiBase.find_assignment(expr, + :initial_refinement_level) if haskey(kwargs, :initial_refinement_level) return kwargs[:initial_refinement_level] @@ -294,7 +145,8 @@ function extract_initial_resolution(elixir, kwargs) if isa(e, ArgumentError) try # get cells_per_dimension from the elixir - cells_per_dimension = eval(find_assignment(expr, :cells_per_dimension)) + cells_per_dimension = eval(TrixiBase.find_assignment(expr, + :cells_per_dimension)) if haskey(kwargs, :cells_per_dimension) return kwargs[:cells_per_dimension] diff --git a/src/callbacks_step/trivial.jl b/src/callbacks_step/trivial.jl index a55b7d85b13..fb93cf96c0c 100644 --- a/src/callbacks_step/trivial.jl +++ b/src/callbacks_step/trivial.jl @@ -8,8 +8,8 @@ """ TrivialCallback() -A callback that does nothing. This can be useful to disable some callbacks -easily via [`trixi_include`](@ref). +A callback that does nothing. This can be useful to disable some callbacks easily via +[`trixi_include`](@ref). """ function TrivialCallback() DiscreteCallback(trivial_callback, trivial_callback, diff --git a/test/test_unit.jl b/test/test_unit.jl index 7943d952f71..3b8dc3c4331 100644 --- a/test/test_unit.jl +++ b/test/test_unit.jl @@ -1529,103 +1529,6 @@ end @test mesh.boundary_faces[:entire_boundary] == [1, 2] end - -@testset "trixi_include" begin - @trixi_testset "Basic" begin - example = """ - x = 4 - """ - - filename = tempname() - try - open(filename, "w") do file - write(file, example) - end - - # Use `@trixi_testset`, which wraps code in a temporary module, and call - # `trixi_include` with `@__MODULE__` in order to isolate this test. - @test_warn "You just called" trixi_include(@__MODULE__, filename) - @test @isdefined x - @test x == 4 - - @test_warn "You just called" trixi_include(@__MODULE__, filename, x = 7) - @test x == 7 - - @test_throws "assignment `y` not found in expression" trixi_include(@__MODULE__, - filename, - y = 3) - finally - rm(filename, force = true) - end - end - - @trixi_testset "With `solve` Without `maxiters`" begin - # `trixi_include` assumes this to be the `solve` function of OrdinaryDiffEq, - # and therefore tries to insert the kwarg `maxiters`, which will fail here. - example = """ - solve() = 0 - x = solve() - """ - - filename = tempname() - try - open(filename, "w") do file - write(file, example) - end - - # Use `@trixi_testset`, which wraps code in a temporary module, and call - # `trixi_include` with `@__MODULE__` in order to isolate this test. - @test_throws "no method matching solve(; maxiters::Int64)" trixi_include(@__MODULE__, - filename) - - @test_throws "no method matching solve(; maxiters::Int64)" trixi_include(@__MODULE__, - filename, - maxiters = 3) - finally - rm(filename, force = true) - end - end - - @trixi_testset "With `solve` with `maxiters`" begin - # We need another example file that we include with `Base.include` first, in order to - # define the `solve` method without `trixi_include` trying to insert `maxiters` kwargs. - # Then, we can test that `trixi_include` inserts the kwarg in the `solve()` call. - example1 = """ - solve(; maxiters=0) = maxiters - """ - - example2 = """ - x = solve() - """ - - filename1 = tempname() - filename2 = tempname() - try - open(filename1, "w") do file - write(file, example1) - end - open(filename2, "w") do file - write(file, example2) - end - - # Use `@trixi_testset`, which wraps code in a temporary module, and call - # `Base.include` and `trixi_include` with `@__MODULE__` in order to isolate this test. - Base.include(@__MODULE__, filename1) - @test_warn "You just called" trixi_include(@__MODULE__, filename2) - @test @isdefined x - # This is the default `maxiters` inserted by `trixi_include` - @test x == 10^5 - - @test_warn "You just called" trixi_include(@__MODULE__, filename2, - maxiters = 7) - # Test that `maxiters` got overwritten - @test x == 7 - finally - rm(filename1, force = true) - rm(filename2, force = true) - end - end -end end end #module From 08c6034139451ba03be2abb6748bcd402f151b5d Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Thu, 15 Feb 2024 21:37:50 +0100 Subject: [PATCH 3/5] Don't export `TrixiBase` (#1846) --- src/Trixi.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Trixi.jl b/src/Trixi.jl index ea72fbc915f..8ab8085d4e8 100644 --- a/src/Trixi.jl +++ b/src/Trixi.jl @@ -70,7 +70,8 @@ using Triangulate: Triangulate, TriangulateIO, triangulate export TriangulateIO # for type parameter in DGMultiMesh using TriplotBase: TriplotBase using TriplotRecipes: DGTriPseudocolor -@reexport using TrixiBase: TrixiBase, trixi_include +@reexport using TrixiBase: trixi_include +using TrixiBase: TrixiBase @reexport using SimpleUnPack: @unpack using SimpleUnPack: @pack! using DataStructures: BinaryHeap, FasterForward, extract_all! From a872bc55e545baf36be91a949940434ae89f3426 Mon Sep 17 00:00:00 2001 From: Hendrik Ranocha Date: Fri, 16 Feb 2024 06:24:33 +0100 Subject: [PATCH 4/5] set version to v0.6.9 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index b4a06a70688..10bf79cf9a5 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Trixi" uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb" authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "] -version = "0.6.9-pre" +version = "0.6.9" [deps] CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2" From 29e173eb5f955771755cc28342e07c9903204327 Mon Sep 17 00:00:00 2001 From: Hendrik Ranocha Date: Fri, 16 Feb 2024 06:24:52 +0100 Subject: [PATCH 5/5] set development version to v0.6.10-pre --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 10bf79cf9a5..af3e6b4d078 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Trixi" uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb" authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "] -version = "0.6.9" +version = "0.6.10-pre" [deps] CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"