From db1ea6b34365e840558c1a95c8f2f30a1b31194c Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Fri, 5 May 2023 16:29:02 +0200 Subject: [PATCH 001/128] Initial commit for the new feature using t8code as meshing backend. --- Project.toml | 1 + examples/dgmulti_2d/t8code_2d_dgsem | 0 .../t8code_2d_dgsem/elixir_advection_basic.jl | 66 +++ .../elixir_advection_nonconforming_flag.jl | 115 +++++ ..._euler_kelvin_helmholtz_instability_amr.jl | 176 +++++++ src/Trixi.jl | 5 +- src/auxiliary/auxiliary.jl | 11 + src/auxiliary/t8code.jl | 465 ++++++++++++++++++ src/callbacks_step/amr.jl | 63 +++ src/callbacks_step/amr_dg2d.jl | 70 ++- src/callbacks_step/analysis.jl | 21 + src/callbacks_step/analysis_dg2d.jl | 14 +- src/callbacks_step/stepsize_dg2d.jl | 4 +- src/meshes/meshes.jl | 1 + src/meshes/t8code_mesh.jl | 304 ++++++++++++ src/solvers/dg.jl | 3 +- src/solvers/dgsem_p4est/containers_2d.jl | 4 +- src/solvers/dgsem_p4est/dg_2d.jl | 30 +- src/solvers/dgsem_structured/dg_2d.jl | 8 +- src/solvers/dgsem_t8code/containers.jl | 329 +++++++++++++ src/solvers/dgsem_t8code/containers_2d.jl | 55 +++ src/solvers/dgsem_t8code/dg.jl | 32 ++ src/solvers/dgsem_tree/dg_2d.jl | 18 +- src/solvers/dgsem_tree/indicators_2d.jl | 2 +- src/solvers/dgsem_unstructured/dg_2d.jl | 8 +- test/test_t8code_2d.jl | 173 +++++++ 26 files changed, 1931 insertions(+), 47 deletions(-) create mode 100644 examples/dgmulti_2d/t8code_2d_dgsem create mode 100644 examples/t8code_2d_dgsem/elixir_advection_basic.jl create mode 100644 examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl create mode 100644 examples/t8code_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_amr.jl create mode 100644 src/auxiliary/t8code.jl create mode 100644 src/meshes/t8code_mesh.jl create mode 100644 src/solvers/dgsem_t8code/containers.jl create mode 100644 src/solvers/dgsem_t8code/containers_2d.jl create mode 100644 src/solvers/dgsem_t8code/dg.jl create mode 100644 test/test_t8code_2d.jl diff --git a/Project.toml b/Project.toml index 3ce9d0c559..e8c495e705 100644 --- a/Project.toml +++ b/Project.toml @@ -36,6 +36,7 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" StrideArrays = "d1fa6d79-ef01-42a6-86c9-f7c551f8593b" StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" SummationByPartsOperators = "9f78cca6-572e-554e-b819-917d2f1cf240" +T8code = "d0cc0030-9a40-4274-8435-baadcfd54fa1" TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" Triangulate = "f7e6ffb2-c36d-4f8f-a77e-16e897189344" TriplotBase = "981d1d27-644d-49a2-9326-4793e63143c3" diff --git a/examples/dgmulti_2d/t8code_2d_dgsem b/examples/dgmulti_2d/t8code_2d_dgsem new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/t8code_2d_dgsem/elixir_advection_basic.jl b/examples/t8code_2d_dgsem/elixir_advection_basic.jl new file mode 100644 index 0000000000..3c89584124 --- /dev/null +++ b/examples/t8code_2d_dgsem/elixir_advection_basic.jl @@ -0,0 +1,66 @@ +# The same setup as tree_2d_dgsem/elixir_advection_basic.jl +# to verify the StructuredMesh implementation against TreeMesh + +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the linear advection equation + +advection_velocity = (0.2, -0.7) +equations = LinearScalarAdvectionEquation2D(advection_velocity) + +# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux +solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) + +coordinates_min = (-1.0, -1.0) # minimum coordinates (min(x), min(y)) +coordinates_max = ( 1.0, 1.0) # maximum coordinates (max(x), max(y)) + +mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) + +trees_per_dimension = (8, 8) + +# Create T8codeMesh with 8 x 8 trees and 16 x 16 elements +mesh = T8codeMesh(trees_per_dimension, polydeg=3, + mapping=mapping, + initial_refinement_level=1) + +# A semidiscretization collects data structures and functions for the spatial discretization +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_convergence_test, solver) + + +############################################################################### +# ODE solvers, callbacks etc. + +# Create ODE problem with time span from 0.0 to 1.0 +ode = semidiscretize(semi, (0.0, 1.0)); + +# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup +# and resets the timers +summary_callback = SummaryCallback() + +# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results +analysis_callback = AnalysisCallback(semi, interval=100) + +# The SaveSolutionCallback allows to save the solution to a file in regular intervals +# save_solution = SaveSolutionCallback(interval=100, +# solution_variables=cons2prim) + +# The StepsizeCallback handles the re-calculation of the maximum Δt after each time step +stepsize_callback = StepsizeCallback(cfl=1.6) + +# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver +# callbacks = CallbackSet(summary_callback, analysis_callback, save_solution, stepsize_callback) +callbacks = CallbackSet(summary_callback, analysis_callback, stepsize_callback) + + +############################################################################### +# run the simulation + +# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep=false, callback=callbacks); + +# Print the timer summary +summary_callback() diff --git a/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl b/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl new file mode 100644 index 0000000000..e99e818e29 --- /dev/null +++ b/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl @@ -0,0 +1,115 @@ + +using OrdinaryDiffEq +using Trixi + + +############################################################################### +# semidiscretization of the linear advection equation + +advection_velocity = (0.2, -0.7) +equations = LinearScalarAdvectionEquation2D(advection_velocity) + +# Create DG solver with polynomial degree = 4 and (local) Lax-Friedrichs/Rusanov flux as surface flux +solver = DGSEM(polydeg=4, surface_flux=flux_lax_friedrichs) + +# Deformed rectangle that looks like a waving flag, +# lower and upper faces are sinus curves, left and right are vertical lines. +f1(s) = SVector(-1.0, s - 1.0) +f2(s) = SVector( 1.0, s + 1.0) +f3(s) = SVector(s, -1.0 + sin(0.5 * pi * s)) +f4(s) = SVector(s, 1.0 + sin(0.5 * pi * s)) + +faces = (f1, f2, f3, f4) +mapping = Trixi.transfinite_mapping(faces) + +# Create P4estMesh with 3 x 2 trees and 6 x 4 elements, +# approximate the geometry with a smaller polydeg for testing. +trees_per_dimension = (3, 2) +mesh = T8codeMesh(trees_per_dimension, polydeg=3, + mapping=mapping, + initial_refinement_level=1) + +function adapt_callback(forest, + forest_from, + which_tree, + lelement_id, + ts, + is_family, + num_elements, + elements_ptr) :: Cint + + vertex = Vector{Cdouble}(undef,3) + + elements = unsafe_wrap(Array, elements_ptr, num_elements) + + Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) + + level = Trixi.t8_element_level(ts,elements[1]) + + # TODO: Make this condition more general. + if vertex[1] < 1e-8 && vertex[2] < 1e-8 && level < 4 + # return true (refine) + return 1 + else + # return false (don't refine) + return 0 + end +end + +@Trixi.T8_ASSERT(Trixi.t8_forest_is_committed(mesh.forest) != 0); + +# Init new forest. +new_forest_ref = Ref{Trixi.t8_forest_t}() +Trixi.t8_forest_init(new_forest_ref); +new_forest = new_forest_ref[] + +let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0 + Trixi.t8_forest_set_user_data(new_forest, C_NULL) + Trixi.t8_forest_set_adapt(new_forest, mesh.forest, @Trixi.t8_adapt_callback(adapt_callback), recursive) + Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) + Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) + # t8_forest_set_ghost(new_forest, 1, T8_GHOST_FACES); + Trixi.t8_forest_commit(new_forest) +end + +mesh.forest = new_forest + +# A semidiscretization collects data structures and functions for the spatial discretization +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_convergence_test, solver) + + +############################################################################### +# ODE solvers, callbacks etc. + +# Create ODE problem with time span from 0.0 to 0.2 +ode = semidiscretize(semi, (0.0, 0.2)); + +# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup +# and resets the timers +summary_callback = SummaryCallback() + +# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results +analysis_callback = AnalysisCallback(semi, interval=100) + +# The SaveSolutionCallback allows to save the solution to a file in regular intervals +# save_solution = SaveSolutionCallback(interval=100, +# solution_variables=cons2prim) + +# The StepsizeCallback handles the re-calculation of the maximum Δt after each time step +stepsize_callback = StepsizeCallback(cfl=1.6) + +# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver +# callbacks = CallbackSet(summary_callback, analysis_callback, save_solution, stepsize_callback) +callbacks = CallbackSet(summary_callback, analysis_callback, stepsize_callback) + + +############################################################################### +# run the simulation + +# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep=false, callback=callbacks); + +# Print the timer summary +summary_callback() diff --git a/examples/t8code_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_amr.jl b/examples/t8code_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_amr.jl new file mode 100644 index 0000000000..03261f5222 --- /dev/null +++ b/examples/t8code_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_amr.jl @@ -0,0 +1,176 @@ +using Plots +using Trixi +using Printf +using OrdinaryDiffEq + +############################################################################### +# semidiscretization of the compressible Euler equations +gamma = 1.4 +equations = CompressibleEulerEquations2D(gamma) + +""" + initial_condition_kelvin_helmholtz_instability(x, t, equations::CompressibleEulerEquations2D) + +A version of the classical Kelvin-Helmholtz instability based on +- Andrés M. Rueda-Ramírez, Gregor J. Gassner (2021) + A Subcell Finite Volume Positivity-Preserving Limiter for DGSEM Discretizations + of the Euler Equations + [arXiv: 2102.06017](https://arxiv.org/abs/2102.06017) +""" +function initial_condition_kelvin_helmholtz_instability(x, t, equations::CompressibleEulerEquations2D) + # change discontinuity to tanh + # typical resolution 128^2, 256^2 + # domain size is [-1,+1]^2 + slope = 15 + amplitude = 0.02 + B = tanh(slope * x[2] + 7.5) - tanh(slope * x[2] - 7.5) + rho = 0.5 + 0.75 * B + v1 = 0.5 * (B - 1) + v2 = 0.1 * sin(2 * pi * x[1]) + p = 1.0 + return prim2cons(SVector(rho, v1, v2, p), equations) +end + +my_initial_condition = initial_condition_kelvin_helmholtz_instability + +surface_flux = flux_lax_friedrichs +volume_flux = flux_ranocha + +polydeg = 3 +inilevel = 2 +maxlevel = 6 + +basis = LobattoLegendreBasis(polydeg) +indicator_sc = IndicatorHennemannGassner(equations, basis, + alpha_max=0.002, + alpha_min=0.0001, + alpha_smooth=true, + variable=Trixi.density) + +# volume_integral = VolumeIntegralShockCapturingHG(indicator_sc; +# volume_flux_dg=volume_flux, +# volume_flux_fv=surface_flux) + +volume_integral = VolumeIntegralWeakForm() + +solver = DGSEM(basis, surface_flux, volume_integral) + +# Warped rectangle that looks like a waving flag, +f1(s) = SVector(-1.0 + 0.1 * sin( pi * s), s) +f2(s) = SVector( 1.0 + 0.1 * sin( pi * s), s) +f3(s) = SVector(s, -1.0 + 0.1 * sin( pi * s)) +f4(s) = SVector(s, 1.0 + 0.1 * sin( pi * s)) + +faces = (f1, f2, f3, f4) + +Trixi.validate_faces(faces) +mapping_flag = Trixi.transfinite_mapping(faces) + +if true + + # Simple periodic, n x n mesh. + + trees_per_dimension = (2, 2) + mesh = T8codeMesh(trees_per_dimension,polydeg=polydeg, initial_refinement_level=inilevel, mapping=mapping_flag, periodicity=true) + semi = SemidiscretizationHyperbolic(mesh, equations, my_initial_condition, solver) + +else + + # Unstructured, crazy-looking mesh read in by a 'msh' file generated with 'gmsh'. + + # It sometimes Trixi crashes for meshes loaded from 'gmesh' files because they can have + # flipped domains, e.g. [-1, 1] x [ 1,-1]. This is the case for the loaded mesh here. + # The following linear transformation flips the domain back. Cool, eh?! + coordinates_min = (-1.0, 1.0) + coordinates_max = ( 1.0, -1.0) + + mapping_flip = Trixi.coordinates2mapping(coordinates_min, coordinates_max) + + my_mapping(x,y) = mapping_flag(mapping_flip(x,y)...) + + mesh_file = joinpath(@__DIR__,"meshfiles/unstructured_quadrangle.msh") + + boundary_conditions = Dict( + :all => BoundaryConditionDirichlet(my_initial_condition), + ) + + mesh = T8codeMesh{2}(mesh_file, polydeg=polydeg, + mapping=my_mapping, + initial_refinement_level=inilevel) + + semi = SemidiscretizationHyperbolic(mesh, equations, my_initial_condition, solver, + boundary_conditions=boundary_conditions) + +end + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 10.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval=analysis_interval) + +alive_callback = AliveCallback(analysis_interval=analysis_interval) + +# Not supported yet. +# save_solution = SaveSolutionCallback(interval=100, +# save_initial_solution=true, +# save_final_solution=true, +# solution_variables=cons2prim) + +amr_indicator = IndicatorLöhner(semi, variable=Trixi.density) + +amr_controller = ControllerThreeLevel(semi, amr_indicator, + base_level=0, + med_level=0, med_threshold=0.05, + max_level=maxlevel, max_threshold=0.1) + +amr_callback = AMRCallback(semi, amr_controller, + interval=1, + adapt_initial_condition=true, + adapt_initial_condition_only_refine=true) + +stepsize_callback = StepsizeCallback(cfl=0.8) + +function my_save_plot(plot_data, variable_names; + show_mesh=true, plot_arguments=Dict{Symbol,Any}(), + time=nothing, timestep=nothing) + + title = @sprintf("2D KHI | Trixi.jl | 4th-order DG | AMR w/ t8code: t = %3.2f", time) + + sol = plot_data["rho"] + + Plots.plot(sol, + clim=(0.25,2.25), + colorbar_title="\ndensity", + title=title,titlefontsize=10, + dpi=300, + ) + Plots.plot!(getmesh(plot_data),linewidth=0.5) + + mkpath("out") + filename = joinpath("out", @sprintf("solution_%06d.png", timestep)) + Plots.savefig(filename) +end + +visualization_callback = VisualizationCallback(plot_creator=my_save_plot,interval=50, clims=(0,1.1), show_mesh=true) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + # save_solution, + amr_callback, + visualization_callback, + stepsize_callback) + +############################################################################### +# run the simulation + +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep=false, callback=callbacks); + +summary_callback() # print the timer summary diff --git a/src/Trixi.jl b/src/Trixi.jl index b45edbbecd..9571fcae2b 100644 --- a/src/Trixi.jl +++ b/src/Trixi.jl @@ -49,6 +49,7 @@ using Octavian: Octavian, matmul! using Polyester: @batch # You know, the cheapest threads you can find... using OffsetArrays: OffsetArray, OffsetVector using P4est +using T8code using Setfield: @set using RecipesBase: RecipesBase using Requires: @require @@ -104,6 +105,7 @@ include("basic_types.jl") include("auxiliary/auxiliary.jl") include("auxiliary/mpi.jl") include("auxiliary/p4est.jl") +include("auxiliary/t8code.jl") include("equations/equations.jl") include("meshes/meshes.jl") include("solvers/solvers.jl") @@ -189,7 +191,7 @@ export entropy, energy_total, energy_kinetic, energy_internal, energy_magnetic, export lake_at_rest_error export ncomponents, eachcomponent -export TreeMesh, StructuredMesh, UnstructuredMesh2D, P4estMesh +export TreeMesh, StructuredMesh, UnstructuredMesh2D, P4estMesh, T8codeMesh export DG, DGSEM, LobattoLegendreBasis, @@ -248,6 +250,7 @@ function __init__() init_mpi() init_p4est() + init_t8code() # Enable features that depend on the availability of the Plots package @require Plots="91a5bcdd-55d7-5caf-9e0b-520d859cae80" begin diff --git a/src/auxiliary/auxiliary.jl b/src/auxiliary/auxiliary.jl index fe1870fc7d..ab3b64d879 100644 --- a/src/auxiliary/auxiliary.jl +++ b/src/auxiliary/auxiliary.jl @@ -278,5 +278,16 @@ macro trixi_timeit(timer_output, label, expr) end end +function split_filename(filename) + + i = findlast(==('.'),filename) + + if isnothing(i) + return filename + end + + return filename[1:i-1],filename[i+1:end] + +end end # @muladd diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl new file mode 100644 index 0000000000..9674893da1 --- /dev/null +++ b/src/auxiliary/t8code.jl @@ -0,0 +1,465 @@ +""" + init_t8code() +""" +function init_t8code() + t8code_package_id = t8_get_package_id() + if t8code_package_id >= 0 + return nothing + end + + # Initialize `p4est` with log level ERROR to prevent a lot of output in AMR simulations + t8_init(SC_LP_ERROR) + + return nothing +end + +@inline t8_mpi_comm() = mpi_comm().val + +function trixi_t8_unref_forest(forest) + t8_forest_unref(Ref(forest)) +end + +function t8_free(ptr) + sc_free(t8_get_package_id(), ptr) +end + +function finalize_t8code() + sc_finalize(); + return nothing +end + +function trixi_t8_count_interfaces(forest) + # /* Check that forest is a committed, that is valid and usable, forest. */ + @T8_ASSERT (t8_forest_is_committed(forest) != 0); + + # /* Get the number of local elements of forest. */ + num_local_elements = t8_forest_get_local_num_elements(forest); + # /* Get the number of ghost elements of forest. */ + num_ghost_elements = t8_forest_get_num_ghosts(forest); + # /* Get the number of trees that have elements of this process. */ + num_local_trees = t8_forest_get_num_local_trees(forest); + + current_index = 0 + + local_num_conform = 0 + local_num_mortars = 0 + local_num_boundry = 0 + + for itree = 0:num_local_trees-1 + tree_class = t8_forest_get_tree_class(forest, itree); + eclass_scheme = t8_forest_get_eclass_scheme(forest, tree_class); + + # /* Get the number of elements of this tree. */ + num_elements_in_tree = t8_forest_get_tree_num_elements(forest, itree); + + for ielement = 0:num_elements_in_tree-1 + element = t8_forest_get_element_in_tree(forest, itree, ielement); + + level = t8_element_level(eclass_scheme, element) + + num_faces = t8_element_num_faces(eclass_scheme,element) + + for iface = 0:num_faces-1 + + pelement_indices_ref = Ref{Ptr{t8_locidx_t}}() + pneighbor_leafs_ref = Ref{Ptr{Ptr{t8_element}}}() + pneigh_scheme_ref = Ref{Ptr{t8_eclass_scheme}}() + + dual_faces_ref = Ref{Ptr{Cint}}() + num_neighbors_ref = Ref{Cint}() + + forest_is_balanced = Cint(1) + + t8_forest_leaf_face_neighbors(forest,itree,element, + pneighbor_leafs_ref, iface, dual_faces_ref, num_neighbors_ref, + pelement_indices_ref, pneigh_scheme_ref, forest_is_balanced) + + num_neighbors = num_neighbors_ref[] + neighbor_ielements = unsafe_wrap(Array,pelement_indices_ref[],num_neighbors) + neighbor_leafs = unsafe_wrap(Array,pneighbor_leafs_ref[],num_neighbors) + neighbor_scheme = pneigh_scheme_ref[] + + if num_neighbors > 0 + neighbor_level = t8_element_level(neighbor_scheme, neighbor_leafs[1]) + + if level == neighbor_level && all(Int32(current_index) .<= neighbor_ielements) + # TODO: Find a fix for the case: Single element on root level with periodic boundaries. + # elseif level == neighbor_level && + # (all(Int32(current_index) .< neighbor_ielements) || + # level == 0 && (iface == 0 || iface == 2 || iface == 4)) + local_num_conform += 1 + elseif level < neighbor_level + local_num_mortars += 1 + end + + else + + local_num_boundry += 1 + + end + + t8_free(dual_faces_ref[]) + t8_free(pneighbor_leafs_ref[]) + t8_free(pelement_indices_ref[]) + + end # for + + current_index += 1 + end # for + end # for + + # println("") + # println(" ## local_num_elements = ", num_local_elements) + # println(" ## local_num_conform = ", local_num_conform) + # println(" ## local_num_mortars = ", local_num_mortars) + # println(" ## local_num_boundry = ", local_num_boundry) + # println("") + + return (interfaces = local_num_conform, + mortars = local_num_mortars, + boundaries = local_num_boundry) +end + +function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundaries, boundary_names) + # /* Check that forest is a committed, that is valid and usable, forest. */ + @T8_ASSERT (t8_forest_is_committed(forest) != 0); + + # /* Get the number of local elements of forest. */ + num_local_elements = t8_forest_get_local_num_elements(forest); + # /* Get the number of ghost elements of forest. */ + num_ghost_elements = t8_forest_get_num_ghosts(forest); + # /* Get the number of trees that have elements of this process. */ + num_local_trees = t8_forest_get_num_local_trees(forest); + + current_index = 0 + + local_num_conform = 0 + local_num_mortars = 0 + local_num_boundry = 0 + + for itree = 0:num_local_trees-1 + tree_class = t8_forest_get_tree_class(forest, itree); + eclass_scheme = t8_forest_get_eclass_scheme(forest, tree_class); + + # Get the number of elements of this tree. + num_elements_in_tree = t8_forest_get_tree_num_elements(forest, itree); + + for ielement = 0:num_elements_in_tree-1 + element = t8_forest_get_element_in_tree(forest, itree, ielement); + + level = t8_element_level(eclass_scheme, element) + + num_faces = t8_element_num_faces(eclass_scheme,element) + + for iface = 0:num_faces-1 + + # Compute the `orientation` of the touching faces. + if t8_element_is_root_boundary(eclass_scheme, element, iface) == 1 + cmesh = t8_forest_get_cmesh(forest) + itree_in_cmesh = t8_forest_ltreeid_to_cmesh_ltreeid(forest, itree) + iface_in_tree = t8_element_tree_face(eclass_scheme, element, iface) + orientation_ref = Ref{Cint}() + + t8_cmesh_get_face_neighbor(cmesh, itree_in_cmesh, iface_in_tree, C_NULL, orientation_ref) + orientation = orientation_ref[] + else + orientation = 0 + end + + pelement_indices_ref = Ref{Ptr{t8_locidx_t}}() + pneighbor_leafs_ref = Ref{Ptr{Ptr{t8_element}}}() + pneigh_scheme_ref = Ref{Ptr{t8_eclass_scheme}}() + + dual_faces_ref = Ref{Ptr{Cint}}() + num_neighbors_ref = Ref{Cint}() + + forest_is_balanced = Cint(1) + + t8_forest_leaf_face_neighbors(forest,itree,element, + pneighbor_leafs_ref, iface, dual_faces_ref, num_neighbors_ref, + pelement_indices_ref, pneigh_scheme_ref, forest_is_balanced) + + num_neighbors = num_neighbors_ref[] + dual_faces = unsafe_wrap(Array,dual_faces_ref[],num_neighbors) + neighbor_ielements = unsafe_wrap(Array,pelement_indices_ref[],num_neighbors) + neighbor_leafs = unsafe_wrap(Array,pneighbor_leafs_ref[],num_neighbors) + neighbor_scheme = pneigh_scheme_ref[] + + if num_neighbors > 0 + neighbor_level = t8_element_level(neighbor_scheme, neighbor_leafs[1]) + + # Conforming interface: The second condition ensures we only visit the interface once. + if level == neighbor_level && Int32(current_index) <= neighbor_ielements[1] + # elseif level == neighbor_level && + # (all(Int32(current_index) .< neighbor_ielements) || + # level == 0 && (iface == 0 || iface == 2 || iface == 4)) + local_num_conform += 1 + + faces = (iface, dual_faces[1]) + interface_id = local_num_conform + + # Write data to interfaces container. + interfaces.neighbor_ids[1, interface_id] = current_index + 1 + interfaces.neighbor_ids[2, interface_id] = neighbor_ielements[1] + 1 + + # Iterate over primary and secondary element. + for side in 1:2 + # Align interface in positive coordinate direction of primary element. + # For orientation == 1, the secondary element needs to be indexed backwards + # relative to the interface. + if side == 1 || orientation == 0 + # Forward indexing + indexing = :i_forward + else + # Backward indexing + indexing = :i_backward + end + + if faces[side] == 0 + # Index face in negative x-direction + interfaces.node_indices[side, interface_id] = (:begin, indexing) + elseif faces[side] == 1 + # Index face in positive x-direction + interfaces.node_indices[side, interface_id] = (:end, indexing) + elseif faces[side] == 2 + # Index face in negative y-direction + interfaces.node_indices[side, interface_id] = (indexing, :begin) + else # faces[side] == 3 + # Index face in positive y-direction + interfaces.node_indices[side, interface_id] = (indexing, :end) + end + end + + # Non-conforming interface. + elseif level < neighbor_level + local_num_mortars += 1 + + faces = (dual_faces[1],iface) + + mortar_id = local_num_mortars + + # Last entry is the large element ... What a stupid convention! + # mortars.neighbor_ids[end, mortar_id] = ielement + 1 + mortars.neighbor_ids[end, mortar_id] = current_index + 1 + + # First `1:end-1` entries are the smaller elements. + mortars.neighbor_ids[1:end-1, mortar_id] .= neighbor_ielements[:] .+ 1 + + for side in 1:2 + # Align mortar in positive coordinate direction of small side. + # For orientation == 1, the large side needs to be indexed backwards + # relative to the mortar. + if side == 1 || orientation == 0 + # Forward indexing for small side or orientation == 0 + indexing = :i_forward + else + # Backward indexing for large side with reversed orientation + indexing = :i_backward + end + + if faces[side] == 0 + # Index face in negative x-direction + mortars.node_indices[side, mortar_id] = (:begin, indexing) + elseif faces[side] == 1 + # Index face in positive x-direction + mortars.node_indices[side, mortar_id] = (:end, indexing) + elseif faces[side] == 2 + # Index face in negative y-direction + mortars.node_indices[side, mortar_id] = (indexing, :begin) + else # faces[side] == 3 + # Index face in positive y-direction + mortars.node_indices[side, mortar_id] = (indexing, :end) + end + end + + # else: "level > neighbor_level" is skipped since we visit the mortar interface only once. + end + + # Domain boundary. + else + local_num_boundry += 1 + boundary_id = local_num_boundry + + boundaries.neighbor_ids[boundary_id] = current_index + 1 + + # println("neighbor_id = ", boundaries.neighbor_ids[boundary_id] -1) + # println("face = ", iface) + + if iface == 0 + # Index face in negative x-direction. + boundaries.node_indices[boundary_id] = (:begin, :i_forward) + elseif iface == 1 + # Index face in positive x-direction. + boundaries.node_indices[boundary_id] = (:end, :i_forward) + elseif iface == 2 + # Index face in negative y-direction. + boundaries.node_indices[boundary_id] = (:i_forward, :begin) + else # iface == 3 + # Index face in positive y-direction. + boundaries.node_indices[boundary_id] = (:i_forward, :end) + end + + # One-based indexing. + boundaries.name[boundary_id] = boundary_names[iface + 1, itree + 1] + end + + t8_free(dual_faces_ref[]) + t8_free(pneighbor_leafs_ref[]) + t8_free(pelement_indices_ref[]) + + end # for iface = ... + + current_index += 1 + end # for + end # for + + # println("") + # println("") + # println(" ## local_num_conform = ", local_num_conform) + # println(" ## local_num_mortars = ", local_num_mortars) + # println(" ## local_num_boundry = ", local_num_boundry) + # println("") + # println("") + + return (interfaces = local_num_conform, + mortars = local_num_mortars, + boundaries = local_num_boundry) +end + +function trixi_t8_get_local_element_levels(forest) + # /* Check that forest is a committed, that is valid and usable, forest. */ + @T8_ASSERT (t8_forest_is_committed(forest) != 0); + + levels = Vector{Int}(undef, t8_forest_get_local_num_elements(forest)) + + # /* Get the number of trees that have elements of this process. */ + num_local_trees = t8_forest_get_num_local_trees(forest); + + current_index = 0 + + for itree = 0:num_local_trees-1 + tree_class = t8_forest_get_tree_class(forest, itree); + eclass_scheme = t8_forest_get_eclass_scheme(forest, tree_class); + + # /* Get the number of elements of this tree. */ + num_elements_in_tree = t8_forest_get_tree_num_elements(forest, itree); + + for ielement = 0:num_elements_in_tree-1 + element = t8_forest_get_element_in_tree(forest, itree, ielement); + current_index += 1 + levels[current_index] = t8_element_level(eclass_scheme, element) + end # for + end # for + + return levels +end + +function adapt_callback(forest, + forest_from, + which_tree, + lelement_id, + ts, + is_family, + num_elements, + elements) :: Cint + + num_levels = t8_forest_get_local_num_elements(forest_from) + + indicator_ptr = Ptr{Int}(t8_forest_get_user_data(forest)) + indicators = unsafe_wrap(Array,indicator_ptr,num_levels) + + offset = t8_forest_get_tree_element_offset(forest_from, which_tree) + + # Only allow coarsening for complete families. + if indicators[offset + lelement_id + 1] < 0 && is_family == 0 + return Cint(0) + end + + return Cint(indicators[offset + lelement_id + 1]) + +end + +function trixi_t8_adapt_new(old_forest, indicators) + # /* Check that forest is a committed, that is valid and usable, forest. */ + @T8_ASSERT (t8_forest_is_committed(old_forest) != 0); + + # Init new forest. + new_forest_ref = Ref{t8_forest_t}() + t8_forest_init(new_forest_ref); + new_forest = new_forest_ref[] + + let set_from = C_NULL, recursive = 0, set_for_coarsening = 0, no_repartition = 0 + t8_forest_set_user_data(new_forest, pointer(indicators)) + t8_forest_set_adapt(new_forest, old_forest, @t8_adapt_callback(adapt_callback), recursive) + t8_forest_set_balance(new_forest, set_from, no_repartition) + t8_forest_set_partition(new_forest, set_from, set_for_coarsening) + # t8_forest_set_ghost(new_forest, 1, T8_GHOST_FACES); + t8_forest_commit(new_forest) + end + + return new_forest +end + +function trixi_t8_get_difference(old_levels, new_levels) + + old_nelems = length(old_levels) + new_nelems = length(new_levels) + + changes = Vector{Int}(undef, old_nelems) + + # Local element indices. + old_index = 1 + new_index = 1 + + # TODO: Make general for 2D/3D and hybrid grids. + T8_CHILDREN = 4 + + while old_index <= old_nelems && new_index <= new_nelems + + if old_levels[old_index] < new_levels[new_index] + # Refined. + + changes[old_index] = 1 + + old_index += 1 + new_index += T8_CHILDREN + + elseif old_levels[old_index] > new_levels[new_index] + # Coarsend. + + for child_index = old_index:old_index+T8_CHILDREN-1 + changes[child_index] = -1 + end + + old_index += T8_CHILDREN + new_index += 1 + + else + # No changes. + + changes[old_index] = 0 + + old_index += 1 + new_index += 1 + end + end + + return changes +end + +# Coarsen or refine marked cells and rebalance forest. Return a difference between +# old and new mesh. +function trixi_t8_adapt!(mesh, indicators) + + old_levels = trixi_t8_get_local_element_levels(mesh.forest) + + forest_cached = trixi_t8_adapt_new(mesh.forest, indicators) + + new_levels = trixi_t8_get_local_element_levels(forest_cached) + + differences = trixi_t8_get_difference(old_levels, new_levels) + + mesh.forest = forest_cached + + return differences +end diff --git a/src/callbacks_step/amr.jl b/src/callbacks_step/amr.jl index 4655a0b9ef..8ae6a74876 100644 --- a/src/callbacks_step/amr.jl +++ b/src/callbacks_step/amr.jl @@ -445,6 +445,66 @@ function (amr_callback::AMRCallback)(u_ode::AbstractVector, mesh::P4estMesh, return has_changed end +function (amr_callback::AMRCallback)(u_ode::AbstractVector, mesh::T8codeMesh, + equations, dg::DG, cache, semi, + t, iter; + only_refine=false, only_coarsen=false, + passive_args=()) + + has_changed = false + + @unpack controller, adaptor = amr_callback + + u = wrap_array(u_ode, mesh, equations, dg, cache) + indicators = @trixi_timeit timer() "indicator" controller(u, mesh, equations, dg, cache, t=t, iter=iter) + + if only_coarsen + indicators[indicators .> 0] .= 0 + end + + if only_refine + indicators[indicators .< 0] .= 0 + end + + @boundscheck begin + @assert axes(indicators) == (Base.OneTo(ncells(mesh)),) ( + "Indicator array (axes = $(axes(indicators))) and mesh cells (axes = $(Base.OneTo(ncells(mesh)))) have different axes" + ) + end + + @trixi_timeit timer() "adapt" begin + difference = @trixi_timeit timer() "mesh" trixi_t8_adapt!(mesh,indicators) + + @trixi_timeit timer() "solver" adapt!(u_ode, adaptor, mesh, equations, dg, cache, difference) + end + + # Store whether there were any cells coarsened or refined and perform load balancing. + has_changed = any(difference .!= 0) + + # TODO: T8codeMesh for MPI not implemented yet. + # Check if mesh changed on other processes + # if mpi_isparallel() + # has_changed = MPI.Allreduce!(Ref(has_changed), |, mpi_comm())[] + # end + + if has_changed + # TODO: T8codeMesh for MPI not implemented yet. + # if mpi_isparallel() && amr_callback.dynamic_load_balancing + # @trixi_timeit timer() "dynamic load balancing" begin + # global_first_quadrant = unsafe_wrap(Array, mesh.p4est.global_first_quadrant, mpi_nranks() + 1) + # old_global_first_quadrant = copy(global_first_quadrant) + # partition!(mesh) + # rebalance_solver!(u_ode, mesh, equations, dg, cache, old_global_first_quadrant) + # end + # end + + reinitialize_boundaries!(semi.boundary_conditions, cache) + end + + # Return true if there were any cells coarsened or refined, otherwise false. + return has_changed +end + function reinitialize_boundaries!(boundary_conditions::UnstructuredSortedBoundaryTypes, cache) # Reinitialize boundary types container because boundaries may have changed. initialize!(boundary_conditions, cache) @@ -604,6 +664,9 @@ function current_element_levels(mesh::P4estMesh, solver, cache) return current_levels end +function current_element_levels(mesh::T8codeMesh, solver, cache) + return trixi_t8_get_local_element_levels(mesh.forest) +end # TODO: Taal refactor, merge the two loops of ControllerThreeLevel and IndicatorLöhner etc.? # But that would remove the simplest possibility to write that stuff to a file... diff --git a/src/callbacks_step/amr_dg2d.jl b/src/callbacks_step/amr_dg2d.jl index 9f677d1dc4..620b1f05da 100644 --- a/src/callbacks_step/amr_dg2d.jl +++ b/src/callbacks_step/amr_dg2d.jl @@ -320,7 +320,7 @@ end # this method is called when an `ControllerThreeLevel` is constructed -function create_cache(::Type{ControllerThreeLevel}, mesh::Union{TreeMesh{2}, P4estMesh{2}}, equations, dg::DG, cache) +function create_cache(::Type{ControllerThreeLevel}, mesh::Union{TreeMesh{2}, P4estMesh{2}, T8codeMesh{2}}, equations, dg::DG, cache) controller_value = Vector{Int}(undef, nelements(dg, cache)) return (; controller_value) @@ -332,5 +332,73 @@ function create_cache(::Type{ControllerThreeLevelCombined}, mesh::TreeMesh{2}, e return (; controller_value) end +# Coarsen and refine elements in the DG solver based on a difference list. +function adapt!(u_ode::AbstractVector, adaptor, mesh::T8codeMesh{2}, equations, dg::DGSEM, cache, difference) + + # Return early if there is nothing to do. + if !any(difference .!= 0) + return nothing + end + + # Number of (local) cells/elements. + old_nelems = nelements(dg, cache) + new_nelems = ncells(mesh) + + # Local element indices. + old_index = 1 + new_index = 1 + + # Note: This is true for `quads` only. + T8_CHILDREN = 4 + + # Retain current solution data. + old_u_ode = copy(u_ode) + + GC.@preserve old_u_ode begin + old_u = wrap_array(old_u_ode, mesh, equations, dg, cache) + + reinitialize_containers!(mesh, equations, dg, cache) + + resize!(u_ode, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) + u = wrap_array(u_ode, mesh, equations, dg, cache) + + while old_index <= old_nelems && new_index <= new_nelems + + if difference[old_index] > 0 # Refine. + + # Refine element and store solution directly in new data structure. + refine_element!(u, new_index, old_u, old_index, adaptor, equations, dg) + + old_index += 1 + new_index += T8_CHILDREN + + elseif difference[old_index] < 0 # Coarsen. + + # If an element is to be removed, sanity check if the following elements + # are also marked - otherwise there would be an error in the way the + # cells/elements are sorted. + @assert all(difference[old_index:(old_index+T8_CHILDREN-1)] .< 0) "bad cell/element order" + + # Coarsen elements and store solution directly in new data structure. + coarsen_elements!(u, new_index, old_u, old_index, adaptor, equations, dg) + + old_index += T8_CHILDREN + new_index += 1 + + else # No changes. + + # Copy old element data to new element container. + @views u[:, .., new_index] .= old_u[:, .., old_index] + + old_index += 1 + new_index += 1 + end + + end # while + + end # GC.@preserve old_u_ode + + return nothing +end end # @muladd diff --git a/src/callbacks_step/analysis.jl b/src/callbacks_step/analysis.jl index 79a442f06c..4587fbf245 100644 --- a/src/callbacks_step/analysis.jl +++ b/src/callbacks_step/analysis.jl @@ -496,6 +496,27 @@ function print_amr_information(callbacks, mesh::P4estMesh, solver, cache) return nothing end +# Print level information only if AMR is enabled +function print_amr_information(callbacks, mesh::T8codeMesh, solver, cache) + + # Return early if there is nothing to print + uses_amr(callbacks) || return nothing + + levels = trixi_t8_get_local_element_levels(mesh.forest) + + min_level = minimum(levels) + max_level = maximum(levels) + + mpi_println(" minlevel = $min_level") + mpi_println(" maxlevel = $max_level") + + # for level = max_level:-1:min_level+1 + # mpi_println(" ├── level $level: " * @sprintf("% 14d", elements_per_level[level + 1])) + # end + # mpi_println(" └── level $min_level: " * @sprintf("% 14d", elements_per_level[min_level + 1])) + + return nothing +end # Iterate over tuples of analysis integrals in a type-stable way using "lispy tuple programming". function analyze_integrals(analysis_integrals::NTuple{N,Any}, io, du, u, t, semi) where {N} diff --git a/src/callbacks_step/analysis_dg2d.jl b/src/callbacks_step/analysis_dg2d.jl index 453474675f..ae7c93d81f 100644 --- a/src/callbacks_step/analysis_dg2d.jl +++ b/src/callbacks_step/analysis_dg2d.jl @@ -26,7 +26,7 @@ function create_cache_analysis(analyzer, mesh::TreeMesh{2}, end -function create_cache_analysis(analyzer, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, +function create_cache_analysis(analyzer, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, equations, dg::DG, cache, RealT, uEltype) @@ -96,7 +96,7 @@ end function calc_error_norms(func, u, t, analyzer, - mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, equations, + mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, equations, initial_condition, dg::DGSEM, cache, cache_analysis) @unpack vandermonde, weights = analyzer @unpack node_coordinates, inverse_jacobian = cache.elements @@ -159,7 +159,7 @@ end function integrate_via_indices(func::Func, u, - mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, equations, + mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, equations, dg::DGSEM, cache, args...; normalize=true) where {Func} @unpack weights = dg.basis @@ -186,7 +186,7 @@ end function integrate(func::Func, u, - mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, + mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, equations, dg::DG, cache; normalize=true) where {Func} integrate_via_indices(u, mesh, equations, dg, cache; normalize=normalize) do u, i, j, element, equations, dg u_local = get_node_vars(u, equations, dg, i, j, element) @@ -196,7 +196,7 @@ end function analyze(::typeof(entropy_timederivative), du, u, t, - mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, + mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, equations, dg::DG, cache) # Calculate ∫(∂S/∂u ⋅ ∂u/∂t)dΩ integrate_via_indices(u, mesh, equations, dg, cache, du) do u, i, j, element, equations, dg, du @@ -237,7 +237,7 @@ function analyze(::Val{:l2_divb}, du, u, t, end function analyze(::Val{:l2_divb}, du, u, t, - mesh::Union{StructuredMesh{2},UnstructuredMesh2D,P4estMesh{2}}, + mesh::Union{StructuredMesh{2},UnstructuredMesh2D,P4estMesh{2}, T8codeMesh{2}}, equations::IdealGlmMhdEquations2D, dg::DGSEM, cache) @unpack contravariant_vectors = cache.elements integrate_via_indices(u, mesh, equations, dg, cache, cache, dg.basis.derivative_matrix) do u, i, j, element, equations, dg, cache, derivative_matrix @@ -301,7 +301,7 @@ function analyze(::Val{:linf_divb}, du, u, t, end function analyze(::Val{:linf_divb}, du, u, t, - mesh::Union{StructuredMesh{2},UnstructuredMesh2D,P4estMesh{2}}, + mesh::Union{StructuredMesh{2},UnstructuredMesh2D,P4estMesh{2}, T8codeMesh{2}}, equations::IdealGlmMhdEquations2D, dg::DGSEM, cache) @unpack derivative_matrix, weights = dg.basis @unpack contravariant_vectors = cache.elements diff --git a/src/callbacks_step/stepsize_dg2d.jl b/src/callbacks_step/stepsize_dg2d.jl index 3c7d288d8d..8f4f558094 100644 --- a/src/callbacks_step/stepsize_dg2d.jl +++ b/src/callbacks_step/stepsize_dg2d.jl @@ -75,7 +75,7 @@ function max_dt(u, t, mesh::ParallelTreeMesh{2}, end -function max_dt(u, t, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, +function max_dt(u, t, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, constant_speed::False, equations, dg::DG, cache) # to avoid a division by zero if the speed vanishes everywhere, # e.g. for steady-state linear advection @@ -108,7 +108,7 @@ function max_dt(u, t, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMe end -function max_dt(u, t, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, +function max_dt(u, t, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, constant_speed::True, equations, dg::DG, cache) @unpack contravariant_vectors, inverse_jacobian = cache.elements diff --git a/src/meshes/meshes.jl b/src/meshes/meshes.jl index a6dcbe132d..b57228825f 100644 --- a/src/meshes/meshes.jl +++ b/src/meshes/meshes.jl @@ -12,6 +12,7 @@ include("unstructured_mesh.jl") include("face_interpolant.jl") include("transfinite_mappings_3d.jl") include("p4est_mesh.jl") +include("t8code_mesh.jl") include("mesh_io.jl") include("dgmulti_meshes.jl") diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl new file mode 100644 index 0000000000..2d6e7ed300 --- /dev/null +++ b/src/meshes/t8code_mesh.jl @@ -0,0 +1,304 @@ +""" + T8codeMesh{NDIMS} <: AbstractMesh{NDIMS} +An unstructured curved mesh based on trees that uses the C library 't8code' +to manage trees and mesh refinement. +""" +mutable struct T8codeMesh{NDIMS, RealT<:Real, IsParallel} <: AbstractMesh{NDIMS} + cmesh :: Ptr{t8_cmesh} # cpointer to coarse mesh + scheme :: Ptr{t8_eclass_scheme} # cpointer to element scheme + forest :: Ptr{t8_forest} # cpointer to forest + is_parallel :: IsParallel + + # ghost :: Ghost # Either Ptr{t8code_ghost_t} or Ptr{t8code_hex_ghost_t} + # Coordinates at the nodes specified by the tensor product of 'nodes' (NDIMS times). + # This specifies the geometry interpolation for each tree. + + tree_node_coordinates # :: Array{RealT, NDIMS+2} # [dimension, i, j, k, tree] + + nodes # :: SVector{NNODES, RealT} + + boundary_names :: Array{Symbol, 2} # [face direction, tree] + current_filename :: String + unsaved_changes :: Bool + + ncells :: Int + ninterfaces :: Int + nmortars :: Int + nboundaries :: Int + + function T8codeMesh{NDIMS}(cmesh, scheme, forest) where NDIMS + # TODO: Implement MPI parallelization. + # if mpi_isparallel() + # if !T8code.uses_mpi() + # error("t8code library does not support MPI") + # end + # is_parallel = Val(true) + # else + # is_parallel = Val(false) + # end + is_parallel = Val(false) + + mesh = new{NDIMS, Float64, typeof(is_parallel)}(cmesh, scheme, forest, is_parallel) + + # Destroy 't8code' structs when the mesh is garbage collected. + finalizer(function (mesh::T8codeMesh{NDIMS}) + trixi_t8_unref_forest(mesh.forest) + finalize_t8code() + end, mesh) + + return mesh + end +end + +function T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, boundary_names, + current_filename, unsaved_changes) where NDIMS + + mesh = T8codeMesh{NDIMS}(cmesh, scheme, forest) + + mesh.nodes = nodes + mesh.boundary_names = boundary_names + mesh.unsaved_changes = unsaved_changes + mesh.current_filename = current_filename + mesh.tree_node_coordinates = tree_node_coordinates + + return mesh +end + +SerialT8codeMesh{NDIMS} = T8codeMesh{NDIMS, <:Val{false}} +# @inline mpi_parallel(mesh::SerialT8codeMesh) = Val(false) + +@inline Base.ndims(::T8codeMesh{NDIMS}) where NDIMS = NDIMS +@inline Base.real(::T8codeMesh{NDIMS, RealT}) where {NDIMS, RealT} = RealT + +# TODO: What should be returned in case of parallel processes? Local vs global. +@inline ntrees(mesh::T8codeMesh) = Int(t8_forest_get_num_global_trees(mesh.forest)) +# @inline ncells(mesh::T8codeMesh) = Int(t8_forest_get_global_num_elements(mesh.forest)) +@inline ncells(mesh::T8codeMesh) = Int(t8_forest_get_local_num_elements(mesh.forest)) +@inline ninterfaces(mesh::T8codeMesh) = mesh.ninterfaces +@inline nmortars(mesh::T8codeMesh) = mesh.nmortars +@inline nboundaries(mesh::T8codeMesh) = mesh.nboundaries + +function Base.show(io::IO, mesh::T8codeMesh) + print(io, "T8codeMesh{", ndims(mesh), ", ", real(mesh), "}") +end + +function Base.show(io::IO, :: MIME"text/plain", mesh::T8codeMesh) + if get(io, :compact, false) + show(io, mesh) + else + setup = [ + "#trees" => ntrees(mesh), + "current #cells" => ncells(mesh), + "polydeg" => length(mesh.nodes) - 1, + ] + summary_box(io, "T8codeMesh{" * string(ndims(mesh)) * ", " * string(real(mesh)) * "}", setup) + end +end + +""" + T8codeMesh(trees_per_dimension; polydeg, + mapping=nothing, faces=nothing, coordinates_min=nothing, coordinates_max=nothing, + RealT=Float64, initial_refinement_level=0, periodicity=true, unsaved_changes=true) +Create a structured curved 'T8codeMesh' of the specified size. +There are three ways to map the mesh to the physical domain. +1. Define a 'mapping' that maps the hypercube '[-1, 1]^n'. +2. Specify a 'Tuple' 'faces' of functions that parametrize each face. +3. Create a rectangular mesh by specifying 'coordinates_min' and 'coordinates_max'. +Non-periodic boundaries will be called ':x_neg', ':x_pos', ':y_neg', ':y_pos', ':z_neg', ':z_pos'. +# Arguments +- 'trees_per_dimension::NTupleE{NDIMS, Int}': the number of trees in each dimension. +- 'polydeg::Integer': polynomial degree used to store the geometry of the mesh. + The mapping will be approximated by an interpolation polynomial + of the specified degree for each tree. +- 'mapping': a function of 'NDIMS' variables to describe the mapping that transforms + the reference mesh ('[-1, 1]^n') to the physical domain. +- 'RealT::Type': the type that should be used for coordinates. +- 'initial_refinement_level::Integer': refine the mesh uniformly to this level before the simulation starts. +- 'periodicity': either a 'Bool' deciding if all of the boundaries are periodic or an 'NTuple{NDIMS, Bool}' + deciding for each dimension if the boundaries in this dimension are periodic. +- 'unsaved_changes::Bool': if set to 'true', the mesh will be saved to a mesh file. +""" +function T8codeMesh(trees_per_dimension; polydeg, + mapping, RealT=Float64, initial_refinement_level=0, periodicity=true, unsaved_changes=true) + + NDIMS = length(trees_per_dimension) + + @assert NDIMS == 2 # Only support for NDIMS = 2 yet. + + # Convert periodicity to a Tuple of a Bool for every dimension + if all(periodicity) + # Also catches case where periodicity = true + periodicity = ntuple(_->true, NDIMS) + elseif !any(periodicity) + # Also catches case where periodicity = false + periodicity = ntuple(_->false, NDIMS) + else + # Default case if periodicity is an iterable + periodicity = Tuple(periodicity) + end + + conn = p4est_connectivity_new_brick(trees_per_dimension..., periodicity...) + do_partition = 0 + cmesh = t8_cmesh_new_from_p4est(conn,t8_mpi_comm(),do_partition) + p4est_connectivity_destroy(conn) + + scheme = t8_scheme_new_default_cxx() + forest = t8_forest_new_uniform(cmesh,scheme,initial_refinement_level,0,mpi_comm().val) + + basis = LobattoLegendreBasis(RealT, polydeg) + nodes = basis.nodes + + tree_node_coordinates = Array{RealT, NDIMS+2}(undef, NDIMS, + ntuple(_ -> length(nodes), NDIMS)..., + prod(trees_per_dimension)) + + # Get cell length in reference mesh: Omega_ref = [-1,1]^2. + dx = 2 / trees_per_dimension[1] + dy = 2 / trees_per_dimension[2] + + num_local_trees = t8_cmesh_get_num_local_trees(cmesh) + + # Non-periodic boundaries. + boundary_names = fill(Symbol("---"), 2 * NDIMS, prod(trees_per_dimension)) + + for itree = 1:num_local_trees + veptr = t8_cmesh_get_tree_vertices(cmesh, itree-1) + verts = unsafe_wrap(Array,veptr,(3,1 << NDIMS)) + + # Calculate node coordinates of reference mesh. + cell_x_offset = (verts[1,1] - 1/2*(trees_per_dimension[1]-1)) * dx + cell_y_offset = (verts[2,1] - 1/2*(trees_per_dimension[2]-1)) * dy + + for j in eachindex(nodes), i in eachindex(nodes) + tree_node_coordinates[:, i, j, itree] .= mapping(cell_x_offset + dx * nodes[i]/2, + cell_y_offset + dy * nodes[j]/2) + end + + if !periodicity[1] + boundary_names[1, itree] = :x_neg + boundary_names[2, itree] = :x_pos + end + + if !periodicity[2] + boundary_names[3, itree] = :y_neg + boundary_names[4, itree] = :y_pos + end + end + + return T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, boundary_names, "", unsaved_changes) + +end + +""" + T8codeMesh{NDIMS}(meshfile::String; + mapping=nothing, polydeg=1, RealT=Float64, + initial_refinement_level=0, unsaved_changes=true) +Main mesh constructor for the `T8codeMesh` that imports an unstructured, conforming +mesh from an Abaqus mesh file (`.inp`). Each element of the conforming mesh parsed +from the `meshfile` is created as a [`p4est`](https://github.com/cburstedde/p4est) +tree datatype. +Note that the `mapping` and `polydeg` keyword arguments are only used by the `p4est_mesh_from_standard_abaqus` +function. The `p4est_mesh_from_hohqmesh_abaqus` function obtains the mesh `polydeg` directly from the `meshfile` +and constructs the transfinite mapping internally. +The particular strategy is selected according to the header present in the `meshfile` where +the constructor checks whether or not the `meshfile` was created with +[HOHQMesh.jl](https://github.com/trixi-framework/HOHQMesh.jl). +If the Abaqus file header is not present in the `meshfile` then the `P4estMesh` is created +with the function `p4est_mesh_from_standard_abaqus`. +The default keyword argument `initial_refinement_level=0` corresponds to a forest +where the number of trees is the same as the number of elements in the original `meshfile`. +Increasing the `initial_refinement_level` allows one to uniformly refine the base mesh given +in the `meshfile` to create a forest with more trees before the simulation begins. +For example, if a two-dimensional base mesh contains 25 elements then setting +`initial_refinement_level=1` creates an initial forest of `2^2 * 25 = 100` trees. +# Arguments +- `meshfile::String`: an uncurved Abaqus mesh file that can be imported by `p4est`. +- `mapping`: a function of `NDIMS` variables to describe the mapping that transforms + the imported mesh to the physical domain. Use `nothing` for the identity map. +- `polydeg::Integer`: polynomial degree used to store the geometry of the mesh. + The mapping will be approximated by an interpolation polynomial + of the specified degree for each tree. + The default of `1` creates an uncurved geometry. Use a higher value if the mapping + will curve the imported uncurved mesh. +- `RealT::Type`: the type that should be used for coordinates. +- `initial_refinement_level::Integer`: refine the mesh uniformly to this level before the simulation starts. +- `unsaved_changes::Bool`: if set to `true`, the mesh will be saved to a mesh file. +""" +function T8codeMesh{NDIMS}(meshfile::String; + mapping=nothing, polydeg=1, RealT=Float64, + initial_refinement_level=0, unsaved_changes=true) where NDIMS + + @assert NDIMS == 2 # Only support for NDIMS = 2 yet. + + # Prevent `t8code` from crashing Julia if the file doesn't exist. + @assert isfile(meshfile) + + meshfile_prefix, meshfile_suffix = split_filename(meshfile) + + cmesh = t8_cmesh_from_msh_file(meshfile_prefix, 0, t8_mpi_comm(), 2, 0, 0) + + scheme = t8_scheme_new_default_cxx() + forest = t8_forest_new_uniform(cmesh,scheme,initial_refinement_level,0,mpi_comm().val) + + basis = LobattoLegendreBasis(RealT, polydeg) + nodes = basis.nodes + + num_local_trees = t8_cmesh_get_num_local_trees(cmesh) + + tree_node_coordinates = Array{RealT, NDIMS+2}(undef, NDIMS, + ntuple(_ -> length(nodes), NDIMS)..., + num_local_trees) + + nodes_in = [-1.0, 1.0] + matrix = polynomial_interpolation_matrix(nodes_in, nodes) + data_in = Array{RealT, 3}(undef, 2, 2, 2) + tmp1 = zeros(RealT, 2, length(nodes), length(nodes_in)) + + for itree in 0:num_local_trees-1 + + veptr = t8_cmesh_get_tree_vertices(cmesh, itree) + verts = unsafe_wrap(Array,veptr,(3,1 << NDIMS)) + + u = verts[:,2] - verts[:,1] + v = verts[:,3] - verts[:,1] + w = [0.0,0.0,1.0] + + vol = dot(cross(u,v),w) + + if vol < 0.0 + @warn "Discovered negative volumes in `cmesh`: vol = $vol" + end + + # Tree vertices are stored in z-order. + @views data_in[:, 1, 1] .= verts[1:2,1] + @views data_in[:, 2, 1] .= verts[1:2,2] + @views data_in[:, 1, 2] .= verts[1:2,3] + @views data_in[:, 2, 2] .= verts[1:2,4] + + # Interpolate corner coordinates to specified nodes. + multiply_dimensionwise!( + view(tree_node_coordinates, :, :, :, itree+1), + matrix, matrix, + data_in, + tmp1 + ) + + end + + map_node_coordinates!(tree_node_coordinates, mapping) + + # There's no simple and generic way to distinguish boundaries. Name all of them :all. + boundary_names = fill(:all, 2 * NDIMS, num_local_trees) + + return T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, boundary_names, "", unsaved_changes) +end + +# TODO: Just a placeholder. +function balance!(mesh::T8codeMesh{2}, init_fn=C_NULL) + return nothing +end + +# TODO: Just a placeholder. +function partition!(mesh::T8codeMesh{2}; allow_coarsening=true, weight_fn=C_NULL) + return nothing +end diff --git a/src/solvers/dg.jl b/src/solvers/dg.jl index 29de151f14..096f787164 100644 --- a/src/solvers/dg.jl +++ b/src/solvers/dg.jl @@ -366,7 +366,7 @@ function get_element_variables!(element_variables, u, mesh, equations, dg::DG, c end -const MeshesDGSEM = Union{TreeMesh, StructuredMesh, UnstructuredMesh2D, P4estMesh} +const MeshesDGSEM = Union{TreeMesh, StructuredMesh, UnstructuredMesh2D, P4estMesh, T8codeMesh} @inline ndofs(mesh::MeshesDGSEM, dg::DG, cache) = nelements(cache.elements) * nnodes(dg)^ndims(mesh) @@ -632,6 +632,7 @@ include("dgsem_tree/dg.jl") include("dgsem_structured/dg.jl") include("dgsem_unstructured/dg.jl") include("dgsem_p4est/dg.jl") +include("dgsem_t8code/dg.jl") # Finite difference methods using summation by parts (SBP) operators diff --git a/src/solvers/dgsem_p4est/containers_2d.jl b/src/solvers/dgsem_p4est/containers_2d.jl index cf18e433ff..51df8ca1e4 100644 --- a/src/solvers/dgsem_p4est/containers_2d.jl +++ b/src/solvers/dgsem_p4est/containers_2d.jl @@ -6,7 +6,7 @@ # Initialize data structures in element container -function init_elements!(elements, mesh::P4estMesh{2}, basis::LobattoLegendreBasis) +function init_elements!(elements, mesh::Union{P4estMesh{2},T8codeMesh{2}}, basis::LobattoLegendreBasis) @unpack node_coordinates, jacobian_matrix, contravariant_vectors, inverse_jacobian = elements @@ -26,7 +26,7 @@ end # Interpolate tree_node_coordinates to each quadrant at the nodes of the specified basis function calc_node_coordinates!(node_coordinates, - mesh::P4estMesh{2}, + mesh::Union{P4estMesh{2},T8codeMesh{2}}, basis::LobattoLegendreBasis) # Hanging nodes will cause holes in the mesh if its polydeg is higher # than the polydeg of the solver. diff --git a/src/solvers/dgsem_p4est/dg_2d.jl b/src/solvers/dgsem_p4est/dg_2d.jl index a6d3d6abae..c4cb01afe8 100644 --- a/src/solvers/dgsem_p4est/dg_2d.jl +++ b/src/solvers/dgsem_p4est/dg_2d.jl @@ -7,7 +7,7 @@ # The methods below are specialized on the mortar type # and called from the basic `create_cache` method at the top. -function create_cache(mesh::P4estMesh{2}, equations, mortar_l2::LobattoLegendreMortarL2, uEltype) +function create_cache(mesh::Union{P4estMesh{2},T8codeMesh{2}}, equations, mortar_l2::LobattoLegendreMortarL2, uEltype) # TODO: Taal performance using different types MA2d = MArray{Tuple{nvariables(equations), nnodes(mortar_l2)}, uEltype, 2, @@ -58,7 +58,7 @@ end # We pass the `surface_integral` argument solely for dispatch function prolong2interfaces!(cache, u, - mesh::P4estMesh{2}, + mesh::Union{P4estMesh{2},T8codeMesh{2}}, equations, surface_integral, dg::DG) @unpack interfaces = cache index_range = eachnode(dg) @@ -109,7 +109,7 @@ end function calc_interface_flux!(surface_flux_values, - mesh::P4estMesh{2}, + mesh::Union{P4estMesh{2},T8codeMesh{2}}, nonconservative_terms, equations, surface_integral, dg::DG, cache) @unpack neighbor_ids, node_indices = cache.interfaces @@ -173,7 +173,7 @@ end # Inlined version of the interface flux computation for conservation laws @inline function calc_interface_flux!(surface_flux_values, - mesh::P4estMesh{2}, + mesh::Union{P4estMesh{2},T8codeMesh{2}}, nonconservative_terms::False, equations, surface_integral, dg::DG, cache, interface_index, normal_direction, @@ -194,7 +194,7 @@ end # Inlined version of the interface flux computation for equations with conservative and nonconservative terms @inline function calc_interface_flux!(surface_flux_values, - mesh::P4estMesh{2}, + mesh::Union{P4estMesh{2},T8codeMesh{2}}, nonconservative_terms::True, equations, surface_integral, dg::DG, cache, interface_index, normal_direction, @@ -229,7 +229,7 @@ end function prolong2boundaries!(cache, u, - mesh::P4estMesh{2}, + mesh::Union{P4estMesh{2},T8codeMesh{2}}, equations, surface_integral, dg::DG) @unpack boundaries = cache index_range = eachnode(dg) @@ -259,7 +259,7 @@ end function calc_boundary_flux!(cache, t, boundary_condition, boundary_indexing, - mesh::P4estMesh{2}, + mesh::Union{P4estMesh{2},T8codeMesh{2}}, equations, surface_integral, dg::DG) @unpack boundaries = cache @unpack surface_flux_values = cache.elements @@ -296,7 +296,7 @@ end # inlined version of the boundary flux calculation along a physical interface @inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition, - mesh::P4estMesh{2}, + mesh::Union{P4estMesh{2},T8codeMesh{2}}, nonconservative_terms::False, equations, surface_integral, dg::DG, cache, i_index, j_index, @@ -325,7 +325,7 @@ end # inlined version of the boundary flux with nonconservative terms calculation along a physical interface @inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition, - mesh::P4estMesh{2}, + mesh::Union{P4estMesh{2},T8codeMesh{2}}, nonconservative_terms::True, equations, surface_integral, dg::DG, cache, i_index, j_index, @@ -363,7 +363,7 @@ end function prolong2mortars!(cache, u, - mesh::P4estMesh{2}, equations, + mesh::Union{P4estMesh{2},T8codeMesh{2}}, equations, mortar_l2::LobattoLegendreMortarL2, surface_integral, dg::DGSEM) @unpack neighbor_ids, node_indices = cache.mortars @@ -427,7 +427,7 @@ end function calc_mortar_flux!(surface_flux_values, - mesh::P4estMesh{2}, + mesh::Union{P4estMesh{2},T8codeMesh{2}}, nonconservative_terms, equations, mortar_l2::LobattoLegendreMortarL2, surface_integral, dg::DG, cache) @@ -484,7 +484,7 @@ end # Inlined version of the mortar flux computation on small elements for conservation laws @inline function calc_mortar_flux!(fstar, - mesh::P4estMesh{2}, + mesh::Union{P4estMesh{2},T8codeMesh{2}}, nonconservative_terms::False, equations, surface_integral, dg::DG, cache, mortar_index, position_index, normal_direction, @@ -503,7 +503,7 @@ end # Inlined version of the mortar flux computation on small elements for equations with conservative and # nonconservative terms @inline function calc_mortar_flux!(fstar, - mesh::P4estMesh{2}, + mesh::Union{P4estMesh{2},T8codeMesh{2}}, nonconservative_terms::True, equations, surface_integral, dg::DG, cache, mortar_index, position_index, normal_direction, @@ -530,7 +530,7 @@ end @inline function mortar_fluxes_to_elements!(surface_flux_values, - mesh::P4estMesh{2}, equations, + mesh::Union{P4estMesh{2},T8codeMesh{2}}, equations, mortar_l2::LobattoLegendreMortarL2, dg::DGSEM, cache, mortar, fstar, u_buffer) @unpack neighbor_ids, node_indices = cache.mortars @@ -589,7 +589,7 @@ end function calc_surface_integral!(du, u, - mesh::P4estMesh{2}, + mesh::Union{P4estMesh{2},T8codeMesh{2}}, equations, surface_integral::SurfaceIntegralWeakForm, dg::DGSEM, cache) diff --git a/src/solvers/dgsem_structured/dg_2d.jl b/src/solvers/dgsem_structured/dg_2d.jl index 3a68bced40..0704758fe3 100644 --- a/src/solvers/dgsem_structured/dg_2d.jl +++ b/src/solvers/dgsem_structured/dg_2d.jl @@ -45,7 +45,7 @@ end @inline function weak_form_kernel!(du, u, - element, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, + element, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, nonconservative_terms::False, equations, dg::DGSEM, cache, alpha=true) # true * [some floating point value] == [exactly the same floating point value] @@ -81,7 +81,7 @@ end @inline function flux_differencing_kernel!(du, u, - element, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, + element, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, nonconservative_terms::False, equations, volume_flux, dg::DGSEM, cache, alpha=true) @unpack derivative_split = dg.basis @@ -189,7 +189,7 @@ end # "A provably entropy stable subcell shock capturing approach for high order split form DG for the compressible Euler equations" # [arXiv: 2008.12044v2](https://arxiv.org/pdf/2008.12044) @inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, u, - mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, + mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, nonconservative_terms::False, equations, volume_flux_fv, dg::DGSEM, element, cache) @unpack contravariant_vectors = cache.elements @@ -543,7 +543,7 @@ end function apply_jacobian!(du, - mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, + mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, equations, dg::DG, cache) @unpack inverse_jacobian = cache.elements diff --git a/src/solvers/dgsem_t8code/containers.jl b/src/solvers/dgsem_t8code/containers.jl new file mode 100644 index 0000000000..294e91fbbe --- /dev/null +++ b/src/solvers/dgsem_t8code/containers.jl @@ -0,0 +1,329 @@ +mutable struct T8codeElementContainer{NDIMS, RealT<:Real, uEltype<:Real, NDIMSP1, NDIMSP2, NDIMSP3} <: AbstractContainer + # Physical coordinates at each node + node_coordinates :: Array{RealT, NDIMSP2} # [orientation, node_i, node_j, node_k, element] + # Jacobian matrix of the transformation + # [jacobian_i, jacobian_j, node_i, node_j, node_k, element] where jacobian_i is the first index of the Jacobian matrix,... + jacobian_matrix :: Array{RealT, NDIMSP3} + # Contravariant vectors, scaled by J, in Kopriva's blue book called Ja^i_n (i index, n dimension) + contravariant_vectors :: Array{RealT, NDIMSP3} # [dimension, index, node_i, node_j, node_k, element] + # 1/J where J is the Jacobian determinant (determinant of Jacobian matrix) + inverse_jacobian :: Array{RealT, NDIMSP1} # [node_i, node_j, node_k, element] + # Buffer for calculated surface flux + surface_flux_values :: Array{uEltype, NDIMSP2} # [variable, i, j, direction, element] + + # internal `resize!`able storage + _node_coordinates :: Vector{RealT} + _jacobian_matrix :: Vector{RealT} + _contravariant_vectors :: Vector{RealT} + _inverse_jacobian :: Vector{RealT} + _surface_flux_values :: Vector{uEltype} +end + +mutable struct T8codeInterfaceContainer{NDIMS, uEltype<:Real, NDIMSP2} <: AbstractContainer + u :: Array{uEltype, NDIMSP2} # [primary/secondary, variable, i, j, interface] + neighbor_ids :: Matrix{Int} # [primary/secondary, interface] + node_indices :: Matrix{NTuple{NDIMS, Symbol}} # [primary/secondary, interface] + + # Internal `resize!`able storage. + _u :: Vector{uEltype} + _neighbor_ids :: Vector{Int} + _node_indices :: Vector{NTuple{NDIMS, Symbol}} +end + +mutable struct T8codeMortarContainer{NDIMS, uEltype<:Real, NDIMSP1, NDIMSP3} <: AbstractContainer + u :: Array{uEltype, NDIMSP3} # [small/large side, variable, position, i, j, mortar] + neighbor_ids :: Matrix{Int} # [position, mortar] + node_indices :: Matrix{NTuple{NDIMS, Symbol}} # [small/large, mortar] + + # internal `resize!`able storage + _u :: Vector{uEltype} + _neighbor_ids :: Vector{Int} + _node_indices :: Vector{NTuple{NDIMS, Symbol}} +end + +mutable struct T8codeBoundaryContainer{NDIMS, uEltype<:Real, NDIMSP1} <: AbstractContainer + u :: Array{uEltype, NDIMSP1} # [variables, i, j, boundary] + neighbor_ids :: Vector{Int} # [boundary] + node_indices :: Vector{NTuple{NDIMS, Symbol}} # [boundary] + name :: Vector{Symbol} # [boundary] + + # Internal `resize!`able storage. + _u :: Vector{uEltype} +end + +# ============================================================================ # +# ============================================================================ # + +@inline nelements(elements::T8codeElementContainer) = size(elements.node_coordinates, ndims(elements) + 2) +@inline Base.ndims(::T8codeElementContainer{NDIMS}) where NDIMS = NDIMS +@inline Base.eltype(::T8codeElementContainer{NDIMS, RealT, uEltype}) where {NDIMS, RealT, uEltype} = uEltype + +@inline ninterfaces(interfaces::T8codeInterfaceContainer) = size(interfaces.neighbor_ids, 2) +@inline Base.ndims(::T8codeInterfaceContainer{NDIMS}) where NDIMS = NDIMS + +@inline nboundaries(boundaries::T8codeBoundaryContainer) = length(boundaries.neighbor_ids) +@inline Base.ndims(::T8codeBoundaryContainer{NDIMS}) where NDIMS = NDIMS + +@inline nmortars(mortars::T8codeMortarContainer) = size(mortars.neighbor_ids, 2) +@inline Base.ndims(::T8codeMortarContainer{NDIMS}) where NDIMS = NDIMS + +# ============================================================================ # +# ============================================================================ # + +function reinitialize_containers!(mesh::T8codeMesh, equations, dg::DGSEM, cache) + + # Re-initialize elements container. + @unpack elements = cache + resize!(elements, ncells(mesh)) + init_elements!(elements, mesh, dg.basis) + + required = count_required_surfaces(mesh) + + # Resize interfaces container. + @unpack interfaces = cache + resize!(interfaces, required.interfaces) + + # Resize mortars container. + @unpack mortars = cache + resize!(mortars, required.mortars) + + # Resize boundaries container. + @unpack boundaries = cache + resize!(boundaries, required.boundaries) + + # Re-initialize containers together to reduce + # the number of iterations over the mesh in `t8code`. + # init_surfaces!(elements,interfaces, mortars, boundaries, mesh) + + trixi_t8_fill_mesh_info(mesh.forest, elements, interfaces, mortars, boundaries, mesh.boundary_names) + + return nothing +end + +# ============================================================================ # +# ============================================================================ # + +# Only one-dimensional `Array`s are `resize!`able in Julia. +# Hence, we use `Vector`s as internal storage and `resize!` +# them whenever needed. Then, we reuse the same memory by +# `unsafe_wrap`ping multi-dimensional `Array`s around the +# internal storage. +function Base.resize!(elements::T8codeElementContainer, capacity) + @unpack _node_coordinates, _jacobian_matrix, _contravariant_vectors, + _inverse_jacobian, _surface_flux_values = elements + + n_dims = ndims(elements) + n_nodes = size(elements.node_coordinates, 2) + n_variables = size(elements.surface_flux_values, 1) + + resize!(_node_coordinates, n_dims * n_nodes^n_dims * capacity) + elements.node_coordinates = unsafe_wrap(Array, pointer(_node_coordinates), + (n_dims, ntuple(_ -> n_nodes, n_dims)..., capacity)) + + resize!(_jacobian_matrix, n_dims^2 * n_nodes^n_dims * capacity) + elements.jacobian_matrix = unsafe_wrap(Array, pointer(_jacobian_matrix), + (n_dims, n_dims, ntuple(_ -> n_nodes, n_dims)..., capacity)) + + resize!(_contravariant_vectors, length(_jacobian_matrix)) + elements.contravariant_vectors = unsafe_wrap(Array, pointer(_contravariant_vectors), + size(elements.jacobian_matrix)) + + resize!(_inverse_jacobian, n_nodes^n_dims * capacity) + elements.inverse_jacobian = unsafe_wrap(Array, pointer(_inverse_jacobian), + (ntuple(_ -> n_nodes, n_dims)..., capacity)) + + resize!(_surface_flux_values, + n_variables * n_nodes^(n_dims-1) * (n_dims*2) * capacity) + elements.surface_flux_values = unsafe_wrap(Array, pointer(_surface_flux_values), + (n_variables, ntuple(_ -> n_nodes, n_dims-1)..., n_dims*2, capacity)) + + return nothing +end + +# See explanation of Base.resize! for the element container. +function Base.resize!(interfaces::T8codeInterfaceContainer, capacity) + @unpack _u, _neighbor_ids, _node_indices = interfaces + + n_dims = ndims(interfaces) + n_nodes = size(interfaces.u, 3) + n_variables = size(interfaces.u, 2) + + resize!(_u, 2 * n_variables * n_nodes^(n_dims-1) * capacity) + interfaces.u = unsafe_wrap(Array, pointer(_u), + (2, n_variables, ntuple(_ -> n_nodes, n_dims-1)..., capacity)) + + resize!(_neighbor_ids, 2 * capacity) + interfaces.neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids), (2, capacity)) + + resize!(_node_indices, n_dims * capacity) + interfaces.node_indices = unsafe_wrap(Array, pointer(_node_indices), (n_dims, capacity)) + + return nothing +end + +# See explanation of Base.resize! for the element container. +function Base.resize!(mortars::T8codeMortarContainer, capacity) + @unpack _u, _neighbor_ids, _node_indices = mortars + + n_dims = ndims(mortars) + n_nodes = size(mortars.u, 4) + n_variables = size(mortars.u, 2) + + resize!(_u, 2 * n_variables * 2^(n_dims-1) * n_nodes^(n_dims-1) * capacity) + mortars.u = unsafe_wrap(Array, pointer(_u), + (2, n_variables, 2^(n_dims-1), ntuple(_ -> n_nodes, n_dims-1)..., capacity)) + + resize!(_neighbor_ids, (2^(n_dims-1) + 1) * capacity) + mortars.neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids), + (2^(n_dims-1) + 1, capacity)) + + resize!(_node_indices, n_dims * capacity) + mortars.node_indices = unsafe_wrap(Array, pointer(_node_indices), (n_dims, capacity)) + + return nothing +end + +# See explanation of Base.resize! for the element container. +function Base.resize!(boundaries::T8codeBoundaryContainer, capacity) + @unpack _u, neighbor_ids, node_indices, name = boundaries + + n_dims = ndims(boundaries) + n_nodes = size(boundaries.u, 2) + n_variables = size(boundaries.u, 1) + + resize!(_u, n_variables * n_nodes^(n_dims-1) * capacity) + boundaries.u = unsafe_wrap(Array, pointer(_u), + (n_variables, ntuple(_ -> n_nodes, n_dims-1)..., capacity)) + + resize!(neighbor_ids, capacity) + + resize!(node_indices, capacity) + + resize!(name, capacity) + + return nothing +end + +# ============================================================================ # +# ============================================================================ # + +# Create element container and initialize element data. +function init_elements(mesh::T8codeMesh{NDIMS,RealT}, equations, + basis, ::Type{uEltype}) where {NDIMS, RealT<:Real, uEltype<:Real} + nelements = ncells(mesh) + + _node_coordinates = Vector{RealT}(undef, NDIMS * nnodes(basis)^NDIMS * nelements) + + node_coordinates = unsafe_wrap(Array, pointer(_node_coordinates), + (NDIMS, ntuple(_ -> nnodes(basis), NDIMS)..., nelements)) + + _jacobian_matrix = Vector{RealT}(undef, NDIMS^2 * nnodes(basis)^NDIMS * nelements) + jacobian_matrix = unsafe_wrap(Array, pointer(_jacobian_matrix), + (NDIMS, NDIMS, ntuple(_ -> nnodes(basis), NDIMS)..., nelements)) + + _contravariant_vectors = similar(_jacobian_matrix) + contravariant_vectors = unsafe_wrap(Array, pointer(_contravariant_vectors), + size(jacobian_matrix)) + + _inverse_jacobian = Vector{RealT}(undef, nnodes(basis)^NDIMS * nelements) + inverse_jacobian = unsafe_wrap(Array, pointer(_inverse_jacobian), + (ntuple(_ -> nnodes(basis), NDIMS)..., nelements)) + + _surface_flux_values = Vector{uEltype}(undef, + nvariables(equations) * nnodes(basis)^(NDIMS-1) * (NDIMS*2) * nelements) + surface_flux_values = unsafe_wrap(Array, pointer(_surface_flux_values), + (nvariables(equations), ntuple(_ -> nnodes(basis), NDIMS-1)..., NDIMS*2, nelements)) + + elements = T8codeElementContainer{NDIMS, RealT, uEltype, NDIMS+1, NDIMS+2, NDIMS+3}( + node_coordinates, jacobian_matrix, contravariant_vectors, + inverse_jacobian, surface_flux_values, + _node_coordinates, _jacobian_matrix, _contravariant_vectors, + _inverse_jacobian, _surface_flux_values) + + # Implementation is found in 'containers_{2,3}d.jl'. + init_elements!(elements, mesh, basis) + return elements +end + +# Create interface container and initialize interface data. +function init_interfaces(mesh::T8codeMesh, equations, basis, elements) + NDIMS = ndims(elements) + uEltype = eltype(elements) + + n_interfaces = ninterfaces(mesh) + + _u = Vector{uEltype}(undef, 2 * nvariables(equations) * nnodes(basis)^(NDIMS-1) * n_interfaces) + u = unsafe_wrap(Array, pointer(_u), + (2, nvariables(equations), ntuple(_ -> nnodes(basis), NDIMS-1)..., n_interfaces)) + + _neighbor_ids = Vector{Int}(undef, 2 * n_interfaces) + neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids), (2, n_interfaces)) + + _node_indices = Vector{NTuple{NDIMS, Symbol}}(undef, NDIMS * n_interfaces) + node_indices = unsafe_wrap(Array, pointer(_node_indices), (NDIMS, n_interfaces)) + + interfaces = T8codeInterfaceContainer{NDIMS, uEltype, NDIMS+2}(u, neighbor_ids, node_indices, + _u, _neighbor_ids, _node_indices) + + return interfaces +end + +# Create mortar container and initialize mortar data. +function init_mortars(mesh::T8codeMesh, equations, basis, elements) + NDIMS = ndims(elements) + uEltype = eltype(elements) + + n_mortars = nmortars(mesh) + + _u = Vector{uEltype}(undef, + 2 * nvariables(equations) * 2^(NDIMS-1) * nnodes(basis)^(NDIMS-1) * n_mortars) + u = unsafe_wrap(Array, pointer(_u), + (2, nvariables(equations), 2^(NDIMS-1), ntuple(_ -> nnodes(basis), NDIMS-1)..., n_mortars)) + + _neighbor_ids = Vector{Int}(undef, (2^(NDIMS-1) + 1) * n_mortars) + neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids), (2^(NDIMS-1) + 1, n_mortars)) + + _node_indices = Vector{NTuple{NDIMS, Symbol}}(undef, NDIMS * n_mortars) + node_indices = unsafe_wrap(Array, pointer(_node_indices), (NDIMS, n_mortars)) + + mortars = T8codeMortarContainer{NDIMS, uEltype, NDIMS+1, NDIMS+3}(u, neighbor_ids, node_indices, + _u, _neighbor_ids, _node_indices) + + return mortars +end + +# Create interface container and initialize interface data in `elements`. +function init_boundaries(mesh::T8codeMesh, equations, basis, elements) + NDIMS = ndims(elements) + uEltype = eltype(elements) + + # Initialize container. + n_boundaries = nboundaries(mesh) + + _u = Vector{uEltype}(undef, nvariables(equations) * nnodes(basis)^(NDIMS-1) * n_boundaries) + u = unsafe_wrap(Array, pointer(_u), + (nvariables(equations), ntuple(_ -> nnodes(basis), NDIMS-1)..., n_boundaries)) + + neighbor_ids = Vector{Int}(undef, n_boundaries) + node_indices = Vector{NTuple{NDIMS, Symbol}}(undef, n_boundaries) + names = Vector{Symbol}(undef, n_boundaries) + + boundaries = T8codeBoundaryContainer{NDIMS, uEltype, NDIMS+1}(u, neighbor_ids, + node_indices, names, _u) + + return boundaries +end + +# ============================================================================ # +# ============================================================================ # + +function count_required_surfaces(mesh::T8codeMesh) + + counts = trixi_t8_count_interfaces(mesh.forest) + + mesh.nmortars = counts.mortars + mesh.ninterfaces = counts.interfaces + mesh.nboundaries = counts.boundaries + + return counts +end diff --git a/src/solvers/dgsem_t8code/containers_2d.jl b/src/solvers/dgsem_t8code/containers_2d.jl new file mode 100644 index 0000000000..944c806030 --- /dev/null +++ b/src/solvers/dgsem_t8code/containers_2d.jl @@ -0,0 +1,55 @@ +@muladd begin + +# Interpolate tree_node_coordinates to each quadrant at the specified nodes. +function calc_node_coordinates!(node_coordinates, + mesh::T8codeMesh{2}, + nodes::AbstractVector) + # We use `StrideArray`s here since these buffers are used in performance-critical + # places and the additional information passed to the compiler makes them faster + # than native `Array`s. + tmp1 = StrideArray(undef, real(mesh), + StaticInt(2), static_length(nodes), static_length(mesh.nodes)) + matrix1 = StrideArray(undef, real(mesh), + static_length(nodes), static_length(mesh.nodes)) + matrix2 = similar(matrix1) + baryweights_in = barycentric_weights(mesh.nodes) + + num_local_trees = t8_forest_get_num_local_trees(mesh.forest) + + current_index = 0 + for itree = 0:num_local_trees-1 + + tree_class = t8_forest_get_tree_class(mesh.forest, itree); + eclass_scheme = t8_forest_get_eclass_scheme(mesh.forest, tree_class); + num_elements_in_tree = t8_forest_get_tree_num_elements(mesh.forest, itree); + + for ielement = 0:num_elements_in_tree-1 + element = t8_forest_get_element_in_tree(mesh.forest, itree, ielement) + element_level = t8_element_level(eclass_scheme, element) + + # TODO: Make this more general. + element_length = t8_quad_len(element_level) / t8_quad_root_len + + element_coords = Array{Float64}(undef,3) + t8_element_vertex_reference_coords(eclass_scheme, element, 0, pointer(element_coords)) + + nodes_out_x = 2 * (element_length * 1/2 * (nodes .+ 1) .+ element_coords[1]) .- 1 + nodes_out_y = 2 * (element_length * 1/2 * (nodes .+ 1) .+ element_coords[2]) .- 1 + + polynomial_interpolation_matrix!(matrix1, mesh.nodes, nodes_out_x, baryweights_in) + polynomial_interpolation_matrix!(matrix2, mesh.nodes, nodes_out_y, baryweights_in) + + multiply_dimensionwise!( + view(node_coordinates, :, :, :, current_index += 1), + matrix1, matrix2, + view(mesh.tree_node_coordinates, :, :, :, itree+1), + tmp1 + ) + # end TODO + end + end + + return node_coordinates +end + +end # @muladd diff --git a/src/solvers/dgsem_t8code/dg.jl b/src/solvers/dgsem_t8code/dg.jl new file mode 100644 index 0000000000..9b5084aaed --- /dev/null +++ b/src/solvers/dgsem_t8code/dg.jl @@ -0,0 +1,32 @@ +@muladd begin + +# This method is called when a SemidiscretizationHyperbolic is constructed. +# It constructs the basic `cache` used throughout the simulation to compute +# the RHS etc. +function create_cache(mesh::T8codeMesh, equations::AbstractEquations, dg::DG, ::Any, ::Type{uEltype}) where {uEltype<:Real} + # Make sure to balance the 't8code' before creating any containers + # in case someone has tampered with the 't8code' after creating the mesh. + # balance!(mesh) + + _ = count_required_surfaces(mesh) + + elements = init_elements(mesh, equations, dg.basis, uEltype) + interfaces = init_interfaces(mesh, equations, dg.basis, elements) + boundaries = init_boundaries(mesh, equations, dg.basis, elements) + mortars = init_mortars(mesh, equations, dg.basis, elements) + + trixi_t8_fill_mesh_info(mesh.forest, elements, interfaces, mortars, boundaries, mesh.boundary_names) + + cache = (; elements, interfaces, boundaries, mortars) + + # Add specialized parts of the cache required to compute the volume integral etc. + cache = (; cache..., create_cache(mesh, equations, dg.volume_integral, dg, uEltype)...) + cache = (; cache..., create_cache(mesh, equations, dg.mortar, uEltype)...) + + return cache +end + +include("containers.jl") +include("containers_2d.jl") + +end # @muladd diff --git a/src/solvers/dgsem_tree/dg_2d.jl b/src/solvers/dgsem_tree/dg_2d.jl index d7f1463fde..657a92a441 100644 --- a/src/solvers/dgsem_tree/dg_2d.jl +++ b/src/solvers/dgsem_tree/dg_2d.jl @@ -36,13 +36,13 @@ end # The methods below are specialized on the volume integral type # and called from the basic `create_cache` method at the top. -function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, +function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, equations, volume_integral::VolumeIntegralFluxDifferencing, dg::DG, uEltype) NamedTuple() end -function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, equations, +function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, equations, volume_integral::VolumeIntegralShockCapturingHG, dg::DG, uEltype) element_ids_dg = Int[] element_ids_dgfv = Int[] @@ -64,7 +64,7 @@ function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMe end -function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, equations, +function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, equations, volume_integral::VolumeIntegralPureLGLFiniteVolume, dg::DG, uEltype) A3dp1_x = Array{uEltype, 3} @@ -80,7 +80,7 @@ end # The methods below are specialized on the mortar type # and called from the basic `create_cache` method at the top. -function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, +function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, equations, mortar_l2::LobattoLegendreMortarL2, uEltype) # TODO: Taal performance using different types MA2d = MArray{Tuple{nvariables(equations), nnodes(mortar_l2)}, uEltype, 2, nvariables(equations) * nnodes(mortar_l2)} @@ -98,7 +98,7 @@ end # TODO: Taal discuss/refactor timer, allowing users to pass a custom timer? function rhs!(du, u, t, - mesh::Union{TreeMesh{2}, P4estMesh{2}}, equations, + mesh::Union{TreeMesh{2}, P4estMesh{2}, T8codeMesh{2}}, equations, initial_condition, boundary_conditions, source_terms, dg::DG, cache) # Reset du @@ -155,7 +155,7 @@ end function calc_volume_integral!(du, u, - mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, + mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, nonconservative_terms, equations, volume_integral::VolumeIntegralWeakForm, dg::DGSEM, cache) @@ -200,7 +200,7 @@ end # mapping terms, stored in `cache.elements.contravariant_vectors`, is peeled apart # from the evaluation of the physical fluxes in each Cartesian direction function calc_volume_integral!(du, u, - mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, + mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, nonconservative_terms, equations, volume_integral::VolumeIntegralFluxDifferencing, dg::DGSEM, cache) @@ -288,7 +288,7 @@ end # TODO: Taal dimension agnostic function calc_volume_integral!(du, u, - mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, + mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, nonconservative_terms, equations, volume_integral::VolumeIntegralShockCapturingHG, dg::DGSEM, cache) @@ -346,7 +346,7 @@ end @inline function fv_kernel!(du, u, - mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, + mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, nonconservative_terms, equations, volume_flux_fv, dg::DGSEM, cache, element, alpha=true) @unpack fstar1_L_threaded, fstar1_R_threaded, fstar2_L_threaded, fstar2_R_threaded = cache diff --git a/src/solvers/dgsem_tree/indicators_2d.jl b/src/solvers/dgsem_tree/indicators_2d.jl index fe1c590815..32aa725133 100644 --- a/src/solvers/dgsem_tree/indicators_2d.jl +++ b/src/solvers/dgsem_tree/indicators_2d.jl @@ -108,7 +108,7 @@ end # Diffuse alpha values by setting each alpha to at least 50% of neighboring elements' alpha -function apply_smoothing!(mesh::Union{TreeMesh{2}, P4estMesh{2}}, alpha, alpha_tmp, dg, cache) +function apply_smoothing!(mesh::Union{TreeMesh{2}, P4estMesh{2}, T8codeMesh{2}}, alpha, alpha_tmp, dg, cache) # Copy alpha values such that smoothing is indpedenent of the element access order alpha_tmp .= alpha diff --git a/src/solvers/dgsem_unstructured/dg_2d.jl b/src/solvers/dgsem_unstructured/dg_2d.jl index acb89bc452..41b43b1f53 100644 --- a/src/solvers/dgsem_unstructured/dg_2d.jl +++ b/src/solvers/dgsem_unstructured/dg_2d.jl @@ -291,7 +291,7 @@ end # TODO: Taal dimension agnostic function calc_boundary_flux!(cache, t, boundary_condition::BoundaryConditionPeriodic, - mesh::Union{UnstructuredMesh2D, P4estMesh}, + mesh::Union{UnstructuredMesh2D, P4estMesh, T8codeMesh}, equations, surface_integral, dg::DG) @assert isempty(eachboundary(dg, cache)) end @@ -299,7 +299,7 @@ end # Function barrier for type stability function calc_boundary_flux!(cache, t, boundary_conditions, - mesh::Union{UnstructuredMesh2D, P4estMesh}, + mesh::Union{UnstructuredMesh2D, P4estMesh, T8codeMesh}, equations, surface_integral, dg::DG) @unpack boundary_condition_types, boundary_indices = boundary_conditions @@ -313,7 +313,7 @@ end # in a type-stable way using "lispy tuple programming". function calc_boundary_flux_by_type!(cache, t, BCs::NTuple{N,Any}, BC_indices::NTuple{N,Vector{Int}}, - mesh::Union{UnstructuredMesh2D, P4estMesh}, + mesh::Union{UnstructuredMesh2D, P4estMesh, T8codeMesh}, equations, surface_integral, dg::DG) where {N} # Extract the boundary condition type and index vector boundary_condition = first(BCs) @@ -336,7 +336,7 @@ end # terminate the type-stable iteration over tuples function calc_boundary_flux_by_type!(cache, t, BCs::Tuple{}, BC_indices::Tuple{}, - mesh::Union{UnstructuredMesh2D, P4estMesh}, + mesh::Union{UnstructuredMesh2D, P4estMesh, T8codeMesh}, equations, surface_integral, dg::DG) nothing end diff --git a/test/test_t8code_2d.jl b/test/test_t8code_2d.jl new file mode 100644 index 0000000000..e39ae1cae3 --- /dev/null +++ b/test/test_t8code_2d.jl @@ -0,0 +1,173 @@ +module TestExamplesT8codeMesh2D + +using Test +using Trixi + +include("test_trixi.jl") + +EXAMPLES_DIR = joinpath(examples_dir(), "t8code_2d_dgsem") + +# Start with a clean environment: remove Trixi.jl output directory if it exists +outdir = "out" +isdir(outdir) && rm(outdir, recursive=true) +mkdir(outdir) + +@testset "T8codeMesh2D" begin + @trixi_testset "elixir_advection_basic.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_basic.jl"), + # Expected errors are exactly the same as with TreeMesh! + l2 = [8.311947673061856e-6], + linf = [6.627000273229378e-5]) + end + + @trixi_testset "elixir_advection_nonconforming_flag.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_nonconforming_flag.jl"), + l2 = [3.198940059144588e-5], + linf = [0.00030636069494005547]) + + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000 + end + end +# +# @trixi_testset "elixir_advection_unstructured_flag.jl" begin +# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_unstructured_flag.jl"), +# l2 = [0.0005379687442422346], +# linf = [0.007438525029884735]) +# end +# +# @trixi_testset "elixir_advection_amr_solution_independent.jl" begin +# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_amr_solution_independent.jl"), +# # Expected errors are exactly the same as with StructuredMesh! +# l2 = [4.949660644033807e-5], +# linf = [0.0004867846262313763], +# coverage_override = (maxiters=6,)) +# end +# +# @trixi_testset "elixir_advection_amr_unstructured_flag.jl" begin +# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_amr_unstructured_flag.jl"), +# l2 = [0.0012766060609964525], +# linf = [0.01750280631586159], +# coverage_override = (maxiters=6,)) +# end +# +# @trixi_testset "elixir_advection_restart.jl" begin +# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_restart.jl"), +# l2 = [4.507575525876275e-6], +# linf = [6.21489667023134e-5]) +# end +# +# @trixi_testset "elixir_euler_source_terms_nonconforming_unstructured_flag.jl" begin +# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_source_terms_nonconforming_unstructured_flag.jl"), +# l2 = [0.0034516244508588046, 0.0023420334036925493, 0.0024261923964557187, 0.004731710454271893], +# linf = [0.04155789011775046, 0.024772109862748914, 0.03759938693042297, 0.08039824959535657]) +# end +# +# @trixi_testset "elixir_euler_free_stream.jl" begin +# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_free_stream.jl"), +# l2 = [2.063350241405049e-15, 1.8571016296925367e-14, 3.1769447886391905e-14, 1.4104095258528071e-14], +# linf = [1.9539925233402755e-14, 2e-12, 4.8e-12, 4e-12], +# atol = 2.0e-12, # required to make CI tests pass on macOS +# ) +# end +# +# @trixi_testset "elixir_euler_shockcapturing_ec.jl" begin +# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_shockcapturing_ec.jl"), +# l2 = [9.53984675e-02, 1.05633455e-01, 1.05636158e-01, 3.50747237e-01], +# linf = [2.94357464e-01, 4.07893014e-01, 3.97334516e-01, 1.08142520e+00], +# tspan = (0.0, 1.0)) +# end +# +# @trixi_testset "elixir_euler_sedov.jl" begin +# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_sedov.jl"), +# l2 = [3.76149952e-01, 2.46970327e-01, 2.46970327e-01, 1.28889042e+00], +# linf = [1.22139001e+00, 1.17742626e+00, 1.17742626e+00, 6.20638482e+00], +# tspan = (0.0, 0.3)) +# end +# +# @trixi_testset "elixir_euler_blast_wave_amr.jl" begin +# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_blast_wave_amr.jl"), +# l2 = [6.32183914e-01, 3.86914231e-01, 3.86869171e-01, 1.06575688e+00], +# linf = [2.76020890e+00, 2.32659890e+00, 2.32580837e+00, 2.15778188e+00], +# tspan = (0.0, 0.3), +# coverage_override = (maxiters=6,)) +# end +# +# @trixi_testset "elixir_euler_wall_bc_amr.jl" begin +# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_wall_bc_amr.jl"), +# l2 = [0.020291447969983396, 0.017479614254319948, 0.011387644425613437, 0.0514420126021293], +# linf = [0.3582779022370579, 0.32073537890751663, 0.221818049107692, 0.9209559420400415], +# tspan = (0.0, 0.15)) +# end +# +# @trixi_testset "elixir_euler_forward_step_amr.jl" begin +# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_forward_step_amr.jl"), +# l2 = [0.004194875320833303, 0.003785140699353966, 0.0013696609105790351, 0.03265268616046424], +# linf = [2.0585399781442852, 2.213428805506876, 3.862362410419163, 17.75187237459251], +# tspan = (0.0, 0.0001), +# rtol = 1.0e-7, +# skip_coverage=true) +# end +# +# @trixi_testset "elixir_euler_double_mach_amr.jl" begin +# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_double_mach_amr.jl"), +# l2 = [0.051359355290192046, 0.4266034859911273, 0.2438304855475594, 4.11487176105527], +# linf = [6.902000373057003, 53.95714139820832, 24.241610279839758, 561.0630401858057], +# tspan = (0.0, 0.0001), +# skip_coverage=true) +# end +# +# @trixi_testset "elixir_euler_supersonic_cylinder.jl" begin +# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_supersonic_cylinder.jl"), +# l2 = [0.026798021911954406, 0.05118546368109259, 0.03206703583774831, 0.19680026567208672], +# linf = [3.653905721692421, 4.285035711361009, 6.8544353186357645, 31.748244912257533], +# tspan = (0.0, 0.001), +# skip_coverage=true) +# end +# +# @trixi_testset "elixir_eulergravity_convergence.jl" begin +# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_eulergravity_convergence.jl"), +# l2 = [0.00024871265138964204, 0.0003370077102132591, 0.0003370077102131964, 0.0007231525513793697], +# linf = [0.0015813032944647087, 0.0020494288423820173, 0.0020494288423824614, 0.004793821195083758], +# tspan = (0.0, 0.1)) +# end +# +# @trixi_testset "elixir_shallowwater_source_terms.jl" begin +# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_source_terms.jl"), +# l2 = [9.168126407325352e-5, 0.0009795410115453788, 0.002546408320320785, 3.941189812642317e-6], +# linf = [0.0009903782521019089, 0.0059752684687262025, 0.010941106525454103, 1.2129488214718265e-5], +# tspan = (0.0, 0.1)) +# end +# +# @trixi_testset "elixir_mhd_alfven_wave.jl" begin +# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_mhd_alfven_wave.jl"), +# l2 = [1.0513414461545583e-5, 1.0517900957166411e-6, 1.0517900957304043e-6, 1.511816606372376e-6, +# 1.0443997728645063e-6, 7.879639064990798e-7, 7.879639065049896e-7, 1.0628631669056271e-6, +# 4.3382328912336153e-7], +# linf = [4.255466285174592e-5, 1.0029706745823264e-5, 1.0029706747467781e-5, 1.2122265939010224e-5, +# 5.4791097160444835e-6, 5.18922042269665e-6, 5.189220422141538e-6, 9.552667261422676e-6, +# 1.4237578427628152e-6]) +# end +# +# @trixi_testset "elixir_mhd_rotor.jl" begin +# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_mhd_rotor.jl"), +# l2 = [0.4552084651735862, 0.8918048264575757, 0.832471223081887, 0.0, +# 0.9801264164951583, 0.10475690769435382, 0.1555132409149897, 0.0, +# 2.0597079362763556e-5], +# linf = [10.194181233788775, 18.25472397868819, 10.031307436191334, 0.0, +# 19.647239392277378, 1.3938810140985936, 1.8724965294853084, 0.0, +# 0.0016290067532561904], +# tspan = (0.0, 0.02)) +# end + +end + +# Clean up afterwards: delete Trixi.jl output directory +@test_nowarn rm(outdir, recursive=true) + +end # module From 2cac6c9228e91714021fb91e5190b473463e0b14 Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Fri, 5 May 2023 16:41:54 +0200 Subject: [PATCH 002/128] Delete t8code_2d_dgsem --- examples/dgmulti_2d/t8code_2d_dgsem | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 examples/dgmulti_2d/t8code_2d_dgsem diff --git a/examples/dgmulti_2d/t8code_2d_dgsem b/examples/dgmulti_2d/t8code_2d_dgsem deleted file mode 100644 index e69de29bb2..0000000000 From 1bb2061972906620e5d57c8b8fa944de7edef939 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Wed, 17 May 2023 17:13:19 +0200 Subject: [PATCH 003/128] Added new examples and tests. Testing updates for T8code.jl. --- .../elixir_advection_unstructured_flag.jl | 87 +++++++++++ ..._euler_kelvin_helmholtz_instability_amr.jl | 8 +- src/auxiliary/t8code.jl | 2 +- src/meshes/t8code_mesh.jl | 137 +++++++++++++++++- test/test_t8code_2d.jl | 14 +- 5 files changed, 234 insertions(+), 14 deletions(-) create mode 100644 examples/t8code_2d_dgsem/elixir_advection_unstructured_flag.jl diff --git a/examples/t8code_2d_dgsem/elixir_advection_unstructured_flag.jl b/examples/t8code_2d_dgsem/elixir_advection_unstructured_flag.jl new file mode 100644 index 0000000000..ba7274235b --- /dev/null +++ b/examples/t8code_2d_dgsem/elixir_advection_unstructured_flag.jl @@ -0,0 +1,87 @@ +using Downloads: download +using OrdinaryDiffEq +using Trixi + +############################################################################### +# Semidiscretization of the linear advection equation. + +advection_velocity = (0.2, -0.7) +equations = LinearScalarAdvectionEquation2D(advection_velocity) + +initial_condition = initial_condition_convergence_test + +boundary_condition = BoundaryConditionDirichlet(initial_condition) +boundary_conditions = Dict( + :all => boundary_condition +) + +# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux. +solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) + +# Deformed rectangle that looks like a waving flag, +# lower and upper faces are sinus curves, left and right are vertical lines. +f1(s) = SVector(-1.0, s - 1.0) +f2(s) = SVector( 1.0, s + 1.0) +f3(s) = SVector(s, -1.0 + sin(0.5 * pi * s)) +f4(s) = SVector(s, 1.0 + sin(0.5 * pi * s)) +faces = (f1, f2, f3, f4) + +Trixi.validate_faces(faces) +mapping_flag = Trixi.transfinite_mapping(faces) + +# Unstructured mesh with 24 cells of the square domain [-1, 1]^n. +mesh_file = joinpath(@__DIR__, "square_unstructured_2.inp") +isfile(mesh_file) || download("https://gist.githubusercontent.com/efaulhaber/63ff2ea224409e55ee8423b3a33e316a/raw/7db58af7446d1479753ae718930741c47a3b79b7/square_unstructured_2.inp", + mesh_file) + +# INP mesh files are only support by p4est. Hence, we +# create a p4est connecvity object first from which +# we can create a t8code mesh. +conn = Trixi.read_inp_p4est(mesh_file,Val(2)) + +mesh = T8codeMesh{2}(conn, polydeg=3, + mapping=mapping_flag, + initial_refinement_level=2) + +# A semidiscretization collects data structures and functions for the spatial discretization. +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, boundary_conditions=boundary_conditions) + +############################################################################### +# ODE solvers, callbacks etc. + +# Create ODE problem with time span from 0.0 to 0.2. +tspan = (0.0, 0.2) +ode = semidiscretize(semi, tspan) + +# At the beginning of the main loop, the SummaryCallback prints a summary of +# the simulation setup and resets the timers. +summary_callback = SummaryCallback() + +# The AnalysisCallback allows to analyse the solution in regular intervals and +# prints the results. +analysis_callback = AnalysisCallback(semi, interval=100) + +# The SaveSolutionCallback allows to save the solution to a file in regular +# intervals. +# save_solution = SaveSolutionCallback(interval=100, +# solution_variables=cons2prim) + +# The StepsizeCallback handles the re-calculation of the maximum Δt after each +# time step. +stepsize_callback = StepsizeCallback(cfl=1.4) + +# Create a CallbackSet to collect all callbacks such that they can be passed to +# the ODE solver. +# callbacks = CallbackSet(summary_callback, analysis_callback, save_solution, stepsize_callback) +callbacks = CallbackSet(summary_callback, analysis_callback, stepsize_callback) + +############################################################################### +# Run the simulation. + +# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks. +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # Solve needs some value here but it will be overwritten by the stepsize_callback. + save_everystep=false, callback=callbacks); + +# Print the timer summary. +summary_callback() diff --git a/examples/t8code_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_amr.jl b/examples/t8code_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_amr.jl index 03261f5222..5d97f2fa97 100644 --- a/examples/t8code_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_amr.jl +++ b/examples/t8code_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_amr.jl @@ -47,11 +47,11 @@ indicator_sc = IndicatorHennemannGassner(equations, basis, alpha_smooth=true, variable=Trixi.density) -# volume_integral = VolumeIntegralShockCapturingHG(indicator_sc; -# volume_flux_dg=volume_flux, -# volume_flux_fv=surface_flux) +volume_integral = VolumeIntegralShockCapturingHG(indicator_sc; + volume_flux_dg=volume_flux, + volume_flux_fv=surface_flux) -volume_integral = VolumeIntegralWeakForm() +# volume_integral = VolumeIntegralWeakForm() solver = DGSEM(basis, surface_flux, volume_integral) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index 9674893da1..e3e572b427 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -24,7 +24,7 @@ function t8_free(ptr) end function finalize_t8code() - sc_finalize(); + # sc_finalize(); return nothing end diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 2d6e7ed300..ff600b36cb 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -189,6 +189,139 @@ function T8codeMesh(trees_per_dimension; polydeg, end +""" + T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh}, + mapping=nothing, polydeg=1, RealT=Float64, + initial_refinement_level=0, unsaved_changes=true) +Main mesh constructor for the `T8codeMesh` that imports an unstructured, conforming +mesh from an Abaqus mesh file (`.inp`). Each element of the conforming mesh parsed +from the `meshfile` is created as a [`p4est`](https://github.com/cburstedde/p4est) +tree datatype. +Note that the `mapping` and `polydeg` keyword arguments are only used by the `p4est_mesh_from_standard_abaqus` +function. The `p4est_mesh_from_hohqmesh_abaqus` function obtains the mesh `polydeg` directly from the `meshfile` +and constructs the transfinite mapping internally. +The particular strategy is selected according to the header present in the `meshfile` where +the constructor checks whether or not the `meshfile` was created with +[HOHQMesh.jl](https://github.com/trixi-framework/HOHQMesh.jl). +If the Abaqus file header is not present in the `meshfile` then the `P4estMesh` is created +with the function `p4est_mesh_from_standard_abaqus`. +The default keyword argument `initial_refinement_level=0` corresponds to a forest +where the number of trees is the same as the number of elements in the original `meshfile`. +Increasing the `initial_refinement_level` allows one to uniformly refine the base mesh given +in the `meshfile` to create a forest with more trees before the simulation begins. +For example, if a two-dimensional base mesh contains 25 elements then setting +`initial_refinement_level=1` creates an initial forest of `2^2 * 25 = 100` trees. +# Arguments +- `cmesh::Ptr{t8_cmesh}`: Pointer to a cmesh object. +- `mapping`: a function of `NDIMS` variables to describe the mapping that transforms + the imported mesh to the physical domain. Use `nothing` for the identity map. +- `polydeg::Integer`: polynomial degree used to store the geometry of the mesh. + The mapping will be approximated by an interpolation polynomial + of the specified degree for each tree. + The default of `1` creates an uncurved geometry. Use a higher value if the mapping + will curve the imported uncurved mesh. +- `RealT::Type`: the type that should be used for coordinates. +- `initial_refinement_level::Integer`: refine the mesh uniformly to this level before the simulation starts. +- `unsaved_changes::Bool`: if set to `true`, the mesh will be saved to a mesh file. +""" +function T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh}; + mapping=nothing, polydeg=1, RealT=Float64, + initial_refinement_level=0, unsaved_changes=true) where NDIMS + + @assert NDIMS == 2 # Only support for NDIMS = 2 yet. + + scheme = t8_scheme_new_default_cxx() + forest = t8_forest_new_uniform(cmesh,scheme,initial_refinement_level,0,mpi_comm()) + + basis = LobattoLegendreBasis(RealT, polydeg) + nodes = basis.nodes + + num_local_trees = t8_cmesh_get_num_local_trees(cmesh) + + tree_node_coordinates = Array{RealT, NDIMS+2}(undef, NDIMS, + ntuple(_ -> length(nodes), NDIMS)..., + num_local_trees) + + nodes_in = [-1.0, 1.0] + matrix = polynomial_interpolation_matrix(nodes_in, nodes) + data_in = Array{RealT, 3}(undef, 2, 2, 2) + tmp1 = zeros(RealT, 2, length(nodes), length(nodes_in)) + + for itree in 0:num_local_trees-1 + + veptr = t8_cmesh_get_tree_vertices(cmesh, itree) + verts = unsafe_wrap(Array,veptr,(3,1 << NDIMS)) + + u = verts[:,2] - verts[:,1] + v = verts[:,3] - verts[:,1] + w = [0.0,0.0,1.0] + + vol = dot(cross(u,v),w) + + if vol < 0.0 + @warn "Discovered negative volumes in `cmesh`: vol = $vol" + end + + # Tree vertices are stored in z-order. + @views data_in[:, 1, 1] .= verts[1:2,1] + @views data_in[:, 2, 1] .= verts[1:2,2] + @views data_in[:, 1, 2] .= verts[1:2,3] + @views data_in[:, 2, 2] .= verts[1:2,4] + + # Interpolate corner coordinates to specified nodes. + multiply_dimensionwise!( + view(tree_node_coordinates, :, :, :, itree+1), + matrix, matrix, + data_in, + tmp1 + ) + + end + + map_node_coordinates!(tree_node_coordinates, mapping) + + # There's no simple and generic way to distinguish boundaries. Name all of them :all. + boundary_names = fill(:all, 2 * NDIMS, num_local_trees) + + return T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, boundary_names, "", unsaved_changes) +end + +""" + T8codeMesh{NDIMS}(conn::Ptr{P4est.LibP4est.p4est_connectivity}, + mapping=nothing, polydeg=1, RealT=Float64, + initial_refinement_level=0, unsaved_changes=true) +Main mesh constructor for the `T8codeMesh` that imports an unstructured, conforming +mesh from an Abaqus mesh file (`.inp`). Each element of the conforming mesh parsed +from the `meshfile` is created as a [`p4est`](https://github.com/cburstedde/p4est) +tree datatype. +Note that the `mapping` and `polydeg` keyword arguments are only used by the `p4est_mesh_from_standard_abaqus` +function. The `p4est_mesh_from_hohqmesh_abaqus` function obtains the mesh `polydeg` directly from the `meshfile` +and constructs the transfinite mapping internally. +The particular strategy is selected according to the header present in the `meshfile` where +the constructor checks whether or not the `meshfile` was created with +[HOHQMesh.jl](https://github.com/trixi-framework/HOHQMesh.jl). +If the Abaqus file header is not present in the `meshfile` then the `P4estMesh` is created +with the function `p4est_mesh_from_standard_abaqus`. +The default keyword argument `initial_refinement_level=0` corresponds to a forest +where the number of trees is the same as the number of elements in the original `meshfile`. +Increasing the `initial_refinement_level` allows one to uniformly refine the base mesh given +in the `meshfile` to create a forest with more trees before the simulation begins. +For example, if a two-dimensional base mesh contains 25 elements then setting +`initial_refinement_level=1` creates an initial forest of `2^2 * 25 = 100` trees. +# Arguments +- `conn::Ptr{P4est.LibP4est.p4est_connectivity}`: Pointer to a cmesh object. +- `kwargs`: keyword arguments +""" +function T8codeMesh{NDIMS}(conn::Ptr{P4est.LibP4est.p4est_connectivity}; kwargs...) where NDIMS + + @assert NDIMS == 2 # Only support for NDIMS = 2 yet. + + cmesh = t8_cmesh_new_from_p4est(conn, mpi_comm(), 0) + + return T8codeMesh{NDIMS}(cmesh; kwargs...) +end + + """ T8codeMesh{NDIMS}(meshfile::String; mapping=nothing, polydeg=1, RealT=Float64, @@ -235,10 +368,10 @@ function T8codeMesh{NDIMS}(meshfile::String; meshfile_prefix, meshfile_suffix = split_filename(meshfile) - cmesh = t8_cmesh_from_msh_file(meshfile_prefix, 0, t8_mpi_comm(), 2, 0, 0) + cmesh = t8_cmesh_from_msh_file(meshfile_prefix, 0, mpi_comm(), 2, 0, 0) scheme = t8_scheme_new_default_cxx() - forest = t8_forest_new_uniform(cmesh,scheme,initial_refinement_level,0,mpi_comm().val) + forest = t8_forest_new_uniform(cmesh,scheme,initial_refinement_level,0,mpi_comm()) basis = LobattoLegendreBasis(RealT, polydeg) nodes = basis.nodes diff --git a/test/test_t8code_2d.jl b/test/test_t8code_2d.jl index e39ae1cae3..05fba64ced 100644 --- a/test/test_t8code_2d.jl +++ b/test/test_t8code_2d.jl @@ -34,13 +34,13 @@ mkdir(outdir) @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000 end end -# -# @trixi_testset "elixir_advection_unstructured_flag.jl" begin -# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_unstructured_flag.jl"), -# l2 = [0.0005379687442422346], -# linf = [0.007438525029884735]) -# end -# + + @trixi_testset "elixir_advection_unstructured_flag.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_unstructured_flag.jl"), + l2 = [0.0005379687442422346], + linf = [0.007438525029884735]) + end + # @trixi_testset "elixir_advection_amr_solution_independent.jl" begin # @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_amr_solution_independent.jl"), # # Expected errors are exactly the same as with StructuredMesh! From 1cab2503c8bb1afad8758a0ae8a4be650f1d231f Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Mon, 22 May 2023 12:16:40 +0200 Subject: [PATCH 004/128] Worked in the comments. --- Project.toml | 1 + src/auxiliary/auxiliary.jl | 12 ------ src/auxiliary/t8code.jl | 84 +++++++++++++++++++------------------- src/meshes/t8code_mesh.jl | 70 ++++--------------------------- 4 files changed, 51 insertions(+), 116 deletions(-) diff --git a/Project.toml b/Project.toml index e8c495e705..272fa786c4 100644 --- a/Project.toml +++ b/Project.toml @@ -72,6 +72,7 @@ StaticArrays = "1" StrideArrays = "0.1.18" StructArrays = "0.6" SummationByPartsOperators = "0.5.25" +T8code = "0.2.0" TimerOutputs = "0.5" Triangulate = "2.0" TriplotBase = "0.1" diff --git a/src/auxiliary/auxiliary.jl b/src/auxiliary/auxiliary.jl index ab3b64d879..8103ca4449 100644 --- a/src/auxiliary/auxiliary.jl +++ b/src/auxiliary/auxiliary.jl @@ -278,16 +278,4 @@ macro trixi_timeit(timer_output, label, expr) end end -function split_filename(filename) - - i = findlast(==('.'),filename) - - if isnothing(i) - return filename - end - - return filename[1:i-1],filename[i+1:end] - -end - end # @muladd diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index e3e572b427..91ff8c45c3 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -7,7 +7,7 @@ function init_t8code() return nothing end - # Initialize `p4est` with log level ERROR to prevent a lot of output in AMR simulations + # Initialize `t8code` with log level ERROR to prevent a lot of output in AMR simulations t8_init(SC_LP_ERROR) return nothing @@ -24,20 +24,20 @@ function t8_free(ptr) end function finalize_t8code() - # sc_finalize(); + # sc_finalize() return nothing end function trixi_t8_count_interfaces(forest) - # /* Check that forest is a committed, that is valid and usable, forest. */ - @T8_ASSERT (t8_forest_is_committed(forest) != 0); + # Check that forest is a committed, that is valid and usable, forest. + @T8_ASSERT (t8_forest_is_committed(forest) != 0) - # /* Get the number of local elements of forest. */ - num_local_elements = t8_forest_get_local_num_elements(forest); - # /* Get the number of ghost elements of forest. */ - num_ghost_elements = t8_forest_get_num_ghosts(forest); - # /* Get the number of trees that have elements of this process. */ - num_local_trees = t8_forest_get_num_local_trees(forest); + # Get the number of local elements of forest. + num_local_elements = t8_forest_get_local_num_elements(forest) + # Get the number of ghost elements of forest. + num_ghost_elements = t8_forest_get_num_ghosts(forest) + # Get the number of trees that have elements of this process. + num_local_trees = t8_forest_get_num_local_trees(forest) current_index = 0 @@ -46,14 +46,14 @@ function trixi_t8_count_interfaces(forest) local_num_boundry = 0 for itree = 0:num_local_trees-1 - tree_class = t8_forest_get_tree_class(forest, itree); - eclass_scheme = t8_forest_get_eclass_scheme(forest, tree_class); + tree_class = t8_forest_get_tree_class(forest, itree) + eclass_scheme = t8_forest_get_eclass_scheme(forest, tree_class) - # /* Get the number of elements of this tree. */ - num_elements_in_tree = t8_forest_get_tree_num_elements(forest, itree); + # Get the number of elements of this tree. + num_elements_in_tree = t8_forest_get_tree_num_elements(forest, itree) for ielement = 0:num_elements_in_tree-1 - element = t8_forest_get_element_in_tree(forest, itree, ielement); + element = t8_forest_get_element_in_tree(forest, itree, ielement) level = t8_element_level(eclass_scheme, element) @@ -121,15 +121,15 @@ function trixi_t8_count_interfaces(forest) end function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundaries, boundary_names) - # /* Check that forest is a committed, that is valid and usable, forest. */ - @T8_ASSERT (t8_forest_is_committed(forest) != 0); + # Check that forest is a committed, that is valid and usable, forest. + @T8_ASSERT (t8_forest_is_committed(forest) != 0) - # /* Get the number of local elements of forest. */ - num_local_elements = t8_forest_get_local_num_elements(forest); - # /* Get the number of ghost elements of forest. */ - num_ghost_elements = t8_forest_get_num_ghosts(forest); - # /* Get the number of trees that have elements of this process. */ - num_local_trees = t8_forest_get_num_local_trees(forest); + # Get the number of local elements of forest. + num_local_elements = t8_forest_get_local_num_elements(forest) + # Get the number of ghost elements of forest. + num_ghost_elements = t8_forest_get_num_ghosts(forest) + # Get the number of trees that have elements of this process. + num_local_trees = t8_forest_get_num_local_trees(forest) current_index = 0 @@ -138,14 +138,14 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari local_num_boundry = 0 for itree = 0:num_local_trees-1 - tree_class = t8_forest_get_tree_class(forest, itree); - eclass_scheme = t8_forest_get_eclass_scheme(forest, tree_class); + tree_class = t8_forest_get_tree_class(forest, itree) + eclass_scheme = t8_forest_get_eclass_scheme(forest, tree_class) # Get the number of elements of this tree. - num_elements_in_tree = t8_forest_get_tree_num_elements(forest, itree); + num_elements_in_tree = t8_forest_get_tree_num_elements(forest, itree) for ielement = 0:num_elements_in_tree-1 - element = t8_forest_get_element_in_tree(forest, itree, ielement); + element = t8_forest_get_element_in_tree(forest, itree, ielement) level = t8_element_level(eclass_scheme, element) @@ -203,7 +203,7 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari interfaces.neighbor_ids[2, interface_id] = neighbor_ielements[1] + 1 # Iterate over primary and secondary element. - for side in 1:2 + for side = 1:2 # Align interface in positive coordinate direction of primary element. # For orientation == 1, the secondary element needs to be indexed backwards # relative to the interface. @@ -245,7 +245,7 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari # First `1:end-1` entries are the smaller elements. mortars.neighbor_ids[1:end-1, mortar_id] .= neighbor_ielements[:] .+ 1 - for side in 1:2 + for side = 1:2 # Align mortar in positive coordinate direction of small side. # For orientation == 1, the large side needs to be indexed backwards # relative to the mortar. @@ -327,25 +327,25 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari end function trixi_t8_get_local_element_levels(forest) - # /* Check that forest is a committed, that is valid and usable, forest. */ - @T8_ASSERT (t8_forest_is_committed(forest) != 0); + # Check that forest is a committed, that is valid and usable, forest. + @T8_ASSERT (t8_forest_is_committed(forest) != 0) levels = Vector{Int}(undef, t8_forest_get_local_num_elements(forest)) - # /* Get the number of trees that have elements of this process. */ - num_local_trees = t8_forest_get_num_local_trees(forest); + # Get the number of trees that have elements of this process. + num_local_trees = t8_forest_get_num_local_trees(forest) current_index = 0 for itree = 0:num_local_trees-1 - tree_class = t8_forest_get_tree_class(forest, itree); - eclass_scheme = t8_forest_get_eclass_scheme(forest, tree_class); + tree_class = t8_forest_get_tree_class(forest, itree) + eclass_scheme = t8_forest_get_eclass_scheme(forest, tree_class) - # /* Get the number of elements of this tree. */ - num_elements_in_tree = t8_forest_get_tree_num_elements(forest, itree); + # Get the number of elements of this tree. + num_elements_in_tree = t8_forest_get_tree_num_elements(forest, itree) for ielement = 0:num_elements_in_tree-1 - element = t8_forest_get_element_in_tree(forest, itree, ielement); + element = t8_forest_get_element_in_tree(forest, itree, ielement) current_index += 1 levels[current_index] = t8_element_level(eclass_scheme, element) end # for @@ -380,12 +380,12 @@ function adapt_callback(forest, end function trixi_t8_adapt_new(old_forest, indicators) - # /* Check that forest is a committed, that is valid and usable, forest. */ - @T8_ASSERT (t8_forest_is_committed(old_forest) != 0); + # Check that forest is a committed, that is valid and usable, forest. + @T8_ASSERT (t8_forest_is_committed(old_forest) != 0) # Init new forest. new_forest_ref = Ref{t8_forest_t}() - t8_forest_init(new_forest_ref); + t8_forest_init(new_forest_ref) new_forest = new_forest_ref[] let set_from = C_NULL, recursive = 0, set_for_coarsening = 0, no_repartition = 0 @@ -393,7 +393,7 @@ function trixi_t8_adapt_new(old_forest, indicators) t8_forest_set_adapt(new_forest, old_forest, @t8_adapt_callback(adapt_callback), recursive) t8_forest_set_balance(new_forest, set_from, no_repartition) t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - # t8_forest_set_ghost(new_forest, 1, T8_GHOST_FACES); + # t8_forest_set_ghost(new_forest, 1, T8_GHOST_FACES) t8_forest_commit(new_forest) end diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index ff600b36cb..7c217d2971 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -19,7 +19,7 @@ mutable struct T8codeMesh{NDIMS, RealT<:Real, IsParallel} <: AbstractMesh{NDIMS} boundary_names :: Array{Symbol, 2} # [face direction, tree] current_filename :: String - unsaved_changes :: Bool + unsaved_changes :: Bool # Not used yet. ncells :: Int ninterfaces :: Int @@ -357,81 +357,27 @@ For example, if a two-dimensional base mesh contains 25 elements then setting - `initial_refinement_level::Integer`: refine the mesh uniformly to this level before the simulation starts. - `unsaved_changes::Bool`: if set to `true`, the mesh will be saved to a mesh file. """ -function T8codeMesh{NDIMS}(meshfile::String; - mapping=nothing, polydeg=1, RealT=Float64, - initial_refinement_level=0, unsaved_changes=true) where NDIMS +function T8codeMesh{NDIMS}(meshfile::String; kwargs...) where NDIMS @assert NDIMS == 2 # Only support for NDIMS = 2 yet. # Prevent `t8code` from crashing Julia if the file doesn't exist. @assert isfile(meshfile) - meshfile_prefix, meshfile_suffix = split_filename(meshfile) + meshfile_prefix, meshfile_suffix = splitext(meshfile) cmesh = t8_cmesh_from_msh_file(meshfile_prefix, 0, mpi_comm(), 2, 0, 0) - scheme = t8_scheme_new_default_cxx() - forest = t8_forest_new_uniform(cmesh,scheme,initial_refinement_level,0,mpi_comm()) - - basis = LobattoLegendreBasis(RealT, polydeg) - nodes = basis.nodes - - num_local_trees = t8_cmesh_get_num_local_trees(cmesh) - - tree_node_coordinates = Array{RealT, NDIMS+2}(undef, NDIMS, - ntuple(_ -> length(nodes), NDIMS)..., - num_local_trees) - - nodes_in = [-1.0, 1.0] - matrix = polynomial_interpolation_matrix(nodes_in, nodes) - data_in = Array{RealT, 3}(undef, 2, 2, 2) - tmp1 = zeros(RealT, 2, length(nodes), length(nodes_in)) - - for itree in 0:num_local_trees-1 - - veptr = t8_cmesh_get_tree_vertices(cmesh, itree) - verts = unsafe_wrap(Array,veptr,(3,1 << NDIMS)) - - u = verts[:,2] - verts[:,1] - v = verts[:,3] - verts[:,1] - w = [0.0,0.0,1.0] - - vol = dot(cross(u,v),w) - - if vol < 0.0 - @warn "Discovered negative volumes in `cmesh`: vol = $vol" - end - - # Tree vertices are stored in z-order. - @views data_in[:, 1, 1] .= verts[1:2,1] - @views data_in[:, 2, 1] .= verts[1:2,2] - @views data_in[:, 1, 2] .= verts[1:2,3] - @views data_in[:, 2, 2] .= verts[1:2,4] - - # Interpolate corner coordinates to specified nodes. - multiply_dimensionwise!( - view(tree_node_coordinates, :, :, :, itree+1), - matrix, matrix, - data_in, - tmp1 - ) - - end - - map_node_coordinates!(tree_node_coordinates, mapping) - - # There's no simple and generic way to distinguish boundaries. Name all of them :all. - boundary_names = fill(:all, 2 * NDIMS, num_local_trees) + return T8codeMesh{NDIMS}(cmesh; kwargs...) - return T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, boundary_names, "", unsaved_changes) end -# TODO: Just a placeholder. -function balance!(mesh::T8codeMesh{2}, init_fn=C_NULL) +# TODO: Just a placeholder. Will be implemented later. +function balance!(mesh::T8codeMesh, init_fn=C_NULL) return nothing end -# TODO: Just a placeholder. -function partition!(mesh::T8codeMesh{2}; allow_coarsening=true, weight_fn=C_NULL) +# TODO: Just a placeholder. Will be implemented later. +function partition!(mesh::T8codeMesh; allow_coarsening=true, weight_fn=C_NULL) return nothing end From cdced173a4fb1d56946c2fcd465032e67b2d430e Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 23 May 2023 12:41:13 +0200 Subject: [PATCH 005/128] Fixed spelling. --- src/auxiliary/t8code.jl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index 91ff8c45c3..09359b9fae 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -43,7 +43,7 @@ function trixi_t8_count_interfaces(forest) local_num_conform = 0 local_num_mortars = 0 - local_num_boundry = 0 + local_num_boundary = 0 for itree = 0:num_local_trees-1 tree_class = t8_forest_get_tree_class(forest, itree) @@ -94,7 +94,7 @@ function trixi_t8_count_interfaces(forest) else - local_num_boundry += 1 + local_num_boundary += 1 end @@ -112,12 +112,12 @@ function trixi_t8_count_interfaces(forest) # println(" ## local_num_elements = ", num_local_elements) # println(" ## local_num_conform = ", local_num_conform) # println(" ## local_num_mortars = ", local_num_mortars) - # println(" ## local_num_boundry = ", local_num_boundry) + # println(" ## local_num_boundary = ", local_num_boundary) # println("") return (interfaces = local_num_conform, mortars = local_num_mortars, - boundaries = local_num_boundry) + boundaries = local_num_boundary) end function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundaries, boundary_names) @@ -135,7 +135,7 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari local_num_conform = 0 local_num_mortars = 0 - local_num_boundry = 0 + local_num_boundary = 0 for itree = 0:num_local_trees-1 tree_class = t8_forest_get_tree_class(forest, itree) @@ -277,8 +277,8 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari # Domain boundary. else - local_num_boundry += 1 - boundary_id = local_num_boundry + local_num_boundary += 1 + boundary_id = local_num_boundary boundaries.neighbor_ids[boundary_id] = current_index + 1 @@ -317,13 +317,13 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari # println("") # println(" ## local_num_conform = ", local_num_conform) # println(" ## local_num_mortars = ", local_num_mortars) - # println(" ## local_num_boundry = ", local_num_boundry) + # println(" ## local_num_boundary = ", local_num_boundary) # println("") # println("") return (interfaces = local_num_conform, mortars = local_num_mortars, - boundaries = local_num_boundry) + boundaries = local_num_boundary) end function trixi_t8_get_local_element_levels(forest) From d02d32e56f13d3927d11f662d86a6220bd3e443b Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Wed, 24 May 2023 09:33:53 +0200 Subject: [PATCH 006/128] Update src/auxiliary/auxiliary.jl Co-authored-by: Hendrik Ranocha --- src/auxiliary/auxiliary.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/auxiliary/auxiliary.jl b/src/auxiliary/auxiliary.jl index 05fee749b0..bdf03e1ec0 100644 --- a/src/auxiliary/auxiliary.jl +++ b/src/auxiliary/auxiliary.jl @@ -280,6 +280,7 @@ macro trixi_timeit(timer_output, label, expr) end end + """ @autoinfiltrate @autoinfiltrate condition::Bool From 7c01df056287b7a9031d44224968102cd17a3c5d Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Wed, 24 May 2023 09:39:57 +0200 Subject: [PATCH 007/128] Added whitespace in Unions. --- src/solvers/dgsem_p4est/containers_2d.jl | 4 ++-- src/solvers/dgsem_p4est/dg_2d.jl | 30 ++++++++++++------------ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/solvers/dgsem_p4est/containers_2d.jl b/src/solvers/dgsem_p4est/containers_2d.jl index 51df8ca1e4..49e3beff3e 100644 --- a/src/solvers/dgsem_p4est/containers_2d.jl +++ b/src/solvers/dgsem_p4est/containers_2d.jl @@ -6,7 +6,7 @@ # Initialize data structures in element container -function init_elements!(elements, mesh::Union{P4estMesh{2},T8codeMesh{2}}, basis::LobattoLegendreBasis) +function init_elements!(elements, mesh::Union{P4estMesh{2}, T8codeMesh{2}}, basis::LobattoLegendreBasis) @unpack node_coordinates, jacobian_matrix, contravariant_vectors, inverse_jacobian = elements @@ -26,7 +26,7 @@ end # Interpolate tree_node_coordinates to each quadrant at the nodes of the specified basis function calc_node_coordinates!(node_coordinates, - mesh::Union{P4estMesh{2},T8codeMesh{2}}, + mesh::Union{P4estMesh{2}, T8codeMesh{2}}, basis::LobattoLegendreBasis) # Hanging nodes will cause holes in the mesh if its polydeg is higher # than the polydeg of the solver. diff --git a/src/solvers/dgsem_p4est/dg_2d.jl b/src/solvers/dgsem_p4est/dg_2d.jl index c4cb01afe8..20234c767e 100644 --- a/src/solvers/dgsem_p4est/dg_2d.jl +++ b/src/solvers/dgsem_p4est/dg_2d.jl @@ -7,7 +7,7 @@ # The methods below are specialized on the mortar type # and called from the basic `create_cache` method at the top. -function create_cache(mesh::Union{P4estMesh{2},T8codeMesh{2}}, equations, mortar_l2::LobattoLegendreMortarL2, uEltype) +function create_cache(mesh::Union{P4estMesh{2}, T8codeMesh{2}}, equations, mortar_l2::LobattoLegendreMortarL2, uEltype) # TODO: Taal performance using different types MA2d = MArray{Tuple{nvariables(equations), nnodes(mortar_l2)}, uEltype, 2, @@ -58,7 +58,7 @@ end # We pass the `surface_integral` argument solely for dispatch function prolong2interfaces!(cache, u, - mesh::Union{P4estMesh{2},T8codeMesh{2}}, + mesh::Union{P4estMesh{2}, T8codeMesh{2}}, equations, surface_integral, dg::DG) @unpack interfaces = cache index_range = eachnode(dg) @@ -109,7 +109,7 @@ end function calc_interface_flux!(surface_flux_values, - mesh::Union{P4estMesh{2},T8codeMesh{2}}, + mesh::Union{P4estMesh{2}, T8codeMesh{2}}, nonconservative_terms, equations, surface_integral, dg::DG, cache) @unpack neighbor_ids, node_indices = cache.interfaces @@ -173,7 +173,7 @@ end # Inlined version of the interface flux computation for conservation laws @inline function calc_interface_flux!(surface_flux_values, - mesh::Union{P4estMesh{2},T8codeMesh{2}}, + mesh::Union{P4estMesh{2}, T8codeMesh{2}}, nonconservative_terms::False, equations, surface_integral, dg::DG, cache, interface_index, normal_direction, @@ -194,7 +194,7 @@ end # Inlined version of the interface flux computation for equations with conservative and nonconservative terms @inline function calc_interface_flux!(surface_flux_values, - mesh::Union{P4estMesh{2},T8codeMesh{2}}, + mesh::Union{P4estMesh{2}, T8codeMesh{2}}, nonconservative_terms::True, equations, surface_integral, dg::DG, cache, interface_index, normal_direction, @@ -229,7 +229,7 @@ end function prolong2boundaries!(cache, u, - mesh::Union{P4estMesh{2},T8codeMesh{2}}, + mesh::Union{P4estMesh{2}, T8codeMesh{2}}, equations, surface_integral, dg::DG) @unpack boundaries = cache index_range = eachnode(dg) @@ -259,7 +259,7 @@ end function calc_boundary_flux!(cache, t, boundary_condition, boundary_indexing, - mesh::Union{P4estMesh{2},T8codeMesh{2}}, + mesh::Union{P4estMesh{2}, T8codeMesh{2}}, equations, surface_integral, dg::DG) @unpack boundaries = cache @unpack surface_flux_values = cache.elements @@ -296,7 +296,7 @@ end # inlined version of the boundary flux calculation along a physical interface @inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition, - mesh::Union{P4estMesh{2},T8codeMesh{2}}, + mesh::Union{P4estMesh{2}, T8codeMesh{2}}, nonconservative_terms::False, equations, surface_integral, dg::DG, cache, i_index, j_index, @@ -325,7 +325,7 @@ end # inlined version of the boundary flux with nonconservative terms calculation along a physical interface @inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition, - mesh::Union{P4estMesh{2},T8codeMesh{2}}, + mesh::Union{P4estMesh{2}, T8codeMesh{2}}, nonconservative_terms::True, equations, surface_integral, dg::DG, cache, i_index, j_index, @@ -363,7 +363,7 @@ end function prolong2mortars!(cache, u, - mesh::Union{P4estMesh{2},T8codeMesh{2}}, equations, + mesh::Union{P4estMesh{2}, T8codeMesh{2}}, equations, mortar_l2::LobattoLegendreMortarL2, surface_integral, dg::DGSEM) @unpack neighbor_ids, node_indices = cache.mortars @@ -427,7 +427,7 @@ end function calc_mortar_flux!(surface_flux_values, - mesh::Union{P4estMesh{2},T8codeMesh{2}}, + mesh::Union{P4estMesh{2}, T8codeMesh{2}}, nonconservative_terms, equations, mortar_l2::LobattoLegendreMortarL2, surface_integral, dg::DG, cache) @@ -484,7 +484,7 @@ end # Inlined version of the mortar flux computation on small elements for conservation laws @inline function calc_mortar_flux!(fstar, - mesh::Union{P4estMesh{2},T8codeMesh{2}}, + mesh::Union{P4estMesh{2}, T8codeMesh{2}}, nonconservative_terms::False, equations, surface_integral, dg::DG, cache, mortar_index, position_index, normal_direction, @@ -503,7 +503,7 @@ end # Inlined version of the mortar flux computation on small elements for equations with conservative and # nonconservative terms @inline function calc_mortar_flux!(fstar, - mesh::Union{P4estMesh{2},T8codeMesh{2}}, + mesh::Union{P4estMesh{2}, T8codeMesh{2}}, nonconservative_terms::True, equations, surface_integral, dg::DG, cache, mortar_index, position_index, normal_direction, @@ -530,7 +530,7 @@ end @inline function mortar_fluxes_to_elements!(surface_flux_values, - mesh::Union{P4estMesh{2},T8codeMesh{2}}, equations, + mesh::Union{P4estMesh{2}, T8codeMesh{2}}, equations, mortar_l2::LobattoLegendreMortarL2, dg::DGSEM, cache, mortar, fstar, u_buffer) @unpack neighbor_ids, node_indices = cache.mortars @@ -589,7 +589,7 @@ end function calc_surface_integral!(du, u, - mesh::Union{P4estMesh{2},T8codeMesh{2}}, + mesh::Union{P4estMesh{2}, T8codeMesh{2}}, equations, surface_integral::SurfaceIntegralWeakForm, dg::DGSEM, cache) From e6cd1abeab265ac302b728a6244c74edeb62f78b Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Wed, 24 May 2023 10:08:10 +0200 Subject: [PATCH 008/128] Adapted commented out code block reporting the no. of elements per level. --- src/callbacks_step/analysis.jl | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/callbacks_step/analysis.jl b/src/callbacks_step/analysis.jl index 4587fbf245..92503f8805 100644 --- a/src/callbacks_step/analysis.jl +++ b/src/callbacks_step/analysis.jl @@ -502,6 +502,8 @@ function print_amr_information(callbacks, mesh::T8codeMesh, solver, cache) # Return early if there is nothing to print uses_amr(callbacks) || return nothing + # TODO: Switch to global element levels array when MPI supported or find + # another solution. levels = trixi_t8_get_local_element_levels(mesh.forest) min_level = minimum(levels) @@ -510,10 +512,14 @@ function print_amr_information(callbacks, mesh::T8codeMesh, solver, cache) mpi_println(" minlevel = $min_level") mpi_println(" maxlevel = $max_level") - # for level = max_level:-1:min_level+1 - # mpi_println(" ├── level $level: " * @sprintf("% 14d", elements_per_level[level + 1])) - # end - # mpi_println(" └── level $min_level: " * @sprintf("% 14d", elements_per_level[min_level + 1])) + if min_level > 0 + elements_per_level = [count(==(l), levels) for l in 1:max_level] + + for level = max_level:-1:min_level+1 + mpi_println(" ├── level $level: " * @sprintf("% 14d", elements_per_level[level])) + end + mpi_println(" └── level $min_level: " * @sprintf("% 14d", elements_per_level[min_level])) + end return nothing end From 0abdcd9dee25d22b8d94294225bc2d305b4b6b4f Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Wed, 24 May 2023 12:09:38 +0200 Subject: [PATCH 009/128] Added dummy save mesh support for . --- examples/t8code_2d_dgsem/elixir_advection_basic.jl | 7 +++---- .../elixir_advection_nonconforming_flag.jl | 7 +++---- .../t8code_2d_dgsem/elixir_advection_unstructured_flag.jl | 7 +++---- src/callbacks_step/save_solution_dg.jl | 2 +- src/meshes/mesh_io.jl | 8 +++++++- src/meshes/t8code_mesh.jl | 6 +++--- 6 files changed, 20 insertions(+), 17 deletions(-) diff --git a/examples/t8code_2d_dgsem/elixir_advection_basic.jl b/examples/t8code_2d_dgsem/elixir_advection_basic.jl index 3c89584124..5ef8c68fcb 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_basic.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_basic.jl @@ -43,15 +43,14 @@ summary_callback = SummaryCallback() analysis_callback = AnalysisCallback(semi, interval=100) # The SaveSolutionCallback allows to save the solution to a file in regular intervals -# save_solution = SaveSolutionCallback(interval=100, -# solution_variables=cons2prim) +save_solution = SaveSolutionCallback(interval=100, + solution_variables=cons2prim) # The StepsizeCallback handles the re-calculation of the maximum Δt after each time step stepsize_callback = StepsizeCallback(cfl=1.6) # Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver -# callbacks = CallbackSet(summary_callback, analysis_callback, save_solution, stepsize_callback) -callbacks = CallbackSet(summary_callback, analysis_callback, stepsize_callback) +callbacks = CallbackSet(summary_callback, analysis_callback, save_solution, stepsize_callback) ############################################################################### diff --git a/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl b/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl index e99e818e29..a44b8aae13 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl @@ -92,15 +92,14 @@ summary_callback = SummaryCallback() analysis_callback = AnalysisCallback(semi, interval=100) # The SaveSolutionCallback allows to save the solution to a file in regular intervals -# save_solution = SaveSolutionCallback(interval=100, -# solution_variables=cons2prim) +save_solution = SaveSolutionCallback(interval=100, + solution_variables=cons2prim) # The StepsizeCallback handles the re-calculation of the maximum Δt after each time step stepsize_callback = StepsizeCallback(cfl=1.6) # Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver -# callbacks = CallbackSet(summary_callback, analysis_callback, save_solution, stepsize_callback) -callbacks = CallbackSet(summary_callback, analysis_callback, stepsize_callback) +callbacks = CallbackSet(summary_callback, analysis_callback, save_solution, stepsize_callback) ############################################################################### diff --git a/examples/t8code_2d_dgsem/elixir_advection_unstructured_flag.jl b/examples/t8code_2d_dgsem/elixir_advection_unstructured_flag.jl index ba7274235b..e6d498f943 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_unstructured_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_unstructured_flag.jl @@ -63,8 +63,8 @@ analysis_callback = AnalysisCallback(semi, interval=100) # The SaveSolutionCallback allows to save the solution to a file in regular # intervals. -# save_solution = SaveSolutionCallback(interval=100, -# solution_variables=cons2prim) +save_solution = SaveSolutionCallback(interval=100, + solution_variables=cons2prim) # The StepsizeCallback handles the re-calculation of the maximum Δt after each # time step. @@ -72,8 +72,7 @@ stepsize_callback = StepsizeCallback(cfl=1.4) # Create a CallbackSet to collect all callbacks such that they can be passed to # the ODE solver. -# callbacks = CallbackSet(summary_callback, analysis_callback, save_solution, stepsize_callback) -callbacks = CallbackSet(summary_callback, analysis_callback, stepsize_callback) +callbacks = CallbackSet(summary_callback, analysis_callback, save_solution, stepsize_callback) ############################################################################### # Run the simulation. diff --git a/src/callbacks_step/save_solution_dg.jl b/src/callbacks_step/save_solution_dg.jl index c1708ca682..7bb7564eb1 100644 --- a/src/callbacks_step/save_solution_dg.jl +++ b/src/callbacks_step/save_solution_dg.jl @@ -6,7 +6,7 @@ function save_solution_file(u, time, dt, timestep, - mesh::Union{SerialTreeMesh, StructuredMesh, UnstructuredMesh2D, SerialP4estMesh}, + mesh::Union{SerialTreeMesh, StructuredMesh, UnstructuredMesh2D, SerialP4estMesh, SerialT8codeMesh}, equations, dg::DG, cache, solution_callback, element_variables=Dict{Symbol,Any}(); system="") diff --git a/src/meshes/mesh_io.jl b/src/meshes/mesh_io.jl index beef5341e2..c73ad4a7d4 100644 --- a/src/meshes/mesh_io.jl +++ b/src/meshes/mesh_io.jl @@ -6,7 +6,7 @@ # Save current mesh with some context information as an HDF5 file. -function save_mesh_file(mesh::Union{TreeMesh, P4estMesh}, output_directory, timestep=0) +function save_mesh_file(mesh::Union{TreeMesh, P4estMesh, T8codeMesh}, output_directory, timestep=0) save_mesh_file(mesh, output_directory, timestep, mpi_parallel(mesh)) end @@ -217,6 +217,12 @@ function save_mesh_file(mesh::P4estMesh, output_directory, timestep, mpi_paralle return filename end +# TODO: Implement this function as soon as there is support for this in `t8code`. +function save_mesh_file(mesh::T8codeMesh, output_directory, timestep, mpi_parallel) + @warn "Mesh file output not supported yet for `T8codeMesh`." + + return joinpath(output_directory, "dummy_mesh.h5") +end """ load_mesh(restart_file::AbstractString; n_cells_max) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 7c217d2971..06c53dfc2a 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -36,7 +36,7 @@ mutable struct T8codeMesh{NDIMS, RealT<:Real, IsParallel} <: AbstractMesh{NDIMS} # else # is_parallel = Val(false) # end - is_parallel = Val(false) + is_parallel = False() mesh = new{NDIMS, Float64, typeof(is_parallel)}(cmesh, scheme, forest, is_parallel) @@ -64,8 +64,8 @@ function T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, return mesh end -SerialT8codeMesh{NDIMS} = T8codeMesh{NDIMS, <:Val{false}} -# @inline mpi_parallel(mesh::SerialT8codeMesh) = Val(false) +const SerialT8codeMesh{NDIMS} = T8codeMesh{NDIMS, <:Real, <:False} +@inline mpi_parallel(mesh::SerialT8codeMesh) = False() @inline Base.ndims(::T8codeMesh{NDIMS}) where NDIMS = NDIMS @inline Base.real(::T8codeMesh{NDIMS, RealT}) where {NDIMS, RealT} = RealT From 10ac87d91b6b1d6346c2094c8769cb998fa5b736 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Wed, 24 May 2023 12:10:14 +0200 Subject: [PATCH 010/128] Added test . --- ...ixir_advection_amr_solution_independent.jl | 149 ++++++++++++++++++ test/test_t8code_2d.jl | 16 +- 2 files changed, 157 insertions(+), 8 deletions(-) create mode 100644 examples/t8code_2d_dgsem/elixir_advection_amr_solution_independent.jl diff --git a/examples/t8code_2d_dgsem/elixir_advection_amr_solution_independent.jl b/examples/t8code_2d_dgsem/elixir_advection_amr_solution_independent.jl new file mode 100644 index 0000000000..e498d8aa75 --- /dev/null +++ b/examples/t8code_2d_dgsem/elixir_advection_amr_solution_independent.jl @@ -0,0 +1,149 @@ +using OrdinaryDiffEq +using Trixi + +# Define new structs inside a module to allow re-evaluating the file. +module TrixiExtension + +using Trixi + +struct IndicatorSolutionIndependent{Cache<:NamedTuple} <: Trixi.AbstractIndicator + cache::Cache +end + +function IndicatorSolutionIndependent(semi) + basis = semi.solver.basis + alpha = Vector{real(basis)}() + cache = (; semi.mesh, alpha) + return IndicatorSolutionIndependent{typeof(cache)}(cache) +end + +function (indicator::IndicatorSolutionIndependent)(u::AbstractArray{<:Any,4}, + mesh, equations, dg, cache; + t, kwargs...) + + mesh = indicator.cache.mesh + alpha = indicator.cache.alpha + resize!(alpha, nelements(dg, cache)) + + # Predict the theoretical center. + advection_velocity = (0.2, -0.7) + center = t.*advection_velocity + + inner_distance = 1 + outer_distance = 1.85 + + # Iterate over all elements. + for element in 1:length(alpha) + # Calculate periodic distance between cell and center. + # This requires an uncurved mesh! + coordinates = SVector(0.5 * (cache.elements.node_coordinates[1, 1, 1, element] + + cache.elements.node_coordinates[1, end, 1, element]), + 0.5 * (cache.elements.node_coordinates[2, 1, 1, element] + + cache.elements.node_coordinates[2, 1, end, element])) + + # The geometric shape of the amr should be preserved when the base_level is increased. + # This is done by looking at the original coordinates of each cell. + cell_coordinates = original_coordinates(coordinates, 5/8) + cell_distance = periodic_distance_2d(cell_coordinates, center, 10) + if cell_distance < (inner_distance+outer_distance)/2 + cell_coordinates = original_coordinates(coordinates, 5/16) + cell_distance = periodic_distance_2d(cell_coordinates, center, 10) + end + + # Set alpha according to cells position inside the circles. + target_level = (cell_distance < inner_distance) + (cell_distance < outer_distance) + alpha[element] = target_level/2 + end + return alpha +end + +# For periodic domains, distance between two points must take into account +# periodic extensions of the domain. +function periodic_distance_2d(coordinates, center, domain_length) + dx = coordinates .- center + dx_shifted = abs.(dx .% domain_length) + dx_periodic = min.(dx_shifted, domain_length .- dx_shifted) + return sqrt(sum(dx_periodic.^2)) +end + +# This takes a cells coordinates and transforms them into the coordinates of a +# parent-cell it originally refined from. It does it so that the parent-cell +# has given cell_length. +function original_coordinates(coordinates, cell_length) + offset = coordinates .% cell_length + offset_sign = sign.(offset) + border = coordinates - offset + center = border + (offset_sign .* cell_length/2) + return center +end + +end # module TrixiExtension + +import .TrixiExtension + +############################################################################### +# Semidiscretization of the linear advection equation. + +advection_velocity = (0.2, -0.7) +equations = LinearScalarAdvectionEquation2D(advection_velocity) + +initial_condition = initial_condition_gauss + +solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) + +coordinates_min = (-5.0, -5.0) +coordinates_max = ( 5.0, 5.0) + +mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) + +trees_per_dimension = (1, 1) + +mesh = T8codeMesh(trees_per_dimension, polydeg=3, + mapping=mapping, + initial_refinement_level=1) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 10.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval=analysis_interval, + extra_analysis_integrals=(entropy,)) + +alive_callback = AliveCallback(analysis_interval=analysis_interval) + +save_solution = SaveSolutionCallback(interval=100, + save_initial_solution=true, + save_final_solution=true, + solution_variables=cons2prim) + +amr_controller = ControllerThreeLevel(semi, TrixiExtension.IndicatorSolutionIndependent(semi), + base_level=4, + med_level=5, med_threshold=0.1, + max_level=6, max_threshold=0.6) + +amr_callback = AMRCallback(semi, amr_controller, + interval=5, + adapt_initial_condition=true, + adapt_initial_condition_only_refine=true) + +stepsize_callback = StepsizeCallback(cfl=1.6) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + save_solution, + amr_callback, stepsize_callback); + +############################################################################### +# Run the simulation. + +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep=false, callback=callbacks); +summary_callback() # print the timer summary diff --git a/test/test_t8code_2d.jl b/test/test_t8code_2d.jl index 05fba64ced..bafea516c5 100644 --- a/test/test_t8code_2d.jl +++ b/test/test_t8code_2d.jl @@ -41,14 +41,14 @@ mkdir(outdir) linf = [0.007438525029884735]) end -# @trixi_testset "elixir_advection_amr_solution_independent.jl" begin -# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_amr_solution_independent.jl"), -# # Expected errors are exactly the same as with StructuredMesh! -# l2 = [4.949660644033807e-5], -# linf = [0.0004867846262313763], -# coverage_override = (maxiters=6,)) -# end -# + @trixi_testset "elixir_advection_amr_solution_independent.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_amr_solution_independent.jl"), + # Expected errors are exactly the same as with StructuredMesh! + l2 = [4.949660644033807e-5], + linf = [0.0004867846262313763], + coverage_override = (maxiters=6,)) + end + # @trixi_testset "elixir_advection_amr_unstructured_flag.jl" begin # @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_amr_unstructured_flag.jl"), # l2 = [0.0012766060609964525], From 349136adb14eb4ec94f147555ef5fd86c4ad7e32 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Wed, 24 May 2023 12:15:34 +0200 Subject: [PATCH 011/128] Added to method signature. --- src/callbacks_step/save_restart_dg.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/callbacks_step/save_restart_dg.jl b/src/callbacks_step/save_restart_dg.jl index 47487124cc..e17ad897b1 100644 --- a/src/callbacks_step/save_restart_dg.jl +++ b/src/callbacks_step/save_restart_dg.jl @@ -6,7 +6,7 @@ function save_restart_file(u, time, dt, timestep, - mesh::Union{SerialTreeMesh, StructuredMesh, UnstructuredMesh2D, SerialP4estMesh}, + mesh::Union{SerialTreeMesh, StructuredMesh, UnstructuredMesh2D, SerialP4estMesh, SerialT8codeMesh}, equations, dg::DG, cache, restart_callback) @unpack output_directory = restart_callback From 3e47616ceb5670deba234f90cfe08969918bab97 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Wed, 24 May 2023 14:47:01 +0200 Subject: [PATCH 012/128] Deleted unnecessary comments. --- src/auxiliary/t8code.jl | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index 09359b9fae..1d35b4ae1e 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -108,13 +108,6 @@ function trixi_t8_count_interfaces(forest) end # for end # for - # println("") - # println(" ## local_num_elements = ", num_local_elements) - # println(" ## local_num_conform = ", local_num_conform) - # println(" ## local_num_mortars = ", local_num_mortars) - # println(" ## local_num_boundary = ", local_num_boundary) - # println("") - return (interfaces = local_num_conform, mortars = local_num_mortars, boundaries = local_num_boundary) @@ -282,9 +275,6 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari boundaries.neighbor_ids[boundary_id] = current_index + 1 - # println("neighbor_id = ", boundaries.neighbor_ids[boundary_id] -1) - # println("face = ", iface) - if iface == 0 # Index face in negative x-direction. boundaries.node_indices[boundary_id] = (:begin, :i_forward) @@ -313,14 +303,6 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari end # for end # for - # println("") - # println("") - # println(" ## local_num_conform = ", local_num_conform) - # println(" ## local_num_mortars = ", local_num_mortars) - # println(" ## local_num_boundary = ", local_num_boundary) - # println("") - # println("") - return (interfaces = local_num_conform, mortars = local_num_mortars, boundaries = local_num_boundary) From fe6d8746b65a1bc77313269f0a5a9137d859fb85 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Wed, 24 May 2023 14:51:35 +0200 Subject: [PATCH 013/128] Removed commented out tests. --- test/test_t8code_2d.jl | 116 ----------------------------------------- 1 file changed, 116 deletions(-) diff --git a/test/test_t8code_2d.jl b/test/test_t8code_2d.jl index bafea516c5..66a200a949 100644 --- a/test/test_t8code_2d.jl +++ b/test/test_t8code_2d.jl @@ -49,122 +49,6 @@ mkdir(outdir) coverage_override = (maxiters=6,)) end -# @trixi_testset "elixir_advection_amr_unstructured_flag.jl" begin -# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_amr_unstructured_flag.jl"), -# l2 = [0.0012766060609964525], -# linf = [0.01750280631586159], -# coverage_override = (maxiters=6,)) -# end -# -# @trixi_testset "elixir_advection_restart.jl" begin -# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_restart.jl"), -# l2 = [4.507575525876275e-6], -# linf = [6.21489667023134e-5]) -# end -# -# @trixi_testset "elixir_euler_source_terms_nonconforming_unstructured_flag.jl" begin -# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_source_terms_nonconforming_unstructured_flag.jl"), -# l2 = [0.0034516244508588046, 0.0023420334036925493, 0.0024261923964557187, 0.004731710454271893], -# linf = [0.04155789011775046, 0.024772109862748914, 0.03759938693042297, 0.08039824959535657]) -# end -# -# @trixi_testset "elixir_euler_free_stream.jl" begin -# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_free_stream.jl"), -# l2 = [2.063350241405049e-15, 1.8571016296925367e-14, 3.1769447886391905e-14, 1.4104095258528071e-14], -# linf = [1.9539925233402755e-14, 2e-12, 4.8e-12, 4e-12], -# atol = 2.0e-12, # required to make CI tests pass on macOS -# ) -# end -# -# @trixi_testset "elixir_euler_shockcapturing_ec.jl" begin -# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_shockcapturing_ec.jl"), -# l2 = [9.53984675e-02, 1.05633455e-01, 1.05636158e-01, 3.50747237e-01], -# linf = [2.94357464e-01, 4.07893014e-01, 3.97334516e-01, 1.08142520e+00], -# tspan = (0.0, 1.0)) -# end -# -# @trixi_testset "elixir_euler_sedov.jl" begin -# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_sedov.jl"), -# l2 = [3.76149952e-01, 2.46970327e-01, 2.46970327e-01, 1.28889042e+00], -# linf = [1.22139001e+00, 1.17742626e+00, 1.17742626e+00, 6.20638482e+00], -# tspan = (0.0, 0.3)) -# end -# -# @trixi_testset "elixir_euler_blast_wave_amr.jl" begin -# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_blast_wave_amr.jl"), -# l2 = [6.32183914e-01, 3.86914231e-01, 3.86869171e-01, 1.06575688e+00], -# linf = [2.76020890e+00, 2.32659890e+00, 2.32580837e+00, 2.15778188e+00], -# tspan = (0.0, 0.3), -# coverage_override = (maxiters=6,)) -# end -# -# @trixi_testset "elixir_euler_wall_bc_amr.jl" begin -# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_wall_bc_amr.jl"), -# l2 = [0.020291447969983396, 0.017479614254319948, 0.011387644425613437, 0.0514420126021293], -# linf = [0.3582779022370579, 0.32073537890751663, 0.221818049107692, 0.9209559420400415], -# tspan = (0.0, 0.15)) -# end -# -# @trixi_testset "elixir_euler_forward_step_amr.jl" begin -# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_forward_step_amr.jl"), -# l2 = [0.004194875320833303, 0.003785140699353966, 0.0013696609105790351, 0.03265268616046424], -# linf = [2.0585399781442852, 2.213428805506876, 3.862362410419163, 17.75187237459251], -# tspan = (0.0, 0.0001), -# rtol = 1.0e-7, -# skip_coverage=true) -# end -# -# @trixi_testset "elixir_euler_double_mach_amr.jl" begin -# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_double_mach_amr.jl"), -# l2 = [0.051359355290192046, 0.4266034859911273, 0.2438304855475594, 4.11487176105527], -# linf = [6.902000373057003, 53.95714139820832, 24.241610279839758, 561.0630401858057], -# tspan = (0.0, 0.0001), -# skip_coverage=true) -# end -# -# @trixi_testset "elixir_euler_supersonic_cylinder.jl" begin -# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_supersonic_cylinder.jl"), -# l2 = [0.026798021911954406, 0.05118546368109259, 0.03206703583774831, 0.19680026567208672], -# linf = [3.653905721692421, 4.285035711361009, 6.8544353186357645, 31.748244912257533], -# tspan = (0.0, 0.001), -# skip_coverage=true) -# end -# -# @trixi_testset "elixir_eulergravity_convergence.jl" begin -# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_eulergravity_convergence.jl"), -# l2 = [0.00024871265138964204, 0.0003370077102132591, 0.0003370077102131964, 0.0007231525513793697], -# linf = [0.0015813032944647087, 0.0020494288423820173, 0.0020494288423824614, 0.004793821195083758], -# tspan = (0.0, 0.1)) -# end -# -# @trixi_testset "elixir_shallowwater_source_terms.jl" begin -# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_source_terms.jl"), -# l2 = [9.168126407325352e-5, 0.0009795410115453788, 0.002546408320320785, 3.941189812642317e-6], -# linf = [0.0009903782521019089, 0.0059752684687262025, 0.010941106525454103, 1.2129488214718265e-5], -# tspan = (0.0, 0.1)) -# end -# -# @trixi_testset "elixir_mhd_alfven_wave.jl" begin -# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_mhd_alfven_wave.jl"), -# l2 = [1.0513414461545583e-5, 1.0517900957166411e-6, 1.0517900957304043e-6, 1.511816606372376e-6, -# 1.0443997728645063e-6, 7.879639064990798e-7, 7.879639065049896e-7, 1.0628631669056271e-6, -# 4.3382328912336153e-7], -# linf = [4.255466285174592e-5, 1.0029706745823264e-5, 1.0029706747467781e-5, 1.2122265939010224e-5, -# 5.4791097160444835e-6, 5.18922042269665e-6, 5.189220422141538e-6, 9.552667261422676e-6, -# 1.4237578427628152e-6]) -# end -# -# @trixi_testset "elixir_mhd_rotor.jl" begin -# @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_mhd_rotor.jl"), -# l2 = [0.4552084651735862, 0.8918048264575757, 0.832471223081887, 0.0, -# 0.9801264164951583, 0.10475690769435382, 0.1555132409149897, 0.0, -# 2.0597079362763556e-5], -# linf = [10.194181233788775, 18.25472397868819, 10.031307436191334, 0.0, -# 19.647239392277378, 1.3938810140985936, 1.8724965294853084, 0.0, -# 0.0016290067532561904], -# tspan = (0.0, 0.02)) -# end - end # Clean up afterwards: delete Trixi.jl output directory From d5d6298f9421e0497942fb59c16e2cd280623cd7 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Thu, 25 May 2023 17:46:15 +0200 Subject: [PATCH 014/128] Fixed Morton ordering bug in 2D at mortar interfaces. --- src/auxiliary/t8code.jl | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index 1d35b4ae1e..907c417f0e 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -39,7 +39,7 @@ function trixi_t8_count_interfaces(forest) # Get the number of trees that have elements of this process. num_local_trees = t8_forest_get_num_local_trees(forest) - current_index = 0 + current_index = t8_locidx_t(0) local_num_conform = 0 local_num_mortars = 0 @@ -82,7 +82,8 @@ function trixi_t8_count_interfaces(forest) if num_neighbors > 0 neighbor_level = t8_element_level(neighbor_scheme, neighbor_leafs[1]) - if level == neighbor_level && all(Int32(current_index) .<= neighbor_ielements) + # Conforming interface: The second condition ensures we only visit the interface once. + if level == neighbor_level && current_index <= neighbor_ielements[1] # TODO: Find a fix for the case: Single element on root level with periodic boundaries. # elseif level == neighbor_level && # (all(Int32(current_index) .< neighbor_ielements) || @@ -124,7 +125,7 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari # Get the number of trees that have elements of this process. num_local_trees = t8_forest_get_num_local_trees(forest) - current_index = 0 + current_index = t8_locidx_t(0) local_num_conform = 0 local_num_mortars = 0 @@ -182,7 +183,8 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari neighbor_level = t8_element_level(neighbor_scheme, neighbor_leafs[1]) # Conforming interface: The second condition ensures we only visit the interface once. - if level == neighbor_level && Int32(current_index) <= neighbor_ielements[1] + if level == neighbor_level && current_index <= neighbor_ielements[1] + # TODO: Find a fix for the case: Single element on root level with periodic boundaries. # elseif level == neighbor_level && # (all(Int32(current_index) .< neighbor_ielements) || # level == 0 && (iface == 0 || iface == 2 || iface == 4)) @@ -231,8 +233,7 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari mortar_id = local_num_mortars - # Last entry is the large element ... What a stupid convention! - # mortars.neighbor_ids[end, mortar_id] = ielement + 1 + # Last entry is the large element. mortars.neighbor_ids[end, mortar_id] = current_index + 1 # First `1:end-1` entries are the smaller elements. @@ -243,11 +244,15 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari # For orientation == 1, the large side needs to be indexed backwards # relative to the mortar. if side == 1 || orientation == 0 - # Forward indexing for small side or orientation == 0 + # Forward indexing for small side or orientation == 0. indexing = :i_forward else - # Backward indexing for large side with reversed orientation + # Backward indexing for large side with reversed orientation. indexing = :i_backward + # TODO: Fully understand what is going on here. Generalize this for 3D. + # Has something to do with Morton ordering. + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 end if faces[side] == 0 From 9c0db6f39ba020081bd03e87a63b7ebcf2093257 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Thu, 25 May 2023 17:53:29 +0200 Subject: [PATCH 015/128] Disabled `save_solution` callbacks and added more tests. --- ...ixir_advection_amr_solution_independent.jl | 11 +- .../elixir_advection_amr_unstructured_flag.jl | 100 +++++++++++++ .../t8code_2d_dgsem/elixir_advection_basic.jl | 10 +- .../elixir_advection_nonconforming_flag.jl | 10 +- .../elixir_advection_unstructured_flag.jl | 12 +- ..._euler_kelvin_helmholtz_instability_amr.jl | 3 +- ...e_terms_nonconforming_unstructured_flag.jl | 133 ++++++++++++++++++ test/test_t8code_2d.jl | 13 ++ 8 files changed, 273 insertions(+), 19 deletions(-) create mode 100644 examples/t8code_2d_dgsem/elixir_advection_amr_unstructured_flag.jl create mode 100644 examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl diff --git a/examples/t8code_2d_dgsem/elixir_advection_amr_solution_independent.jl b/examples/t8code_2d_dgsem/elixir_advection_amr_solution_independent.jl index e498d8aa75..12fc263cc0 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_amr_solution_independent.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_amr_solution_independent.jl @@ -118,10 +118,11 @@ analysis_callback = AnalysisCallback(semi, interval=analysis_interval, alive_callback = AliveCallback(analysis_interval=analysis_interval) -save_solution = SaveSolutionCallback(interval=100, - save_initial_solution=true, - save_final_solution=true, - solution_variables=cons2prim) +# Not implemented yet. +# save_solution = SaveSolutionCallback(interval=100, +# save_initial_solution=true, +# save_final_solution=true, +# solution_variables=cons2prim) amr_controller = ControllerThreeLevel(semi, TrixiExtension.IndicatorSolutionIndependent(semi), base_level=4, @@ -137,7 +138,7 @@ stepsize_callback = StepsizeCallback(cfl=1.6) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, - save_solution, + # save_solution, amr_callback, stepsize_callback); ############################################################################### diff --git a/examples/t8code_2d_dgsem/elixir_advection_amr_unstructured_flag.jl b/examples/t8code_2d_dgsem/elixir_advection_amr_unstructured_flag.jl new file mode 100644 index 0000000000..49406b44c5 --- /dev/null +++ b/examples/t8code_2d_dgsem/elixir_advection_amr_unstructured_flag.jl @@ -0,0 +1,100 @@ +using Downloads: download +using OrdinaryDiffEq +using Trixi + +############################################################################### +# Semidiscretization of the linear advection equation. + +advection_velocity = (0.2, -0.7) +equations = LinearScalarAdvectionEquation2D(advection_velocity) + +initial_condition = initial_condition_gauss + +boundary_condition = BoundaryConditionDirichlet(initial_condition) +boundary_conditions = Dict( + :all => boundary_condition +) + +solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) + +# Deformed rectangle that looks like a waving flag, lower and upper faces are +# sinus curves, left and right are vertical lines. +f1(s) = SVector(-5.0, 5 * s - 5.0) +f2(s) = SVector( 5.0, 5 * s + 5.0) +f3(s) = SVector(5 * s, -5.0 + 5 * sin(0.5 * pi * s)) +f4(s) = SVector(5 * s, 5.0 + 5 * sin(0.5 * pi * s)) +faces = (f1, f2, f3, f4) + +# This creates a mapping that transforms [-1, 1]^2 to the domain with the faces +# defined above. It generally doesn't work for meshes loaded from mesh files +# because these can be meshes of arbitrary domains, but the mesh below is +# specifically built on the domain [-1, 1]^2. +Trixi.validate_faces(faces) +mapping_flag = Trixi.transfinite_mapping(faces) + +# Unstructured mesh with 24 cells of the square domain [-1, 1]^n +mesh_file = joinpath(@__DIR__, "square_unstructured_2.inp") +isfile(mesh_file) || download("https://gist.githubusercontent.com/efaulhaber/63ff2ea224409e55ee8423b3a33e316a/raw/7db58af7446d1479753ae718930741c47a3b79b7/square_unstructured_2.inp", + mesh_file) + +# INP mesh files are only support by p4est. Hence, we +# create a p4est connecvity object first from which +# we can create a t8code mesh. +conn = Trixi.read_inp_p4est(mesh_file,Val(2)) + +mesh = T8codeMesh{2}(conn, polydeg=3, + mapping=mapping_flag, + initial_refinement_level=1) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, + boundary_conditions=boundary_conditions) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 10.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval=analysis_interval, + extra_analysis_integrals=(entropy,)) + +alive_callback = AliveCallback(analysis_interval=analysis_interval) + +# Not implemented yet. +# save_restart = SaveRestartCallback(interval=100, +# save_final_restart=true) + +# Not implemented yet. +# save_solution = SaveSolutionCallback(interval=100, +# save_initial_solution=true, +# save_final_solution=true, +# solution_variables=cons2prim) + +amr_controller = ControllerThreeLevel(semi, IndicatorMax(semi, variable=first), + base_level=1, + med_level=2, med_threshold=0.1, + max_level=3, max_threshold=0.6) +amr_callback = AMRCallback(semi, amr_controller, + interval=5, + adapt_initial_condition=true, + adapt_initial_condition_only_refine=true) + +stepsize_callback = StepsizeCallback(cfl=0.7) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + # save_restart, save_solution, + amr_callback, stepsize_callback) + + +############################################################################### +# Run the simulation. + +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep=false, callback=callbacks); + +summary_callback() # print the timer summary diff --git a/examples/t8code_2d_dgsem/elixir_advection_basic.jl b/examples/t8code_2d_dgsem/elixir_advection_basic.jl index 5ef8c68fcb..ed7ae68c78 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_basic.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_basic.jl @@ -42,15 +42,17 @@ summary_callback = SummaryCallback() # The AnalysisCallback allows to analyse the solution in regular intervals and prints the results analysis_callback = AnalysisCallback(semi, interval=100) -# The SaveSolutionCallback allows to save the solution to a file in regular intervals -save_solution = SaveSolutionCallback(interval=100, - solution_variables=cons2prim) +# Not implemented yet. +# # The SaveSolutionCallback allows to save the solution to a file in regular intervals +# save_solution = SaveSolutionCallback(interval=100, +# solution_variables=cons2prim) # The StepsizeCallback handles the re-calculation of the maximum Δt after each time step stepsize_callback = StepsizeCallback(cfl=1.6) # Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver -callbacks = CallbackSet(summary_callback, analysis_callback, save_solution, stepsize_callback) +callbacks = CallbackSet(summary_callback, analysis_callback, # save_solution, + stepsize_callback) ############################################################################### diff --git a/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl b/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl index a44b8aae13..6f341facc1 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl @@ -91,15 +91,17 @@ summary_callback = SummaryCallback() # The AnalysisCallback allows to analyse the solution in regular intervals and prints the results analysis_callback = AnalysisCallback(semi, interval=100) -# The SaveSolutionCallback allows to save the solution to a file in regular intervals -save_solution = SaveSolutionCallback(interval=100, - solution_variables=cons2prim) +# Not implemented yet. +# # The SaveSolutionCallback allows to save the solution to a file in regular intervals +# save_solution = SaveSolutionCallback(interval=100, +# solution_variables=cons2prim) # The StepsizeCallback handles the re-calculation of the maximum Δt after each time step stepsize_callback = StepsizeCallback(cfl=1.6) # Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver -callbacks = CallbackSet(summary_callback, analysis_callback, save_solution, stepsize_callback) +callbacks = CallbackSet(summary_callback, analysis_callback, # save_solution, + stepsize_callback) ############################################################################### diff --git a/examples/t8code_2d_dgsem/elixir_advection_unstructured_flag.jl b/examples/t8code_2d_dgsem/elixir_advection_unstructured_flag.jl index e6d498f943..e0e0dd3e5c 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_unstructured_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_unstructured_flag.jl @@ -61,10 +61,11 @@ summary_callback = SummaryCallback() # prints the results. analysis_callback = AnalysisCallback(semi, interval=100) -# The SaveSolutionCallback allows to save the solution to a file in regular -# intervals. -save_solution = SaveSolutionCallback(interval=100, - solution_variables=cons2prim) +# Not implemented yet. +# # The SaveSolutionCallback allows to save the solution to a file in regular +# # intervals. +# save_solution = SaveSolutionCallback(interval=100, +# solution_variables=cons2prim) # The StepsizeCallback handles the re-calculation of the maximum Δt after each # time step. @@ -72,7 +73,8 @@ stepsize_callback = StepsizeCallback(cfl=1.4) # Create a CallbackSet to collect all callbacks such that they can be passed to # the ODE solver. -callbacks = CallbackSet(summary_callback, analysis_callback, save_solution, stepsize_callback) +callbacks = CallbackSet(summary_callback, analysis_callback, # save_solution, + stepsize_callback) ############################################################################### # Run the simulation. diff --git a/examples/t8code_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_amr.jl b/examples/t8code_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_amr.jl index 5d97f2fa97..885473a6eb 100644 --- a/examples/t8code_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_amr.jl +++ b/examples/t8code_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_amr.jl @@ -106,7 +106,8 @@ end ############################################################################### # ODE solvers, callbacks etc. -tspan = (0.0, 10.0) +tspan = (0.0, 0.1) +# tspan = (0.0, 10.0) ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() diff --git a/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl b/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl new file mode 100644 index 0000000000..a9282f61ee --- /dev/null +++ b/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl @@ -0,0 +1,133 @@ +using Downloads: download +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations + +equations = CompressibleEulerEquations2D(1.4) + +initial_condition = initial_condition_convergence_test + +source_terms = source_terms_convergence_test + +# BCs must be passed as Dict +boundary_condition = BoundaryConditionDirichlet(initial_condition) +boundary_conditions = Dict( + :all => boundary_condition +) + +solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) + +# Deformed rectangle that looks like a waving flag, +# lower and upper faces are sinus curves, left and right are vertical lines. +f1(s) = SVector(-1.0, s - 1.0) +f2(s) = SVector( 1.0, s + 1.0) +f3(s) = SVector(s, -1.0 + sin(0.5 * pi * s)) +f4(s) = SVector(s, 1.0 + sin(0.5 * pi * s)) +faces = (f1, f2, f3, f4) + +Trixi.validate_faces(faces) +mapping_flag = Trixi.transfinite_mapping(faces) + +# Get the uncurved mesh from a file (downloads the file if not available locally) +# Unstructured mesh with 24 cells of the square domain [-1, 1]^n +mesh_file = joinpath(@__DIR__, "square_unstructured_2.inp") +isfile(mesh_file) || download("https://gist.githubusercontent.com/efaulhaber/63ff2ea224409e55ee8423b3a33e316a/raw/7db58af7446d1479753ae718930741c47a3b79b7/square_unstructured_2.inp", + mesh_file) + +# INP mesh files are only support by p4est. Hence, we +# create a p4est connecvity object first from which +# we can create a t8code mesh. +conn = Trixi.read_inp_p4est(mesh_file,Val(2)) + +mesh = T8codeMesh{2}(conn, polydeg=3, + mapping=mapping_flag, + initial_refinement_level=0) + +function adapt_callback(forest, + forest_from, + which_tree, + lelement_id, + ts, + is_family, + num_elements, + elements_ptr) :: Cint + + vertex = Vector{Cdouble}(undef,3) + + elements = unsafe_wrap(Array, elements_ptr, num_elements) + + Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) + + level = Trixi.t8_element_level(ts,elements[1]) + + # TODO: Make this condition more general. + if vertex[1] < 1e-8 && vertex[2] < 1e-8 && level < 2 + # return true (refine) + return 1 + else + # return false (don't refine) + return 0 + end +end + +@Trixi.T8_ASSERT(Trixi.t8_forest_is_committed(mesh.forest) != 0); + +# Init new forest. +new_forest_ref = Ref{Trixi.t8_forest_t}() +Trixi.t8_forest_init(new_forest_ref); +new_forest = new_forest_ref[] + +let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0 + Trixi.t8_forest_set_user_data(new_forest, C_NULL) + Trixi.t8_forest_set_adapt(new_forest, mesh.forest, @Trixi.t8_adapt_callback(adapt_callback), recursive) + Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) + Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) + # t8_forest_set_ghost(new_forest, 1, T8_GHOST_FACES); + Trixi.t8_forest_commit(new_forest) +end + +mesh.forest = new_forest + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, + source_terms=source_terms, + boundary_conditions=boundary_conditions) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 1.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval=analysis_interval) + +alive_callback = AliveCallback(analysis_interval=analysis_interval) + +# Not implemted yet. +# save_restart = SaveRestartCallback(interval=100, +# save_final_restart=true) +# +# Not implemented yet. +# save_solution = SaveSolutionCallback(interval=100, +# save_initial_solution=true, +# save_final_solution=true, +# solution_variables=cons2prim) + +stepsize_callback = StepsizeCallback(cfl=0.8) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + # save_restart, save_solution, + stepsize_callback, +) +############################################################################### +# run the simulation + +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep=false, callback=callbacks); +summary_callback() # print the timer summary diff --git a/test/test_t8code_2d.jl b/test/test_t8code_2d.jl index 66a200a949..23cec35673 100644 --- a/test/test_t8code_2d.jl +++ b/test/test_t8code_2d.jl @@ -41,6 +41,13 @@ mkdir(outdir) linf = [0.007438525029884735]) end + @trixi_testset "elixir_advection_amr_unstructured_flag.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_amr_unstructured_flag.jl"), + l2 = [0.001993165013217687], + linf = [0.032891018571625796], + coverage_override = (maxiters=6,)) + end + @trixi_testset "elixir_advection_amr_solution_independent.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_amr_solution_independent.jl"), # Expected errors are exactly the same as with StructuredMesh! @@ -49,6 +56,12 @@ mkdir(outdir) coverage_override = (maxiters=6,)) end + @trixi_testset "elixir_euler_source_terms_nonconforming_unstructured_flag.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_source_terms_nonconforming_unstructured_flag.jl"), + l2 = [0.0034516244508588046, 0.0023420334036925493, 0.0024261923964557187, 0.004731710454271893], + linf = [0.04155789011775046, 0.024772109862748914, 0.03759938693042297, 0.08039824959535657]) + end + end # Clean up afterwards: delete Trixi.jl output directory From 1440f90c12d1cb48d1d43ca54570198def4f8f06 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Fri, 26 May 2023 14:39:37 +0200 Subject: [PATCH 016/128] Added more tests. --- .../t8code_2d_dgsem/elixir_advection_basic.jl | 1 - .../elixir_euler_free_stream.jl | 129 +++++++++++++ ..._euler_kelvin_helmholtz_instability_amr.jl | 177 ------------------ .../t8code_2d_dgsem/elixir_euler_sedov.jl | 102 ++++++++++ .../elixir_euler_shockcapturing_ec.jl | 73 ++++++++ ...e_terms_nonconforming_unstructured_flag.jl | 2 +- .../t8code_2d_dgsem/elixir_mhd_alfven_wave.jl | 60 ++++++ examples/t8code_2d_dgsem/elixir_mhd_rotor.jl | 141 ++++++++++++++ .../elixir_shallowwater_source_terms.jl | 60 ++++++ src/solvers/dgsem_structured/dg_2d.jl | 4 +- test/test_t8code_2d.jl | 50 +++++ 11 files changed, 618 insertions(+), 181 deletions(-) create mode 100644 examples/t8code_2d_dgsem/elixir_euler_free_stream.jl delete mode 100644 examples/t8code_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_amr.jl create mode 100644 examples/t8code_2d_dgsem/elixir_euler_sedov.jl create mode 100644 examples/t8code_2d_dgsem/elixir_euler_shockcapturing_ec.jl create mode 100644 examples/t8code_2d_dgsem/elixir_mhd_alfven_wave.jl create mode 100644 examples/t8code_2d_dgsem/elixir_mhd_rotor.jl create mode 100644 examples/t8code_2d_dgsem/elixir_shallowwater_source_terms.jl diff --git a/examples/t8code_2d_dgsem/elixir_advection_basic.jl b/examples/t8code_2d_dgsem/elixir_advection_basic.jl index ed7ae68c78..e9827db8c9 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_basic.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_basic.jl @@ -20,7 +20,6 @@ mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) trees_per_dimension = (8, 8) -# Create T8codeMesh with 8 x 8 trees and 16 x 16 elements mesh = T8codeMesh(trees_per_dimension, polydeg=3, mapping=mapping, initial_refinement_level=1) diff --git a/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl b/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl new file mode 100644 index 0000000000..2beb73f818 --- /dev/null +++ b/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl @@ -0,0 +1,129 @@ +using Downloads: download +using OrdinaryDiffEq +using Trixi + +############################################################################### +# Semidiscretization of the compressible Euler equations. + +equations = CompressibleEulerEquations2D(1.4) + +initial_condition = initial_condition_constant + +solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) + +# Mapping as described in https://arxiv.org/abs/2012.12040 but reduced to 2D +function mapping(xi_, eta_) + # Transform input variables between -1 and 1 onto [0,3] + xi = 1.5 * xi_ + 1.5 + eta = 1.5 * eta_ + 1.5 + + y = eta + 3/8 * (cos(1.5 * pi * (2 * xi - 3)/3) * + cos(0.5 * pi * (2 * eta - 3)/3)) + + x = xi + 3/8 * (cos(0.5 * pi * (2 * xi - 3)/3) * + cos(2 * pi * (2 * y - 3)/3)) + + return SVector(x, y) +end + +############################################################################### +# Get the uncurved mesh from a file (downloads the file if not available locally) + +# Unstructured mesh with 48 cells of the square domain [-1, 1]^n +mesh_file = joinpath(@__DIR__, "square_unstructured_1.inp") +isfile(mesh_file) || download("https://gist.githubusercontent.com/efaulhaber/a075f8ec39a67fa9fad8f6f84342cbca/raw/a7206a02ed3a5d3cadacd8d9694ac154f9151db7/square_unstructured_1.inp", + mesh_file) + +# INP mesh files are only support by p4est. Hence, we +# create a p4est connecvity object first from which +# we can create a t8code mesh. +conn = Trixi.read_inp_p4est(mesh_file,Val(2)) + +mesh = T8codeMesh{2}(conn, polydeg=3, + mapping=mapping, + initial_refinement_level=1) + +function adapt_callback(forest, + forest_from, + which_tree, + lelement_id, + ts, + is_family, + num_elements, + elements_ptr) :: Cint + + vertex = Vector{Cdouble}(undef,3) + + elements = unsafe_wrap(Array, elements_ptr, num_elements) + + Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) + + level = Trixi.t8_element_level(ts,elements[1]) + + # TODO: Make this condition more general. + if vertex[1] < 1e-8 && vertex[2] < 1e-8 && level < 3 + # return true (refine) + return 1 + else + # return false (don't refine) + return 0 + end +end + +@Trixi.T8_ASSERT(Trixi.t8_forest_is_committed(mesh.forest) != 0); + +# Init new forest. +new_forest_ref = Ref{Trixi.t8_forest_t}() +Trixi.t8_forest_init(new_forest_ref); +new_forest = new_forest_ref[] + +let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0 + Trixi.t8_forest_set_user_data(new_forest, C_NULL) + Trixi.t8_forest_set_adapt(new_forest, mesh.forest, @Trixi.t8_adapt_callback(adapt_callback), recursive) + Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) + Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) + # t8_forest_set_ghost(new_forest, 1, T8_GHOST_FACES); + Trixi.t8_forest_commit(new_forest) +end + +mesh.forest = new_forest + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, + boundary_conditions=Dict( + :all => BoundaryConditionDirichlet(initial_condition) + )) + + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 1.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval=analysis_interval) + +alive_callback = AliveCallback(analysis_interval=analysis_interval) + +# Not implemented yet. +# save_solution = SaveSolutionCallback(interval=100, +# save_initial_solution=true, +# save_final_solution=true, +# solution_variables=cons2prim) + +stepsize_callback = StepsizeCallback(cfl=2.0) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + # save_solution, + stepsize_callback) + +############################################################################### +# run the simulation + +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep=false, callback=callbacks); +summary_callback() # print the timer summary diff --git a/examples/t8code_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_amr.jl b/examples/t8code_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_amr.jl deleted file mode 100644 index 885473a6eb..0000000000 --- a/examples/t8code_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_amr.jl +++ /dev/null @@ -1,177 +0,0 @@ -using Plots -using Trixi -using Printf -using OrdinaryDiffEq - -############################################################################### -# semidiscretization of the compressible Euler equations -gamma = 1.4 -equations = CompressibleEulerEquations2D(gamma) - -""" - initial_condition_kelvin_helmholtz_instability(x, t, equations::CompressibleEulerEquations2D) - -A version of the classical Kelvin-Helmholtz instability based on -- Andrés M. Rueda-Ramírez, Gregor J. Gassner (2021) - A Subcell Finite Volume Positivity-Preserving Limiter for DGSEM Discretizations - of the Euler Equations - [arXiv: 2102.06017](https://arxiv.org/abs/2102.06017) -""" -function initial_condition_kelvin_helmholtz_instability(x, t, equations::CompressibleEulerEquations2D) - # change discontinuity to tanh - # typical resolution 128^2, 256^2 - # domain size is [-1,+1]^2 - slope = 15 - amplitude = 0.02 - B = tanh(slope * x[2] + 7.5) - tanh(slope * x[2] - 7.5) - rho = 0.5 + 0.75 * B - v1 = 0.5 * (B - 1) - v2 = 0.1 * sin(2 * pi * x[1]) - p = 1.0 - return prim2cons(SVector(rho, v1, v2, p), equations) -end - -my_initial_condition = initial_condition_kelvin_helmholtz_instability - -surface_flux = flux_lax_friedrichs -volume_flux = flux_ranocha - -polydeg = 3 -inilevel = 2 -maxlevel = 6 - -basis = LobattoLegendreBasis(polydeg) -indicator_sc = IndicatorHennemannGassner(equations, basis, - alpha_max=0.002, - alpha_min=0.0001, - alpha_smooth=true, - variable=Trixi.density) - -volume_integral = VolumeIntegralShockCapturingHG(indicator_sc; - volume_flux_dg=volume_flux, - volume_flux_fv=surface_flux) - -# volume_integral = VolumeIntegralWeakForm() - -solver = DGSEM(basis, surface_flux, volume_integral) - -# Warped rectangle that looks like a waving flag, -f1(s) = SVector(-1.0 + 0.1 * sin( pi * s), s) -f2(s) = SVector( 1.0 + 0.1 * sin( pi * s), s) -f3(s) = SVector(s, -1.0 + 0.1 * sin( pi * s)) -f4(s) = SVector(s, 1.0 + 0.1 * sin( pi * s)) - -faces = (f1, f2, f3, f4) - -Trixi.validate_faces(faces) -mapping_flag = Trixi.transfinite_mapping(faces) - -if true - - # Simple periodic, n x n mesh. - - trees_per_dimension = (2, 2) - mesh = T8codeMesh(trees_per_dimension,polydeg=polydeg, initial_refinement_level=inilevel, mapping=mapping_flag, periodicity=true) - semi = SemidiscretizationHyperbolic(mesh, equations, my_initial_condition, solver) - -else - - # Unstructured, crazy-looking mesh read in by a 'msh' file generated with 'gmsh'. - - # It sometimes Trixi crashes for meshes loaded from 'gmesh' files because they can have - # flipped domains, e.g. [-1, 1] x [ 1,-1]. This is the case for the loaded mesh here. - # The following linear transformation flips the domain back. Cool, eh?! - coordinates_min = (-1.0, 1.0) - coordinates_max = ( 1.0, -1.0) - - mapping_flip = Trixi.coordinates2mapping(coordinates_min, coordinates_max) - - my_mapping(x,y) = mapping_flag(mapping_flip(x,y)...) - - mesh_file = joinpath(@__DIR__,"meshfiles/unstructured_quadrangle.msh") - - boundary_conditions = Dict( - :all => BoundaryConditionDirichlet(my_initial_condition), - ) - - mesh = T8codeMesh{2}(mesh_file, polydeg=polydeg, - mapping=my_mapping, - initial_refinement_level=inilevel) - - semi = SemidiscretizationHyperbolic(mesh, equations, my_initial_condition, solver, - boundary_conditions=boundary_conditions) - -end - -############################################################################### -# ODE solvers, callbacks etc. - -tspan = (0.0, 0.1) -# tspan = (0.0, 10.0) -ode = semidiscretize(semi, tspan) - -summary_callback = SummaryCallback() - -analysis_interval = 100 -analysis_callback = AnalysisCallback(semi, interval=analysis_interval) - -alive_callback = AliveCallback(analysis_interval=analysis_interval) - -# Not supported yet. -# save_solution = SaveSolutionCallback(interval=100, -# save_initial_solution=true, -# save_final_solution=true, -# solution_variables=cons2prim) - -amr_indicator = IndicatorLöhner(semi, variable=Trixi.density) - -amr_controller = ControllerThreeLevel(semi, amr_indicator, - base_level=0, - med_level=0, med_threshold=0.05, - max_level=maxlevel, max_threshold=0.1) - -amr_callback = AMRCallback(semi, amr_controller, - interval=1, - adapt_initial_condition=true, - adapt_initial_condition_only_refine=true) - -stepsize_callback = StepsizeCallback(cfl=0.8) - -function my_save_plot(plot_data, variable_names; - show_mesh=true, plot_arguments=Dict{Symbol,Any}(), - time=nothing, timestep=nothing) - - title = @sprintf("2D KHI | Trixi.jl | 4th-order DG | AMR w/ t8code: t = %3.2f", time) - - sol = plot_data["rho"] - - Plots.plot(sol, - clim=(0.25,2.25), - colorbar_title="\ndensity", - title=title,titlefontsize=10, - dpi=300, - ) - Plots.plot!(getmesh(plot_data),linewidth=0.5) - - mkpath("out") - filename = joinpath("out", @sprintf("solution_%06d.png", timestep)) - Plots.savefig(filename) -end - -visualization_callback = VisualizationCallback(plot_creator=my_save_plot,interval=50, clims=(0,1.1), show_mesh=true) - -callbacks = CallbackSet(summary_callback, - analysis_callback, alive_callback, - # save_solution, - amr_callback, - visualization_callback, - stepsize_callback) - -############################################################################### -# run the simulation - -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); - -summary_callback() # print the timer summary diff --git a/examples/t8code_2d_dgsem/elixir_euler_sedov.jl b/examples/t8code_2d_dgsem/elixir_euler_sedov.jl new file mode 100644 index 0000000000..117c54804b --- /dev/null +++ b/examples/t8code_2d_dgsem/elixir_euler_sedov.jl @@ -0,0 +1,102 @@ +using OrdinaryDiffEq +using Trixi + +############################################################################### +# Semidiscretization of the compressible Euler equations. + +equations = CompressibleEulerEquations2D(1.4) + +""" + initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations2D) + +The Sedov blast wave setup based on Flash +- http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000 +""" +function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations2D) + # Set up polar coordinates + inicenter = SVector(0.0, 0.0) + x_norm = x[1] - inicenter[1] + y_norm = x[2] - inicenter[2] + r = sqrt(x_norm^2 + y_norm^2) + + # Setup based on http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000 + r0 = 0.21875 # = 3.5 * smallest dx (for domain length=4 and max-ref=6) + E = 1.0 + p0_inner = 3 * (equations.gamma - 1) * E / (3 * pi * r0^2) + p0_outer = 1.0e-5 # = true Sedov setup + + # Calculate primitive variables + rho = 1.0 + v1 = 0.0 + v2 = 0.0 + p = r > r0 ? p0_outer : p0_inner + + return prim2cons(SVector(rho, v1, v2, p), equations) +end + +initial_condition = initial_condition_sedov_blast_wave + +# Get the DG approximation space +surface_flux = flux_lax_friedrichs +volume_flux = flux_ranocha +polydeg = 4 +basis = LobattoLegendreBasis(polydeg) +indicator_sc = IndicatorHennemannGassner(equations, basis, + alpha_max=1.0, + alpha_min=0.001, + alpha_smooth=true, + variable=density_pressure) +volume_integral = VolumeIntegralShockCapturingHG(indicator_sc; + volume_flux_dg=volume_flux, + volume_flux_fv=surface_flux) + +solver = DGSEM(polydeg=polydeg, surface_flux=surface_flux, volume_integral=volume_integral) + +############################################################################### + +coordinates_min = (-1.0, -1.0) +coordinates_max = ( 1.0, 1.0) + +mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) + +trees_per_dimension = (4, 4) + +mesh = T8codeMesh(trees_per_dimension, polydeg=4, + mapping=mapping, + initial_refinement_level=2, periodicity=true) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 12.5) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 300 +analysis_callback = AnalysisCallback(semi, interval=analysis_interval) + +alive_callback = AliveCallback(analysis_interval=analysis_interval) + +# Not implemented yet. +# save_solution = SaveSolutionCallback(interval=300, +# save_initial_solution=true, +# save_final_solution=true) + +stepsize_callback = StepsizeCallback(cfl=0.5) + +callbacks = CallbackSet(summary_callback, + analysis_callback, + alive_callback, + # save_solution, + stepsize_callback) + +############################################################################### +# run the simulation + +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep=false, callback=callbacks); +summary_callback() # print the timer summary diff --git a/examples/t8code_2d_dgsem/elixir_euler_shockcapturing_ec.jl b/examples/t8code_2d_dgsem/elixir_euler_shockcapturing_ec.jl new file mode 100644 index 0000000000..e80d400fae --- /dev/null +++ b/examples/t8code_2d_dgsem/elixir_euler_shockcapturing_ec.jl @@ -0,0 +1,73 @@ +using OrdinaryDiffEq +using Trixi + +############################################################################### +# Semidiscretization of the compressible Euler equations. + +equations = CompressibleEulerEquations2D(1.4) + +initial_condition = initial_condition_weak_blast_wave + +surface_flux = flux_ranocha +volume_flux = flux_ranocha +polydeg = 4 +basis = LobattoLegendreBasis(polydeg) +indicator_sc = IndicatorHennemannGassner(equations, basis, + alpha_max=1.0, + alpha_min=0.001, + alpha_smooth=true, + variable=density_pressure) +volume_integral = VolumeIntegralShockCapturingHG(indicator_sc; + volume_flux_dg=volume_flux, + volume_flux_fv=surface_flux) + +solver = DGSEM(polydeg=polydeg, surface_flux=surface_flux, volume_integral=volume_integral) + +############################################################################### + +coordinates_min = (-1.0, -1.0) +coordinates_max = ( 1.0, 1.0) + +mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) + +trees_per_dimension = (4, 4) + +mesh = T8codeMesh(trees_per_dimension, polydeg=4, + mapping=mapping, + initial_refinement_level=2,periodicity=true) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 2.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval=analysis_interval) + +alive_callback = AliveCallback(analysis_interval=analysis_interval) + +# Not implemented yet. +# save_solution = SaveSolutionCallback(interval=100, +# save_initial_solution=true, +# save_final_solution=true, +# solution_variables=cons2prim) + +stepsize_callback = StepsizeCallback(cfl=1.0) + +callbacks = CallbackSet(summary_callback, + analysis_callback, + alive_callback, + # save_solution, + stepsize_callback) +############################################################################### +# run the simulation + +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep=false, callback=callbacks); +summary_callback() # print the timer summary diff --git a/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl b/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl index a9282f61ee..6dba1c42b2 100644 --- a/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl @@ -43,7 +43,7 @@ conn = Trixi.read_inp_p4est(mesh_file,Val(2)) mesh = T8codeMesh{2}(conn, polydeg=3, mapping=mapping_flag, - initial_refinement_level=0) + initial_refinement_level=1) function adapt_callback(forest, forest_from, diff --git a/examples/t8code_2d_dgsem/elixir_mhd_alfven_wave.jl b/examples/t8code_2d_dgsem/elixir_mhd_alfven_wave.jl new file mode 100644 index 0000000000..463f916fa2 --- /dev/null +++ b/examples/t8code_2d_dgsem/elixir_mhd_alfven_wave.jl @@ -0,0 +1,60 @@ +using OrdinaryDiffEq +using Trixi + +############################################################################### +# Semidiscretization of the compressible ideal GLM-MHD equations. + +gamma = 5/3 +equations = IdealGlmMhdEquations2D(gamma) + +initial_condition = initial_condition_convergence_test + +# Get the DG approximation space +volume_flux = (flux_central, flux_nonconservative_powell) +solver = DGSEM(polydeg=4, surface_flux=(flux_hll, flux_nonconservative_powell), + volume_integral=VolumeIntegralFluxDifferencing(volume_flux)) + +coordinates_min = (0.0 , 0.0 ) +coordinates_max = (sqrt(2.0), sqrt(2.0)) + +mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) + +trees_per_dimension = (8, 8) + +mesh = T8codeMesh(trees_per_dimension, polydeg=3, + mapping=mapping, + initial_refinement_level=0, periodicity=true) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 1.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval=analysis_interval) + +alive_callback = AliveCallback(analysis_interval=analysis_interval) + +cfl = 0.9 +stepsize_callback = StepsizeCallback(cfl=cfl) + +glm_speed_callback = GlmSpeedCallback(glm_scale=0.5, cfl=cfl) + +callbacks = CallbackSet(summary_callback, + analysis_callback, + alive_callback, + stepsize_callback, + glm_speed_callback) + +############################################################################### +# run the simulation + +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep=false, callback=callbacks); +summary_callback() # print the timer summary diff --git a/examples/t8code_2d_dgsem/elixir_mhd_rotor.jl b/examples/t8code_2d_dgsem/elixir_mhd_rotor.jl new file mode 100644 index 0000000000..606eda2125 --- /dev/null +++ b/examples/t8code_2d_dgsem/elixir_mhd_rotor.jl @@ -0,0 +1,141 @@ +using Downloads: download +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible ideal GLM-MHD equations +equations = IdealGlmMhdEquations2D(1.4) + +""" + initial_condition_rotor(x, t, equations::IdealGlmMhdEquations2D) + +The classical MHD rotor test case. Here, the setup is taken from +- Dominik Derigs, Gregor J. Gassner, Stefanie Walch & Andrew R. Winters (2018) + Entropy Stable Finite Volume Approximations for Ideal Magnetohydrodynamics + [doi: 10.1365/s13291-018-0178-9](https://doi.org/10.1365/s13291-018-0178-9) +""" +function initial_condition_rotor(x, t, equations::IdealGlmMhdEquations2D) + # setup taken from Derigs et al. DMV article (2018) + # domain must be [0, 1] x [0, 1], γ = 1.4 + dx = x[1] - 0.5 + dy = x[2] - 0.5 + r = sqrt(dx^2 + dy^2) + f = (0.115 - r)/0.015 + if r <= 0.1 + rho = 10.0 + v1 = -20.0*dy + v2 = 20.0*dx + elseif r >= 0.115 + rho = 1.0 + v1 = 0.0 + v2 = 0.0 + else + rho = 1.0 + 9.0*f + v1 = -20.0*f*dy + v2 = 20.0*f*dx + end + v3 = 0.0 + p = 1.0 + B1 = 5.0/sqrt(4.0*pi) + B2 = 0.0 + B3 = 0.0 + psi = 0.0 + return prim2cons(SVector(rho, v1, v2, v3, p, B1, B2, B3, psi), equations) +end +initial_condition = initial_condition_rotor + +surface_flux = (flux_lax_friedrichs, flux_nonconservative_powell) +volume_flux = (flux_hindenlang_gassner, flux_nonconservative_powell) +polydeg = 4 +basis = LobattoLegendreBasis(polydeg) +indicator_sc = IndicatorHennemannGassner(equations, basis, + alpha_max=0.5, + alpha_min=0.001, + alpha_smooth=true, + variable=density_pressure) +volume_integral = VolumeIntegralShockCapturingHG(indicator_sc; + volume_flux_dg=volume_flux, + volume_flux_fv=surface_flux) +solver = DGSEM(basis, surface_flux, volume_integral) + +# Affine type mapping to take the [-1,1]^2 domain from the mesh file +# and put it onto the rotor domain [0,1]^2 and then warp it with a mapping +# as described in https://arxiv.org/abs/2012.12040 +function mapping_twist(xi, eta) + y = 0.5 * (eta + 1.0) + 0.05 * cos(1.5 * pi * (2.0 * xi - 1.0)) * cos(0.5 * pi * (2.0 * eta - 1.0)) + x = 0.5 * (xi + 1.0) + 0.05 * cos(0.5 * pi * (2.0 * xi - 1.0)) * cos(2.0 * pi * y) + return SVector(x, y) +end + + +mesh_file = joinpath(@__DIR__, "square_unstructured_2.inp") +isfile(mesh_file) || download("https://gist.githubusercontent.com/efaulhaber/63ff2ea224409e55ee8423b3a33e316a/raw/7db58af7446d1479753ae718930741c47a3b79b7/square_unstructured_2.inp", + mesh_file) + +# INP mesh files are only support by p4est. Hence, we +# create a p4est connecvity object first from which +# we can create a t8code mesh. +conn = Trixi.read_inp_p4est(mesh_file,Val(2)) + +mesh = T8codeMesh{2}(conn, polydeg=4, + mapping=mapping_twist, + initial_refinement_level=1) + +boundary_condition = BoundaryConditionDirichlet(initial_condition) +boundary_conditions = Dict( :all => boundary_condition ) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, + boundary_conditions=boundary_conditions) + + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 0.15) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval=analysis_interval) + +alive_callback = AliveCallback(analysis_interval=analysis_interval) + +# Not implemented yet. +# save_solution = SaveSolutionCallback(interval=100, +# save_initial_solution=true, +# save_final_solution=true, +# solution_variables=cons2prim) + +amr_indicator = IndicatorLöhner(semi, + variable=density_pressure) + +amr_controller = ControllerThreeLevel(semi, amr_indicator, + base_level=1, + med_level =3, med_threshold=0.05, + max_level =5, max_threshold=0.1) +amr_callback = AMRCallback(semi, amr_controller, + interval=5, + adapt_initial_condition=true, + adapt_initial_condition_only_refine=true) + +cfl = 0.5 +stepsize_callback = StepsizeCallback(cfl=cfl) + +glm_speed_callback = GlmSpeedCallback(glm_scale=0.5, cfl=cfl) + +callbacks = CallbackSet(summary_callback, + analysis_callback, + alive_callback, + # save_solution, + amr_callback, + stepsize_callback, + glm_speed_callback) + +############################################################################### +# run the simulation + +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep=false, callback=callbacks); +summary_callback() # print the timer summary diff --git a/examples/t8code_2d_dgsem/elixir_shallowwater_source_terms.jl b/examples/t8code_2d_dgsem/elixir_shallowwater_source_terms.jl new file mode 100644 index 0000000000..c19f440ebc --- /dev/null +++ b/examples/t8code_2d_dgsem/elixir_shallowwater_source_terms.jl @@ -0,0 +1,60 @@ +using OrdinaryDiffEq +using Trixi + +############################################################################### +# Semidiscretization of the shallow water equations. + +equations = ShallowWaterEquations2D(gravity_constant=9.81) + +initial_condition = initial_condition_convergence_test # MMS EOC test + + +############################################################################### +# Get the DG approximation space + +volume_flux = (flux_wintermeyer_etal, flux_nonconservative_wintermeyer_etal) +solver = DGSEM(polydeg=3, surface_flux=(flux_lax_friedrichs, flux_nonconservative_fjordholm_etal), + volume_integral=VolumeIntegralFluxDifferencing(volume_flux)) + +############################################################################### +# Get the P4estMesh and setup a periodic mesh + +coordinates_min = (0.0, 0.0) # minimum coordinates (min(x), min(y)) +coordinates_max = (sqrt(2.0), sqrt(2.0)) # maximum coordinates (max(x), max(y)) + +mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) + +trees_per_dimension = (8, 8) + +mesh = T8codeMesh(trees_per_dimension, polydeg=3, + mapping=mapping, + initial_refinement_level=1) + +# A semidiscretization collects data structures and functions for the spatial discretization +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, + source_terms=source_terms_convergence_test) + + +############################################################################### +# ODE solvers, callbacks etc. + +# Create ODE problem with time span from 0.0 to 1.0 +tspan = (0.0, 1.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 500 +analysis_callback = AnalysisCallback(semi, interval=analysis_interval) + +alive_callback = AliveCallback(analysis_interval=analysis_interval) + +callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback) + +############################################################################### +# run the simulation + +# use a Runge-Kutta method with automatic (error based) time step size control +sol = solve(ode, RDPK3SpFSAL49(); abstol=1.0e-8, reltol=1.0e-8, + ode_default_options()..., callback=callbacks); +summary_callback() # print the timer summary diff --git a/src/solvers/dgsem_structured/dg_2d.jl b/src/solvers/dgsem_structured/dg_2d.jl index 8cdc77395f..bd12ab6d3f 100644 --- a/src/solvers/dgsem_structured/dg_2d.jl +++ b/src/solvers/dgsem_structured/dg_2d.jl @@ -129,7 +129,7 @@ end end @inline function flux_differencing_kernel!(du, u, - element, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, + element, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, nonconservative_terms::True, equations, volume_flux, dg::DGSEM, cache, alpha=true) @unpack derivative_split = dg.basis @@ -251,7 +251,7 @@ end # Calculate the finite volume fluxes inside curvilinear elements (**with non-conservative terms**). @inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, u::AbstractArray{<:Any,4}, - mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, + mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, nonconservative_terms::True, equations, volume_flux_fv, dg::DGSEM, element, cache) @unpack contravariant_vectors = cache.elements diff --git a/test/test_t8code_2d.jl b/test/test_t8code_2d.jl index 23cec35673..348feba463 100644 --- a/test/test_t8code_2d.jl +++ b/test/test_t8code_2d.jl @@ -62,6 +62,56 @@ mkdir(outdir) linf = [0.04155789011775046, 0.024772109862748914, 0.03759938693042297, 0.08039824959535657]) end + @trixi_testset "elixir_euler_free_stream.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_free_stream.jl"), + l2 = [2.063350241405049e-15, 1.8571016296925367e-14, 3.1769447886391905e-14, 1.4104095258528071e-14], + linf = [1.9539925233402755e-14, 2e-12, 4.8e-12, 4e-12], + atol = 2.0e-12, # required to make CI tests pass on macOS + ) + end + + @trixi_testset "elixir_euler_shockcapturing_ec.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_shockcapturing_ec.jl"), + l2 = [9.53984675e-02, 1.05633455e-01, 1.05636158e-01, 3.50747237e-01], + linf = [2.94357464e-01, 4.07893014e-01, 3.97334516e-01, 1.08142520e+00], + tspan = (0.0, 1.0)) + end + + @trixi_testset "elixir_euler_sedov.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_sedov.jl"), + l2 = [3.76149952e-01, 2.46970327e-01, 2.46970327e-01, 1.28889042e+00], + linf = [1.22139001e+00, 1.17742626e+00, 1.17742626e+00, 6.20638482e+00], + tspan = (0.0, 0.3)) + end + + @trixi_testset "elixir_shallowwater_source_terms.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_source_terms.jl"), + l2 = [9.168126407325352e-5, 0.0009795410115453788, 0.002546408320320785, 3.941189812642317e-6], + linf = [0.0009903782521019089, 0.0059752684687262025, 0.010941106525454103, 1.2129488214718265e-5], + tspan = (0.0, 0.1)) + end + + @trixi_testset "elixir_mhd_alfven_wave.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_mhd_alfven_wave.jl"), + l2 = [1.0513414461545583e-5, 1.0517900957166411e-6, 1.0517900957304043e-6, 1.511816606372376e-6, + 1.0443997728645063e-6, 7.879639064990798e-7, 7.879639065049896e-7, 1.0628631669056271e-6, + 4.3382328912336153e-7], + linf = [4.255466285174592e-5, 1.0029706745823264e-5, 1.0029706747467781e-5, 1.2122265939010224e-5, + 5.4791097160444835e-6, 5.18922042269665e-6, 5.189220422141538e-6, 9.552667261422676e-6, + 1.4237578427628152e-6]) + end + + @trixi_testset "elixir_mhd_rotor.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_mhd_rotor.jl"), + l2 = [0.44211360369891683, 0.8805178316216257, 0.8262710688468049, 0.0, + 0.9616090460973586, 0.10386643568745411, 0.15403457366543802, 0.0, + 2.8399715649715473e-5], + linf = [10.04369305341599, 17.995640564998403, 9.576041548174265, 0.0, + 19.429658884314534, 1.3821395681242314, 1.818559351543182, 0.0, + 0.002261930217575465], + tspan = (0.0, 0.02)) + end + end # Clean up afterwards: delete Trixi.jl output directory From 068558f1a3fd0693cec93772f33556f72018bc8d Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 30 May 2023 14:35:28 +0200 Subject: [PATCH 017/128] Updated code according to the review. --- src/auxiliary/t8code.jl | 11 ++++------- src/meshes/t8code_mesh.jl | 3 +-- src/solvers/dgsem_t8code/containers.jl | 13 +++++-------- src/solvers/dgsem_t8code/containers_2d.jl | 4 +--- src/solvers/dgsem_t8code/dg.jl | 2 +- 5 files changed, 12 insertions(+), 21 deletions(-) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index 907c417f0e..4ab7c6af1c 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -1,5 +1,9 @@ """ init_t8code() + +Initialize `t8code` by calling `t8_init` and setting the log level to `SC_LP_ERROR`. +This function will check if `t8code` is already initialized +and if yes, do nothing, thus it is safe to call it multiple times. """ function init_t8code() t8code_package_id = t8_get_package_id() @@ -13,8 +17,6 @@ function init_t8code() return nothing end -@inline t8_mpi_comm() = mpi_comm().val - function trixi_t8_unref_forest(forest) t8_forest_unref(Ref(forest)) end @@ -23,11 +25,6 @@ function t8_free(ptr) sc_free(t8_get_package_id(), ptr) end -function finalize_t8code() - # sc_finalize() - return nothing -end - function trixi_t8_count_interfaces(forest) # Check that forest is a committed, that is valid and usable, forest. @T8_ASSERT (t8_forest_is_committed(forest) != 0) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 06c53dfc2a..e2d5408a93 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -43,7 +43,6 @@ mutable struct T8codeMesh{NDIMS, RealT<:Real, IsParallel} <: AbstractMesh{NDIMS} # Destroy 't8code' structs when the mesh is garbage collected. finalizer(function (mesh::T8codeMesh{NDIMS}) trixi_t8_unref_forest(mesh.forest) - finalize_t8code() end, mesh) return mesh @@ -139,7 +138,7 @@ function T8codeMesh(trees_per_dimension; polydeg, conn = p4est_connectivity_new_brick(trees_per_dimension..., periodicity...) do_partition = 0 - cmesh = t8_cmesh_new_from_p4est(conn,t8_mpi_comm(),do_partition) + cmesh = t8_cmesh_new_from_p4est(conn,mpi_comm(),do_partition) p4est_connectivity_destroy(conn) scheme = t8_scheme_new_default_cxx() diff --git a/src/solvers/dgsem_t8code/containers.jl b/src/solvers/dgsem_t8code/containers.jl index 294e91fbbe..e39e4b8cac 100644 --- a/src/solvers/dgsem_t8code/containers.jl +++ b/src/solvers/dgsem_t8code/containers.jl @@ -77,19 +77,19 @@ function reinitialize_containers!(mesh::T8codeMesh, equations, dg::DGSEM, cache) resize!(elements, ncells(mesh)) init_elements!(elements, mesh, dg.basis) - required = count_required_surfaces(mesh) + count_required_surfaces!(mesh) # Resize interfaces container. @unpack interfaces = cache - resize!(interfaces, required.interfaces) + resize!(interfaces, mesh.ninterfaces) # Resize mortars container. @unpack mortars = cache - resize!(mortars, required.mortars) + resize!(mortars, mesh.nmortars) # Resize boundaries container. @unpack boundaries = cache - resize!(boundaries, required.boundaries) + resize!(boundaries, mesh.nboundaries) # Re-initialize containers together to reduce # the number of iterations over the mesh in `t8code`. @@ -317,13 +317,10 @@ end # ============================================================================ # # ============================================================================ # -function count_required_surfaces(mesh::T8codeMesh) - +function count_required_surfaces!(mesh::T8codeMesh) counts = trixi_t8_count_interfaces(mesh.forest) mesh.nmortars = counts.mortars mesh.ninterfaces = counts.interfaces mesh.nboundaries = counts.boundaries - - return counts end diff --git a/src/solvers/dgsem_t8code/containers_2d.jl b/src/solvers/dgsem_t8code/containers_2d.jl index 944c806030..1ebec5c2fc 100644 --- a/src/solvers/dgsem_t8code/containers_2d.jl +++ b/src/solvers/dgsem_t8code/containers_2d.jl @@ -27,10 +27,9 @@ function calc_node_coordinates!(node_coordinates, element = t8_forest_get_element_in_tree(mesh.forest, itree, ielement) element_level = t8_element_level(eclass_scheme, element) - # TODO: Make this more general. element_length = t8_quad_len(element_level) / t8_quad_root_len - element_coords = Array{Float64}(undef,3) + element_coords = Array{Float64}(undef, 3) t8_element_vertex_reference_coords(eclass_scheme, element, 0, pointer(element_coords)) nodes_out_x = 2 * (element_length * 1/2 * (nodes .+ 1) .+ element_coords[1]) .- 1 @@ -45,7 +44,6 @@ function calc_node_coordinates!(node_coordinates, view(mesh.tree_node_coordinates, :, :, :, itree+1), tmp1 ) - # end TODO end end diff --git a/src/solvers/dgsem_t8code/dg.jl b/src/solvers/dgsem_t8code/dg.jl index 9b5084aaed..394bd39db9 100644 --- a/src/solvers/dgsem_t8code/dg.jl +++ b/src/solvers/dgsem_t8code/dg.jl @@ -8,7 +8,7 @@ function create_cache(mesh::T8codeMesh, equations::AbstractEquations, dg::DG, :: # in case someone has tampered with the 't8code' after creating the mesh. # balance!(mesh) - _ = count_required_surfaces(mesh) + count_required_surfaces!(mesh) elements = init_elements(mesh, equations, dg.basis, uEltype) interfaces = init_interfaces(mesh, equations, dg.basis, elements) From 497f95714879ef2e7760030ffe06a88482f61666 Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Thu, 1 Jun 2023 15:33:01 +0200 Subject: [PATCH 018/128] Update src/auxiliary/t8code.jl Co-authored-by: Hendrik Ranocha --- src/auxiliary/t8code.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index 4ab7c6af1c..1adfd2ddaa 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -67,7 +67,7 @@ function trixi_t8_count_interfaces(forest) forest_is_balanced = Cint(1) - t8_forest_leaf_face_neighbors(forest,itree,element, + t8_forest_leaf_face_neighbors(forest, itree, element, pneighbor_leafs_ref, iface, dual_faces_ref, num_neighbors_ref, pelement_indices_ref, pneigh_scheme_ref, forest_is_balanced) From 4a958c4455351aec8f211707963d58755029163d Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Thu, 1 Jun 2023 15:41:43 +0200 Subject: [PATCH 019/128] Update src/auxiliary/t8code.jl Co-authored-by: Hendrik Ranocha --- src/auxiliary/t8code.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index 1adfd2ddaa..22ace3365d 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -154,7 +154,7 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari t8_cmesh_get_face_neighbor(cmesh, itree_in_cmesh, iface_in_tree, C_NULL, orientation_ref) orientation = orientation_ref[] else - orientation = 0 + orientation = zero(Cint) end pelement_indices_ref = Ref{Ptr{t8_locidx_t}}() From 3d869b0a6ccce3c08313fa413c90ca2668bea73d Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Thu, 1 Jun 2023 15:42:55 +0200 Subject: [PATCH 020/128] Update src/auxiliary/t8code.jl Co-authored-by: Hendrik Ranocha --- src/auxiliary/t8code.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index 22ace3365d..8db6724708 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -234,7 +234,7 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari mortars.neighbor_ids[end, mortar_id] = current_index + 1 # First `1:end-1` entries are the smaller elements. - mortars.neighbor_ids[1:end-1, mortar_id] .= neighbor_ielements[:] .+ 1 + mortars.neighbor_ids[1:end-1, mortar_id] .= neighbor_ielements .+ 1 for side = 1:2 # Align mortar in positive coordinate direction of small side. From c04b0f0dde755a76a27ef35685ae13fe34fad210 Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Thu, 1 Jun 2023 15:43:53 +0200 Subject: [PATCH 021/128] Update src/auxiliary/t8code.jl Co-authored-by: Hendrik Ranocha --- src/auxiliary/t8code.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index 8db6724708..d1a8c9d87c 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -339,13 +339,13 @@ function trixi_t8_get_local_element_levels(forest) end function adapt_callback(forest, - forest_from, - which_tree, - lelement_id, - ts, - is_family, - num_elements, - elements) :: Cint + forest_from, + which_tree, + lelement_id, + ts, + is_family, + num_elements, + elements) :: Cint num_levels = t8_forest_get_local_num_elements(forest_from) From 5396b87b950c3fa3f2053cf464a5af1001060fbc Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Thu, 1 Jun 2023 15:55:15 +0200 Subject: [PATCH 022/128] Update src/meshes/t8code_mesh.jl Co-authored-by: Hendrik Ranocha --- src/meshes/t8code_mesh.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index e2d5408a93..9357fac7f2 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -190,8 +190,9 @@ end """ T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh}, - mapping=nothing, polydeg=1, RealT=Float64, - initial_refinement_level=0, unsaved_changes=true) + mapping=nothing, polydeg=1, RealT=Float64, + initial_refinement_level=0, unsaved_changes=true) + Main mesh constructor for the `T8codeMesh` that imports an unstructured, conforming mesh from an Abaqus mesh file (`.inp`). Each element of the conforming mesh parsed from the `meshfile` is created as a [`p4est`](https://github.com/cburstedde/p4est) From 095fdbc2183659759765f842cf24faf9e7c0c419 Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Thu, 1 Jun 2023 15:55:36 +0200 Subject: [PATCH 023/128] Update src/meshes/t8code_mesh.jl Co-authored-by: Hendrik Ranocha --- src/meshes/t8code_mesh.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 9357fac7f2..caff875904 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -1,6 +1,8 @@ """ T8codeMesh{NDIMS} <: AbstractMesh{NDIMS} -An unstructured curved mesh based on trees that uses the C library 't8code' + +An unstructured curved mesh based on trees that uses the C library +['t8code'](https://github.com/DLR-AMR/t8code) to manage trees and mesh refinement. """ mutable struct T8codeMesh{NDIMS, RealT<:Real, IsParallel} <: AbstractMesh{NDIMS} From 32226931543f41731197229851005273474bfb55 Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Thu, 1 Jun 2023 15:56:34 +0200 Subject: [PATCH 024/128] Update src/meshes/t8code_mesh.jl Co-authored-by: Hendrik Ranocha --- src/meshes/t8code_mesh.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index caff875904..ebc9009735 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -98,8 +98,9 @@ end """ T8codeMesh(trees_per_dimension; polydeg, - mapping=nothing, faces=nothing, coordinates_min=nothing, coordinates_max=nothing, - RealT=Float64, initial_refinement_level=0, periodicity=true, unsaved_changes=true) + mapping=nothing, faces=nothing, coordinates_min=nothing, coordinates_max=nothing, + RealT=Float64, initial_refinement_level=0, periodicity=true, unsaved_changes=true) + Create a structured curved 'T8codeMesh' of the specified size. There are three ways to map the mesh to the physical domain. 1. Define a 'mapping' that maps the hypercube '[-1, 1]^n'. From db52949f7ff4772b041cc3031019d9abba5bd227 Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Thu, 1 Jun 2023 15:57:55 +0200 Subject: [PATCH 025/128] Update src/meshes/t8code_mesh.jl Co-authored-by: Hendrik Ranocha --- src/meshes/t8code_mesh.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index ebc9009735..a7cd620e1c 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -103,10 +103,12 @@ end Create a structured curved 'T8codeMesh' of the specified size. There are three ways to map the mesh to the physical domain. -1. Define a 'mapping' that maps the hypercube '[-1, 1]^n'. +1. Define a `mapping` that maps the hypercube '[-1, 1]^n'. 2. Specify a 'Tuple' 'faces' of functions that parametrize each face. 3. Create a rectangular mesh by specifying 'coordinates_min' and 'coordinates_max'. + Non-periodic boundaries will be called ':x_neg', ':x_pos', ':y_neg', ':y_pos', ':z_neg', ':z_pos'. + # Arguments - 'trees_per_dimension::NTupleE{NDIMS, Int}': the number of trees in each dimension. - 'polydeg::Integer': polynomial degree used to store the geometry of the mesh. From 62cc2b68e4649c1c86f4513c539f62640ce26a2a Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Thu, 1 Jun 2023 16:50:26 +0200 Subject: [PATCH 026/128] Update src/meshes/t8code_mesh.jl Co-authored-by: Hendrik Ranocha --- src/meshes/t8code_mesh.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index a7cd620e1c..62d81dfebe 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -216,6 +216,7 @@ Increasing the `initial_refinement_level` allows one to uniformly refine the bas in the `meshfile` to create a forest with more trees before the simulation begins. For example, if a two-dimensional base mesh contains 25 elements then setting `initial_refinement_level=1` creates an initial forest of `2^2 * 25 = 100` trees. + # Arguments - `cmesh::Ptr{t8_cmesh}`: Pointer to a cmesh object. - `mapping`: a function of `NDIMS` variables to describe the mapping that transforms From 3ec60f28fa8aa9613c0adcd945794536fb53d685 Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Thu, 1 Jun 2023 16:50:56 +0200 Subject: [PATCH 027/128] Update src/meshes/t8code_mesh.jl Co-authored-by: Hendrik Ranocha --- src/meshes/t8code_mesh.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 62d81dfebe..49f5a187db 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -231,8 +231,8 @@ For example, if a two-dimensional base mesh contains 25 elements then setting - `unsaved_changes::Bool`: if set to `true`, the mesh will be saved to a mesh file. """ function T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh}; - mapping=nothing, polydeg=1, RealT=Float64, - initial_refinement_level=0, unsaved_changes=true) where NDIMS + mapping=nothing, polydeg=1, RealT=Float64, + initial_refinement_level=0, unsaved_changes=true) where NDIMS @assert NDIMS == 2 # Only support for NDIMS = 2 yet. From 13169bfcda571374b6e99efadd98ccae31f2c110 Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Thu, 1 Jun 2023 16:53:03 +0200 Subject: [PATCH 028/128] Update src/solvers/dgsem_t8code/containers_2d.jl Co-authored-by: Hendrik Ranocha --- src/solvers/dgsem_t8code/containers_2d.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/solvers/dgsem_t8code/containers_2d.jl b/src/solvers/dgsem_t8code/containers_2d.jl index 1ebec5c2fc..aeb0d2fac4 100644 --- a/src/solvers/dgsem_t8code/containers_2d.jl +++ b/src/solvers/dgsem_t8code/containers_2d.jl @@ -19,9 +19,9 @@ function calc_node_coordinates!(node_coordinates, current_index = 0 for itree = 0:num_local_trees-1 - tree_class = t8_forest_get_tree_class(mesh.forest, itree); - eclass_scheme = t8_forest_get_eclass_scheme(mesh.forest, tree_class); - num_elements_in_tree = t8_forest_get_tree_num_elements(mesh.forest, itree); + tree_class = t8_forest_get_tree_class(mesh.forest, itree) + eclass_scheme = t8_forest_get_eclass_scheme(mesh.forest, tree_class) + num_elements_in_tree = t8_forest_get_tree_num_elements(mesh.forest, itree) for ielement = 0:num_elements_in_tree-1 element = t8_forest_get_element_in_tree(mesh.forest, itree, ielement) From 1eb3d4cab7f3a84db1efd4fc8ae11cd16a3b2056 Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Thu, 1 Jun 2023 16:53:40 +0200 Subject: [PATCH 029/128] Update src/meshes/t8code_mesh.jl Co-authored-by: Hendrik Ranocha --- src/meshes/t8code_mesh.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 49f5a187db..38cff94954 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -294,8 +294,9 @@ end """ T8codeMesh{NDIMS}(conn::Ptr{P4est.LibP4est.p4est_connectivity}, - mapping=nothing, polydeg=1, RealT=Float64, - initial_refinement_level=0, unsaved_changes=true) + mapping=nothing, polydeg=1, RealT=Float64, + initial_refinement_level=0, unsaved_changes=true) + Main mesh constructor for the `T8codeMesh` that imports an unstructured, conforming mesh from an Abaqus mesh file (`.inp`). Each element of the conforming mesh parsed from the `meshfile` is created as a [`p4est`](https://github.com/cburstedde/p4est) From c3e386928a7f285970f92246712ca3200b584b97 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Mon, 12 Jun 2023 09:47:20 +0200 Subject: [PATCH 030/128] Code cleanup. --- src/auxiliary/t8code.jl | 203 ++++++++++++++++++--------------- src/meshes/t8code_mesh.jl | 33 ++---- src/solvers/dgsem_t8code/dg.jl | 4 - 3 files changed, 121 insertions(+), 119 deletions(-) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index 4ab7c6af1c..74f31a0a84 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -27,7 +27,7 @@ end function trixi_t8_count_interfaces(forest) # Check that forest is a committed, that is valid and usable, forest. - @T8_ASSERT (t8_forest_is_committed(forest) != 0) + @assert t8_forest_is_committed(forest) != 0 # Get the number of local elements of forest. num_local_elements = t8_forest_get_local_num_elements(forest) @@ -54,7 +54,7 @@ function trixi_t8_count_interfaces(forest) level = t8_element_level(eclass_scheme, element) - num_faces = t8_element_num_faces(eclass_scheme,element) + num_faces = t8_element_num_faces(eclass_scheme, element) for iface = 0:num_faces-1 @@ -67,13 +67,13 @@ function trixi_t8_count_interfaces(forest) forest_is_balanced = Cint(1) - t8_forest_leaf_face_neighbors(forest,itree,element, + t8_forest_leaf_face_neighbors(forest, itree, element, pneighbor_leafs_ref, iface, dual_faces_ref, num_neighbors_ref, pelement_indices_ref, pneigh_scheme_ref, forest_is_balanced) num_neighbors = num_neighbors_ref[] - neighbor_ielements = unsafe_wrap(Array,pelement_indices_ref[],num_neighbors) - neighbor_leafs = unsafe_wrap(Array,pneighbor_leafs_ref[],num_neighbors) + neighbor_ielements = unsafe_wrap(Array, pelement_indices_ref[], num_neighbors) + neighbor_leafs = unsafe_wrap(Array, pneighbor_leafs_ref[], num_neighbors) neighbor_scheme = pneigh_scheme_ref[] if num_neighbors > 0 @@ -85,9 +85,9 @@ function trixi_t8_count_interfaces(forest) # elseif level == neighbor_level && # (all(Int32(current_index) .< neighbor_ielements) || # level == 0 && (iface == 0 || iface == 2 || iface == 4)) - local_num_conform += 1 + local_num_conform += 1 elseif level < neighbor_level - local_num_mortars += 1 + local_num_mortars += 1 end else @@ -106,6 +106,10 @@ function trixi_t8_count_interfaces(forest) end # for end # for + println("local_num_conform = ", local_num_conform) + println("local_num_mortars = ", local_num_mortars) + println("local_num_boundary = ", local_num_boundary) + return (interfaces = local_num_conform, mortars = local_num_mortars, boundaries = local_num_boundary) @@ -113,7 +117,7 @@ end function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundaries, boundary_names) # Check that forest is a committed, that is valid and usable, forest. - @T8_ASSERT (t8_forest_is_committed(forest) != 0) + @assert t8_forest_is_committed(forest) != 0 # Get the number of local elements of forest. num_local_elements = t8_forest_get_local_num_elements(forest) @@ -171,9 +175,9 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari pelement_indices_ref, pneigh_scheme_ref, forest_is_balanced) num_neighbors = num_neighbors_ref[] - dual_faces = unsafe_wrap(Array,dual_faces_ref[],num_neighbors) - neighbor_ielements = unsafe_wrap(Array,pelement_indices_ref[],num_neighbors) - neighbor_leafs = unsafe_wrap(Array,pneighbor_leafs_ref[],num_neighbors) + dual_faces = unsafe_wrap(Array, dual_faces_ref[], num_neighbors) + neighbor_ielements = unsafe_wrap(Array, pelement_indices_ref[], num_neighbors) + neighbor_leafs = unsafe_wrap(Array, pneighbor_leafs_ref[], num_neighbors) neighbor_scheme = pneigh_scheme_ref[] if num_neighbors > 0 @@ -185,87 +189,87 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari # elseif level == neighbor_level && # (all(Int32(current_index) .< neighbor_ielements) || # level == 0 && (iface == 0 || iface == 2 || iface == 4)) - local_num_conform += 1 - - faces = (iface, dual_faces[1]) - interface_id = local_num_conform - - # Write data to interfaces container. - interfaces.neighbor_ids[1, interface_id] = current_index + 1 - interfaces.neighbor_ids[2, interface_id] = neighbor_ielements[1] + 1 - - # Iterate over primary and secondary element. - for side = 1:2 - # Align interface in positive coordinate direction of primary element. - # For orientation == 1, the secondary element needs to be indexed backwards - # relative to the interface. - if side == 1 || orientation == 0 - # Forward indexing - indexing = :i_forward - else - # Backward indexing - indexing = :i_backward - end - - if faces[side] == 0 - # Index face in negative x-direction - interfaces.node_indices[side, interface_id] = (:begin, indexing) - elseif faces[side] == 1 - # Index face in positive x-direction - interfaces.node_indices[side, interface_id] = (:end, indexing) - elseif faces[side] == 2 - # Index face in negative y-direction - interfaces.node_indices[side, interface_id] = (indexing, :begin) - else # faces[side] == 3 - # Index face in positive y-direction - interfaces.node_indices[side, interface_id] = (indexing, :end) - end + local_num_conform += 1 + + faces = (iface, dual_faces[1]) + interface_id = local_num_conform + + # Write data to interfaces container. + interfaces.neighbor_ids[1, interface_id] = current_index + 1 + interfaces.neighbor_ids[2, interface_id] = neighbor_ielements[1] + 1 + + # Iterate over primary and secondary element. + for side = 1:2 + # Align interface in positive coordinate direction of primary element. + # For orientation == 1, the secondary element needs to be indexed backwards + # relative to the interface. + if side == 1 || orientation == 0 + # Forward indexing + indexing = :i_forward + else + # Backward indexing + indexing = :i_backward end + if faces[side] == 0 + # Index face in negative x-direction + interfaces.node_indices[side, interface_id] = (:begin, indexing) + elseif faces[side] == 1 + # Index face in positive x-direction + interfaces.node_indices[side, interface_id] = (:end, indexing) + elseif faces[side] == 2 + # Index face in negative y-direction + interfaces.node_indices[side, interface_id] = (indexing, :begin) + else # faces[side] == 3 + # Index face in positive y-direction + interfaces.node_indices[side, interface_id] = (indexing, :end) + end + end + # Non-conforming interface. elseif level < neighbor_level - local_num_mortars += 1 - - faces = (dual_faces[1],iface) - - mortar_id = local_num_mortars - - # Last entry is the large element. - mortars.neighbor_ids[end, mortar_id] = current_index + 1 - - # First `1:end-1` entries are the smaller elements. - mortars.neighbor_ids[1:end-1, mortar_id] .= neighbor_ielements[:] .+ 1 - - for side = 1:2 - # Align mortar in positive coordinate direction of small side. - # For orientation == 1, the large side needs to be indexed backwards - # relative to the mortar. - if side == 1 || orientation == 0 - # Forward indexing for small side or orientation == 0. - indexing = :i_forward - else - # Backward indexing for large side with reversed orientation. - indexing = :i_backward - # TODO: Fully understand what is going on here. Generalize this for 3D. - # Has something to do with Morton ordering. - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 - end - - if faces[side] == 0 - # Index face in negative x-direction - mortars.node_indices[side, mortar_id] = (:begin, indexing) - elseif faces[side] == 1 - # Index face in positive x-direction - mortars.node_indices[side, mortar_id] = (:end, indexing) - elseif faces[side] == 2 - # Index face in negative y-direction - mortars.node_indices[side, mortar_id] = (indexing, :begin) - else # faces[side] == 3 - # Index face in positive y-direction - mortars.node_indices[side, mortar_id] = (indexing, :end) - end + local_num_mortars += 1 + + faces = (dual_faces[1],iface) + + mortar_id = local_num_mortars + + # Last entry is the large element. + mortars.neighbor_ids[end, mortar_id] = current_index + 1 + + # First `1:end-1` entries are the smaller elements. + mortars.neighbor_ids[1:end-1, mortar_id] .= neighbor_ielements[:] .+ 1 + + for side = 1:2 + # Align mortar in positive coordinate direction of small side. + # For orientation == 1, the large side needs to be indexed backwards + # relative to the mortar. + if side == 1 || orientation == 0 + # Forward indexing for small side or orientation == 0. + indexing = :i_forward + else + # Backward indexing for large side with reversed orientation. + indexing = :i_backward + # TODO: Fully understand what is going on here. Generalize this for 3D. + # Has something to do with Morton ordering. + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 + end + + if faces[side] == 0 + # Index face in negative x-direction + mortars.node_indices[side, mortar_id] = (:begin, indexing) + elseif faces[side] == 1 + # Index face in positive x-direction + mortars.node_indices[side, mortar_id] = (:end, indexing) + elseif faces[side] == 2 + # Index face in negative y-direction + mortars.node_indices[side, mortar_id] = (indexing, :begin) + else # faces[side] == 3 + # Index face in positive y-direction + mortars.node_indices[side, mortar_id] = (indexing, :end) end + end # else: "level > neighbor_level" is skipped since we visit the mortar interface only once. end @@ -312,7 +316,7 @@ end function trixi_t8_get_local_element_levels(forest) # Check that forest is a committed, that is valid and usable, forest. - @T8_ASSERT (t8_forest_is_committed(forest) != 0) + @assert t8_forest_is_committed(forest) != 0 levels = Vector{Int}(undef, t8_forest_get_local_num_elements(forest)) @@ -338,6 +342,25 @@ function trixi_t8_get_local_element_levels(forest) return levels end +# Callback function prototype to decide for refining and coarsening. +# If `is_family` equals 1, the first `num_elements` in elements +# form a family and we decide whether this family should be coarsened +# or only the first element should be refined. +# Otherwise `is_family` must equal zero and we consider the first entry +# of the element array for refinement. +# Entries of the element array beyond the first `num_elements` are undefined. +# \param [in] forest the forest to which the new elements belong +# \param [in] forest_from the forest that is adapted. +# \param [in] which_tree the local tree containing `elements` +# \param [in] lelement_id the local element id in `forest_old` in the tree of the current element +# \param [in] ts the eclass scheme of the tree +# \param [in] is_family if 1, the first `num_elements` entries in `elements` form a family. If 0, they do not. +# \param [in] num_elements the number of entries in `elements` that are defined +# \param [in] elements Pointers to a family or, if `is_family` is zero, +# pointer to one element. +# \return greater zero if the first entry in `elements` should be refined, +# smaller zero if the family `elements` shall be coarsened, +# zero else. function adapt_callback(forest, forest_from, which_tree, @@ -365,7 +388,7 @@ end function trixi_t8_adapt_new(old_forest, indicators) # Check that forest is a committed, that is valid and usable, forest. - @T8_ASSERT (t8_forest_is_committed(old_forest) != 0) + @assert t8_forest_is_committed(old_forest) != 0 # Init new forest. new_forest_ref = Ref{t8_forest_t}() @@ -377,7 +400,7 @@ function trixi_t8_adapt_new(old_forest, indicators) t8_forest_set_adapt(new_forest, old_forest, @t8_adapt_callback(adapt_callback), recursive) t8_forest_set_balance(new_forest, set_from, no_repartition) t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - # t8_forest_set_ghost(new_forest, 1, T8_GHOST_FACES) + # t8_forest_set_ghost(new_forest, 1, T8_GHOST_FACES) # MPI support not available yet. t8_forest_commit(new_forest) end diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index e2d5408a93..c41f541695 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -3,19 +3,17 @@ An unstructured curved mesh based on trees that uses the C library 't8code' to manage trees and mesh refinement. """ -mutable struct T8codeMesh{NDIMS, RealT<:Real, IsParallel} <: AbstractMesh{NDIMS} +mutable struct T8codeMesh{NDIMS, RealT<:Real, IsParallel, NDIMSP2, NNODES} <: AbstractMesh{NDIMS} cmesh :: Ptr{t8_cmesh} # cpointer to coarse mesh scheme :: Ptr{t8_eclass_scheme} # cpointer to element scheme forest :: Ptr{t8_forest} # cpointer to forest is_parallel :: IsParallel - # ghost :: Ghost # Either Ptr{t8code_ghost_t} or Ptr{t8code_hex_ghost_t} - # Coordinates at the nodes specified by the tensor product of 'nodes' (NDIMS times). # This specifies the geometry interpolation for each tree. - - tree_node_coordinates # :: Array{RealT, NDIMS+2} # [dimension, i, j, k, tree] + tree_node_coordinates :: Array{RealT, NDIMSP2} # [dimension, i, j, k, tree] - nodes # :: SVector{NNODES, RealT} + # Stores the quadrature nodes. + nodes :: SVector{NNODES, RealT} boundary_names :: Array{Symbol, 2} # [face direction, tree] current_filename :: String @@ -38,7 +36,7 @@ mutable struct T8codeMesh{NDIMS, RealT<:Real, IsParallel} <: AbstractMesh{NDIMS} # end is_parallel = False() - mesh = new{NDIMS, Float64, typeof(is_parallel)}(cmesh, scheme, forest, is_parallel) + mesh = new{NDIMS, Float64, typeof(is_parallel), NDIMS+2, length(nodes)}(cmesh, scheme, forest, is_parallel) # Destroy 't8code' structs when the mesh is garbage collected. finalizer(function (mesh::T8codeMesh{NDIMS}) @@ -192,24 +190,9 @@ end T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh}, mapping=nothing, polydeg=1, RealT=Float64, initial_refinement_level=0, unsaved_changes=true) -Main mesh constructor for the `T8codeMesh` that imports an unstructured, conforming -mesh from an Abaqus mesh file (`.inp`). Each element of the conforming mesh parsed -from the `meshfile` is created as a [`p4est`](https://github.com/cburstedde/p4est) -tree datatype. -Note that the `mapping` and `polydeg` keyword arguments are only used by the `p4est_mesh_from_standard_abaqus` -function. The `p4est_mesh_from_hohqmesh_abaqus` function obtains the mesh `polydeg` directly from the `meshfile` -and constructs the transfinite mapping internally. -The particular strategy is selected according to the header present in the `meshfile` where -the constructor checks whether or not the `meshfile` was created with -[HOHQMesh.jl](https://github.com/trixi-framework/HOHQMesh.jl). -If the Abaqus file header is not present in the `meshfile` then the `P4estMesh` is created -with the function `p4est_mesh_from_standard_abaqus`. -The default keyword argument `initial_refinement_level=0` corresponds to a forest -where the number of trees is the same as the number of elements in the original `meshfile`. -Increasing the `initial_refinement_level` allows one to uniformly refine the base mesh given -in the `meshfile` to create a forest with more trees before the simulation begins. -For example, if a two-dimensional base mesh contains 25 elements then setting -`initial_refinement_level=1` creates an initial forest of `2^2 * 25 = 100` trees. +Main mesh constructor for the `T8codeMesh` that imports an unstructured, +conforming mesh from `t8_cmesh` data structure. + # Arguments - `cmesh::Ptr{t8_cmesh}`: Pointer to a cmesh object. - `mapping`: a function of `NDIMS` variables to describe the mapping that transforms diff --git a/src/solvers/dgsem_t8code/dg.jl b/src/solvers/dgsem_t8code/dg.jl index 394bd39db9..e92f9000c2 100644 --- a/src/solvers/dgsem_t8code/dg.jl +++ b/src/solvers/dgsem_t8code/dg.jl @@ -4,10 +4,6 @@ # It constructs the basic `cache` used throughout the simulation to compute # the RHS etc. function create_cache(mesh::T8codeMesh, equations::AbstractEquations, dg::DG, ::Any, ::Type{uEltype}) where {uEltype<:Real} - # Make sure to balance the 't8code' before creating any containers - # in case someone has tampered with the 't8code' after creating the mesh. - # balance!(mesh) - count_required_surfaces!(mesh) elements = init_elements(mesh, equations, dg.basis, uEltype) From 25265e35bbec0898a1b08cc5dea2d995b3b4f20d Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Mon, 12 Jun 2023 10:43:18 +0200 Subject: [PATCH 031/128] Updated to T8code@0.3.0 --- Project.toml | 2 +- src/auxiliary/t8code.jl | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Project.toml b/Project.toml index 675c15389f..bc9ecfc63b 100644 --- a/Project.toml +++ b/Project.toml @@ -81,7 +81,7 @@ StaticArrays = "1" StrideArrays = "0.1.18" StructArrays = "0.6" SummationByPartsOperators = "0.5.25" -T8code = "0.2.0" +T8code = "0.3.0" TimerOutputs = "0.5" Triangulate = "2.0" TriplotBase = "0.1" diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index d7b7d1a5d2..96e0705ec2 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -11,18 +11,27 @@ function init_t8code() return nothing end + + # Initialize the sc library, has to happen before we initialize t8code. + T8code.Libt8.sc_init(mpi_comm(), 1, 1, C_NULL, T8code.Libt8.SC_LP_ESSENTIAL) # Initialize `t8code` with log level ERROR to prevent a lot of output in AMR simulations - t8_init(SC_LP_ERROR) + t8_init(T8code.Libt8.SC_LP_ERROR) return nothing end +function finalize_t8code() + T8code.Libt8.sc_finalize() +end + +finalizer(finalize_t8code, T8code) + function trixi_t8_unref_forest(forest) t8_forest_unref(Ref(forest)) end function t8_free(ptr) - sc_free(t8_get_package_id(), ptr) + T8code.Libt8.sc_free(t8_get_package_id(), ptr) end function trixi_t8_count_interfaces(forest) From 86b6fed34494481220a3ee7ad08d38452ea1b6a3 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Mon, 12 Jun 2023 11:07:43 +0200 Subject: [PATCH 032/128] Fixing minor issues. --- src/auxiliary/t8code.jl | 9 +-------- src/meshes/t8code_mesh.jl | 5 +++-- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index 96e0705ec2..faaac2ce43 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -11,21 +11,14 @@ function init_t8code() return nothing end - # Initialize the sc library, has to happen before we initialize t8code. - T8code.Libt8.sc_init(mpi_comm(), 1, 1, C_NULL, T8code.Libt8.SC_LP_ESSENTIAL) + T8code.Libt8.sc_init(mpi_comm(), 1, 1, C_NULL, T8code.Libt8.SC_LP_ERROR) # Initialize `t8code` with log level ERROR to prevent a lot of output in AMR simulations t8_init(T8code.Libt8.SC_LP_ERROR) return nothing end -function finalize_t8code() - T8code.Libt8.sc_finalize() -end - -finalizer(finalize_t8code, T8code) - function trixi_t8_unref_forest(forest) t8_forest_unref(Ref(forest)) end diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index f3dca5b7ae..187a508818 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -17,6 +17,7 @@ mutable struct T8codeMesh{NDIMS, RealT<:Real, IsParallel, NDIMSP2, NNODES} <: Ab # Stores the quadrature nodes. nodes :: SVector{NNODES, RealT} + boundary_names :: Array{Symbol, 2} # [face direction, tree] current_filename :: String unsaved_changes :: Bool # Not used yet. @@ -26,7 +27,7 @@ mutable struct T8codeMesh{NDIMS, RealT<:Real, IsParallel, NDIMSP2, NNODES} <: Ab nmortars :: Int nboundaries :: Int - function T8codeMesh{NDIMS}(cmesh, scheme, forest) where NDIMS + function T8codeMesh{NDIMS}(cmesh, scheme, forest, nodes) where NDIMS # TODO: Implement MPI parallelization. # if mpi_isparallel() # if !T8code.uses_mpi() @@ -52,7 +53,7 @@ end function T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, boundary_names, current_filename, unsaved_changes) where NDIMS - mesh = T8codeMesh{NDIMS}(cmesh, scheme, forest) + mesh = T8codeMesh{NDIMS}(cmesh, scheme, forest, nodes) mesh.nodes = nodes mesh.boundary_names = boundary_names From 12dc4fe6da0e46bbd2d1bd5bf6e5d4f7fd8e0d53 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Mon, 12 Jun 2023 12:12:44 +0200 Subject: [PATCH 033/128] Fixed typo. --- src/auxiliary/t8code.jl | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index faaac2ce43..d4598516b8 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -1,9 +1,9 @@ """ init_t8code() -Initialize `t8code` by calling `t8_init` and setting the log level to `SC_LP_ERROR`. -This function will check if `t8code` is already initialized -and if yes, do nothing, thus it is safe to call it multiple times. +Initialize `t8code` by calling `sc_init` and `t8_init` while setting the log +level to `SC_LP_ERROR`. This function will check if `t8code` is already +initialized and if yes, do nothing, thus it is safe to call it multiple times. """ function init_t8code() t8code_package_id = t8_get_package_id() @@ -108,10 +108,6 @@ function trixi_t8_count_interfaces(forest) end # for end # for - println("local_num_conform = ", local_num_conform) - println("local_num_mortars = ", local_num_mortars) - println("local_num_boundary = ", local_num_boundary) - return (interfaces = local_num_conform, mortars = local_num_mortars, boundaries = local_num_boundary) @@ -252,8 +248,8 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari else # Backward indexing for large side with reversed orientation. indexing = :i_backward - # TODO: Fully understand what is going on here. Generalize this for 3D. - # Has something to do with Morton ordering. + # Since the orientation is reversed we have to account for this + # when filling the `neighbor_ids` array. mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 end @@ -402,14 +398,14 @@ function trixi_t8_adapt_new(old_forest, indicators) t8_forest_set_adapt(new_forest, old_forest, @t8_adapt_callback(adapt_callback), recursive) t8_forest_set_balance(new_forest, set_from, no_repartition) t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - # t8_forest_set_ghost(new_forest, 1, T8_GHOST_FACES) # MPI support not available yet. + t8_forest_set_ghost(new_forest, 1, T8_GHOST_FACES) # Note: MPI support not available yet so it is a dummy call. t8_forest_commit(new_forest) end return new_forest end -function trixi_t8_get_difference(old_levels, new_levels) +function trixi_t8_get_difference(old_levels, new_levels, num_children) old_nelems = length(old_levels) new_nelems = length(new_levels) @@ -420,9 +416,6 @@ function trixi_t8_get_difference(old_levels, new_levels) old_index = 1 new_index = 1 - # TODO: Make general for 2D/3D and hybrid grids. - T8_CHILDREN = 4 - while old_index <= old_nelems && new_index <= new_nelems if old_levels[old_index] < new_levels[new_index] @@ -431,16 +424,16 @@ function trixi_t8_get_difference(old_levels, new_levels) changes[old_index] = 1 old_index += 1 - new_index += T8_CHILDREN + new_index += num_children elseif old_levels[old_index] > new_levels[new_index] # Coarsend. - for child_index = old_index:old_index+T8_CHILDREN-1 + for child_index = old_index:old_index+num_children-1 changes[child_index] = -1 end - old_index += T8_CHILDREN + old_index += num_children new_index += 1 else @@ -466,7 +459,7 @@ function trixi_t8_adapt!(mesh, indicators) new_levels = trixi_t8_get_local_element_levels(forest_cached) - differences = trixi_t8_get_difference(old_levels, new_levels) + differences = trixi_t8_get_difference(old_levels, new_levels, 2^ndims(mesh)) mesh.forest = forest_cached From 8e118dcf71f97cf078afef1508e61b60a1e38dcd Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Mon, 12 Jun 2023 12:50:58 +0200 Subject: [PATCH 034/128] Code cleanup. --- src/auxiliary/t8code.jl | 23 ++--- src/meshes/t8code_mesh.jl | 114 +++++++++---------------- src/solvers/dgsem_t8code/containers.jl | 4 - 3 files changed, 47 insertions(+), 94 deletions(-) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index d4598516b8..e402e9cf7a 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -1,9 +1,10 @@ """ init_t8code() -Initialize `t8code` by calling `sc_init` and `t8_init` while setting the log -level to `SC_LP_ERROR`. This function will check if `t8code` is already -initialized and if yes, do nothing, thus it is safe to call it multiple times. +Initialize `t8code` by calling `sc_init`, `p4est_init`, and `t8_init` while +setting the log level to `SC_LP_ERROR`. This function will check if `t8code` +is already initialized and if yes, do nothing, thus it is safe to call it +multiple times. """ function init_t8code() t8code_package_id = t8_get_package_id() @@ -13,7 +14,10 @@ function init_t8code() # Initialize the sc library, has to happen before we initialize t8code. T8code.Libt8.sc_init(mpi_comm(), 1, 1, C_NULL, T8code.Libt8.SC_LP_ERROR) - # Initialize `t8code` with log level ERROR to prevent a lot of output in AMR simulations + + # Initialize `p4est` with log level ERROR to prevent a lot of output in AMR simulations + T8code.Libt8.p4est_init(C_NULL, SC_LP_ERROR) + # Initialize t8code with log level ERROR to prevent a lot of output in AMR simulations. t8_init(T8code.Libt8.SC_LP_ERROR) return nothing @@ -59,7 +63,6 @@ function trixi_t8_count_interfaces(forest) num_faces = t8_element_num_faces(eclass_scheme, element) for iface = 0:num_faces-1 - pelement_indices_ref = Ref{Ptr{t8_locidx_t}}() pneighbor_leafs_ref = Ref{Ptr{Ptr{t8_element}}}() pneigh_scheme_ref = Ref{Ptr{t8_eclass_scheme}}() @@ -83,10 +86,6 @@ function trixi_t8_count_interfaces(forest) # Conforming interface: The second condition ensures we only visit the interface once. if level == neighbor_level && current_index <= neighbor_ielements[1] - # TODO: Find a fix for the case: Single element on root level with periodic boundaries. - # elseif level == neighbor_level && - # (all(Int32(current_index) .< neighbor_ielements) || - # level == 0 && (iface == 0 || iface == 2 || iface == 4)) local_num_conform += 1 elseif level < neighbor_level local_num_mortars += 1 @@ -95,7 +94,6 @@ function trixi_t8_count_interfaces(forest) else local_num_boundary += 1 - end t8_free(dual_faces_ref[]) @@ -183,10 +181,6 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari # Conforming interface: The second condition ensures we only visit the interface once. if level == neighbor_level && current_index <= neighbor_ielements[1] - # TODO: Find a fix for the case: Single element on root level with periodic boundaries. - # elseif level == neighbor_level && - # (all(Int32(current_index) .< neighbor_ielements) || - # level == 0 && (iface == 0 || iface == 2 || iface == 4)) local_num_conform += 1 faces = (iface, dual_faces[1]) @@ -381,7 +375,6 @@ function adapt_callback(forest, end return Cint(indicators[offset + lelement_id + 1]) - end function trixi_t8_adapt_new(old_forest, indicators) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 187a508818..8c5443e8e3 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -17,12 +17,9 @@ mutable struct T8codeMesh{NDIMS, RealT<:Real, IsParallel, NDIMSP2, NNODES} <: Ab # Stores the quadrature nodes. nodes :: SVector{NNODES, RealT} - boundary_names :: Array{Symbol, 2} # [face direction, tree] current_filename :: String - unsaved_changes :: Bool # Not used yet. - ncells :: Int ninterfaces :: Int nmortars :: Int nboundaries :: Int @@ -51,13 +48,12 @@ mutable struct T8codeMesh{NDIMS, RealT<:Real, IsParallel, NDIMSP2, NNODES} <: Ab end function T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, boundary_names, - current_filename, unsaved_changes) where NDIMS + current_filename) where NDIMS mesh = T8codeMesh{NDIMS}(cmesh, scheme, forest, nodes) mesh.nodes = nodes mesh.boundary_names = boundary_names - mesh.unsaved_changes = unsaved_changes mesh.current_filename = current_filename mesh.tree_node_coordinates = tree_node_coordinates @@ -70,9 +66,7 @@ const SerialT8codeMesh{NDIMS} = T8codeMesh{NDIMS, <:Real, <:False} @inline Base.ndims(::T8codeMesh{NDIMS}) where NDIMS = NDIMS @inline Base.real(::T8codeMesh{NDIMS, RealT}) where {NDIMS, RealT} = RealT -# TODO: What should be returned in case of parallel processes? Local vs global. -@inline ntrees(mesh::T8codeMesh) = Int(t8_forest_get_num_global_trees(mesh.forest)) -# @inline ncells(mesh::T8codeMesh) = Int(t8_forest_get_global_num_elements(mesh.forest)) +@inline ntrees(mesh::T8codeMesh) = Int(t8_forest_get_num_local_trees(mesh.forest)) @inline ncells(mesh::T8codeMesh) = Int(t8_forest_get_local_num_elements(mesh.forest)) @inline ninterfaces(mesh::T8codeMesh) = mesh.ninterfaces @inline nmortars(mesh::T8codeMesh) = mesh.nmortars @@ -96,15 +90,10 @@ function Base.show(io::IO, :: MIME"text/plain", mesh::T8codeMesh) end """ - T8codeMesh(trees_per_dimension; polydeg, - mapping=nothing, faces=nothing, coordinates_min=nothing, coordinates_max=nothing, - RealT=Float64, initial_refinement_level=0, periodicity=true, unsaved_changes=true) + T8codeMesh(trees_per_dimension; polydeg, mapping=identity, + RealT=Float64, initial_refinement_level=0, periodicity=true) -Create a structured curved 'T8codeMesh' of the specified size. -There are three ways to map the mesh to the physical domain. -1. Define a `mapping` that maps the hypercube '[-1, 1]^n'. -2. Specify a 'Tuple' 'faces' of functions that parametrize each face. -3. Create a rectangular mesh by specifying 'coordinates_min' and 'coordinates_max'. +Create a structured potentially curved 'T8codeMesh' of the specified size. Non-periodic boundaries will be called ':x_neg', ':x_pos', ':y_neg', ':y_pos', ':z_neg', ':z_pos'. @@ -119,10 +108,9 @@ Non-periodic boundaries will be called ':x_neg', ':x_pos', ':y_neg', ':y_pos', ' - 'initial_refinement_level::Integer': refine the mesh uniformly to this level before the simulation starts. - 'periodicity': either a 'Bool' deciding if all of the boundaries are periodic or an 'NTuple{NDIMS, Bool}' deciding for each dimension if the boundaries in this dimension are periodic. -- 'unsaved_changes::Bool': if set to 'true', the mesh will be saved to a mesh file. """ function T8codeMesh(trees_per_dimension; polydeg, - mapping, RealT=Float64, initial_refinement_level=0, periodicity=true, unsaved_changes=true) + mapping=coordinates2mapping((-1.0,-1.0), (1.0,1.0)), RealT=Float64, initial_refinement_level=0, periodicity=true) NDIMS = length(trees_per_dimension) @@ -140,13 +128,13 @@ function T8codeMesh(trees_per_dimension; polydeg, periodicity = Tuple(periodicity) end - conn = p4est_connectivity_new_brick(trees_per_dimension..., periodicity...) + conn = T8code.Libt8.p4est_connectivity_new_brick(trees_per_dimension..., periodicity...) do_partition = 0 cmesh = t8_cmesh_new_from_p4est(conn,mpi_comm(),do_partition) - p4est_connectivity_destroy(conn) + T8code.Libt8.p4est_connectivity_destroy(conn) scheme = t8_scheme_new_default_cxx() - forest = t8_forest_new_uniform(cmesh,scheme,initial_refinement_level,0,mpi_comm().val) + forest = t8_forest_new_uniform(cmesh,scheme,initial_refinement_level,0,mpi_comm()) basis = LobattoLegendreBasis(RealT, polydeg) nodes = basis.nodes @@ -188,16 +176,17 @@ function T8codeMesh(trees_per_dimension; polydeg, end end - return T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, boundary_names, "", unsaved_changes) - + return T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, boundary_names, "") end """ T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh}, mapping=nothing, polydeg=1, RealT=Float64, - initial_refinement_level=0, unsaved_changes=true) + initial_refinement_level=0) + Main mesh constructor for the `T8codeMesh` that imports an unstructured, -conforming mesh from `t8_cmesh` data structure. +conforming mesh from a `t8_cmesh` data structure. + # Arguments - `cmesh::Ptr{t8_cmesh}`: Pointer to a cmesh object. - `mapping`: a function of `NDIMS` variables to describe the mapping that transforms @@ -209,11 +198,10 @@ conforming mesh from `t8_cmesh` data structure. will curve the imported uncurved mesh. - `RealT::Type`: the type that should be used for coordinates. - `initial_refinement_level::Integer`: refine the mesh uniformly to this level before the simulation starts. -- `unsaved_changes::Bool`: if set to `true`, the mesh will be saved to a mesh file. """ function T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh}; mapping=nothing, polydeg=1, RealT=Float64, - initial_refinement_level=0, unsaved_changes=true) where NDIMS + initial_refinement_level=0) where NDIMS @assert NDIMS == 2 # Only support for NDIMS = 2 yet. @@ -270,37 +258,30 @@ function T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh}; # There's no simple and generic way to distinguish boundaries. Name all of them :all. boundary_names = fill(:all, 2 * NDIMS, num_local_trees) - return T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, boundary_names, "", unsaved_changes) + return T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, boundary_names, "") end """ - T8codeMesh{NDIMS}(conn::Ptr{P4est.LibP4est.p4est_connectivity}, + T8codeMesh{NDIMS}(conn::Ptr{p4est_connectivity}, mapping=nothing, polydeg=1, RealT=Float64, - initial_refinement_level=0, unsaved_changes=true) + initial_refinement_level=0) + +Main mesh constructor for the `T8codeMesh` that imports an unstructured, +conforming mesh from a `p4est_connectivity` data structure. -Main mesh constructor for the `T8codeMesh` that imports an unstructured, conforming -mesh from an Abaqus mesh file (`.inp`). Each element of the conforming mesh parsed -from the `meshfile` is created as a [`p4est`](https://github.com/cburstedde/p4est) -tree datatype. -Note that the `mapping` and `polydeg` keyword arguments are only used by the `p4est_mesh_from_standard_abaqus` -function. The `p4est_mesh_from_hohqmesh_abaqus` function obtains the mesh `polydeg` directly from the `meshfile` -and constructs the transfinite mapping internally. -The particular strategy is selected according to the header present in the `meshfile` where -the constructor checks whether or not the `meshfile` was created with -[HOHQMesh.jl](https://github.com/trixi-framework/HOHQMesh.jl). -If the Abaqus file header is not present in the `meshfile` then the `P4estMesh` is created -with the function `p4est_mesh_from_standard_abaqus`. -The default keyword argument `initial_refinement_level=0` corresponds to a forest -where the number of trees is the same as the number of elements in the original `meshfile`. -Increasing the `initial_refinement_level` allows one to uniformly refine the base mesh given -in the `meshfile` to create a forest with more trees before the simulation begins. -For example, if a two-dimensional base mesh contains 25 elements then setting -`initial_refinement_level=1` creates an initial forest of `2^2 * 25 = 100` trees. # Arguments - `conn::Ptr{P4est.LibP4est.p4est_connectivity}`: Pointer to a cmesh object. -- `kwargs`: keyword arguments +- `mapping`: a function of `NDIMS` variables to describe the mapping that transforms + the imported mesh to the physical domain. Use `nothing` for the identity map. +- `polydeg::Integer`: polynomial degree used to store the geometry of the mesh. + The mapping will be approximated by an interpolation polynomial + of the specified degree for each tree. + The default of `1` creates an uncurved geometry. Use a higher value if the mapping + will curve the imported uncurved mesh. +- `RealT::Type`: the type that should be used for coordinates. +- `initial_refinement_level::Integer`: refine the mesh uniformly to this level before the simulation starts. """ -function T8codeMesh{NDIMS}(conn::Ptr{P4est.LibP4est.p4est_connectivity}; kwargs...) where NDIMS +function T8codeMesh{NDIMS}(conn::Ptr{T8code.Libt8.p4est_connectivity}; kwargs...) where NDIMS @assert NDIMS == 2 # Only support for NDIMS = 2 yet. @@ -309,31 +290,16 @@ function T8codeMesh{NDIMS}(conn::Ptr{P4est.LibP4est.p4est_connectivity}; kwargs. return T8codeMesh{NDIMS}(cmesh; kwargs...) end - """ T8codeMesh{NDIMS}(meshfile::String; mapping=nothing, polydeg=1, RealT=Float64, - initial_refinement_level=0, unsaved_changes=true) + initial_refinement_level=0) + Main mesh constructor for the `T8codeMesh` that imports an unstructured, conforming -mesh from an Abaqus mesh file (`.inp`). Each element of the conforming mesh parsed -from the `meshfile` is created as a [`p4est`](https://github.com/cburstedde/p4est) -tree datatype. -Note that the `mapping` and `polydeg` keyword arguments are only used by the `p4est_mesh_from_standard_abaqus` -function. The `p4est_mesh_from_hohqmesh_abaqus` function obtains the mesh `polydeg` directly from the `meshfile` -and constructs the transfinite mapping internally. -The particular strategy is selected according to the header present in the `meshfile` where -the constructor checks whether or not the `meshfile` was created with -[HOHQMesh.jl](https://github.com/trixi-framework/HOHQMesh.jl). -If the Abaqus file header is not present in the `meshfile` then the `P4estMesh` is created -with the function `p4est_mesh_from_standard_abaqus`. -The default keyword argument `initial_refinement_level=0` corresponds to a forest -where the number of trees is the same as the number of elements in the original `meshfile`. -Increasing the `initial_refinement_level` allows one to uniformly refine the base mesh given -in the `meshfile` to create a forest with more trees before the simulation begins. -For example, if a two-dimensional base mesh contains 25 elements then setting -`initial_refinement_level=1` creates an initial forest of `2^2 * 25 = 100` trees. +mesh from a Gmsh mesh file (`.msh`). + # Arguments -- `meshfile::String`: an uncurved Abaqus mesh file that can be imported by `p4est`. +- `meshfile::String`: path to a Gmsh mesh file. - `mapping`: a function of `NDIMS` variables to describe the mapping that transforms the imported mesh to the physical domain. Use `nothing` for the identity map. - `polydeg::Integer`: polynomial degree used to store the geometry of the mesh. @@ -343,7 +309,6 @@ For example, if a two-dimensional base mesh contains 25 elements then setting will curve the imported uncurved mesh. - `RealT::Type`: the type that should be used for coordinates. - `initial_refinement_level::Integer`: refine the mesh uniformly to this level before the simulation starts. -- `unsaved_changes::Bool`: if set to `true`, the mesh will be saved to a mesh file. """ function T8codeMesh{NDIMS}(meshfile::String; kwargs...) where NDIMS @@ -354,18 +319,17 @@ function T8codeMesh{NDIMS}(meshfile::String; kwargs...) where NDIMS meshfile_prefix, meshfile_suffix = splitext(meshfile) - cmesh = t8_cmesh_from_msh_file(meshfile_prefix, 0, mpi_comm(), 2, 0, 0) + cmesh = t8_cmesh_from_msh_file(meshfile_prefix, 0, mpi_comm(), NDIMS, 0, 0) return T8codeMesh{NDIMS}(cmesh; kwargs...) - end -# TODO: Just a placeholder. Will be implemented later. +# TODO: Just a placeholder. Will be implemented later when MPI is supported. function balance!(mesh::T8codeMesh, init_fn=C_NULL) return nothing end -# TODO: Just a placeholder. Will be implemented later. +# TODO: Just a placeholder. Will be implemented later when MPI is supported. function partition!(mesh::T8codeMesh; allow_coarsening=true, weight_fn=C_NULL) return nothing end diff --git a/src/solvers/dgsem_t8code/containers.jl b/src/solvers/dgsem_t8code/containers.jl index e39e4b8cac..b4fcc5f5dc 100644 --- a/src/solvers/dgsem_t8code/containers.jl +++ b/src/solvers/dgsem_t8code/containers.jl @@ -91,10 +91,6 @@ function reinitialize_containers!(mesh::T8codeMesh, equations, dg::DGSEM, cache) @unpack boundaries = cache resize!(boundaries, mesh.nboundaries) - # Re-initialize containers together to reduce - # the number of iterations over the mesh in `t8code`. - # init_surfaces!(elements,interfaces, mortars, boundaries, mesh) - trixi_t8_fill_mesh_info(mesh.forest, elements, interfaces, mortars, boundaries, mesh.boundary_names) return nothing From eb3af2697ea91ac4d8354c97f61a9c71a6f58298 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 13 Jun 2023 10:26:36 +0200 Subject: [PATCH 035/128] Enabled `set_ghost` in examples. --- .../t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl | 4 +--- examples/t8code_2d_dgsem/elixir_euler_free_stream.jl | 2 +- ...ixir_euler_source_terms_nonconforming_unstructured_flag.jl | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl b/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl index 6f341facc1..78c0e32c2d 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl @@ -1,8 +1,6 @@ - using OrdinaryDiffEq using Trixi - ############################################################################### # semidiscretization of the linear advection equation @@ -68,7 +66,7 @@ let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0 Trixi.t8_forest_set_adapt(new_forest, mesh.forest, @Trixi.t8_adapt_callback(adapt_callback), recursive) Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - # t8_forest_set_ghost(new_forest, 1, T8_GHOST_FACES); + Trixi.t8_forest_set_ghost(new_forest, 1, T8_GHOST_FACES); Trixi.t8_forest_commit(new_forest) end diff --git a/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl b/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl index 2beb73f818..8ba4255fed 100644 --- a/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl +++ b/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl @@ -82,7 +82,7 @@ let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0 Trixi.t8_forest_set_adapt(new_forest, mesh.forest, @Trixi.t8_adapt_callback(adapt_callback), recursive) Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - # t8_forest_set_ghost(new_forest, 1, T8_GHOST_FACES); + Trixi.t8_forest_set_ghost(new_forest, 1, T8_GHOST_FACES); Trixi.t8_forest_commit(new_forest) end diff --git a/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl b/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl index 6dba1c42b2..f62fafda3a 100644 --- a/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl @@ -84,7 +84,7 @@ let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0 Trixi.t8_forest_set_adapt(new_forest, mesh.forest, @Trixi.t8_adapt_callback(adapt_callback), recursive) Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - # t8_forest_set_ghost(new_forest, 1, T8_GHOST_FACES); + Trixi.t8_forest_set_ghost(new_forest, 1, T8_GHOST_FACES); Trixi.t8_forest_commit(new_forest) end From 0dd672575407cbe99b2cba8651e3d6f9a567a497 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 13 Jun 2023 10:31:08 +0200 Subject: [PATCH 036/128] Generalized type info in function signature. --- src/meshes/t8code_mesh.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 8c5443e8e3..5ad0571f17 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -281,7 +281,7 @@ conforming mesh from a `p4est_connectivity` data structure. - `RealT::Type`: the type that should be used for coordinates. - `initial_refinement_level::Integer`: refine the mesh uniformly to this level before the simulation starts. """ -function T8codeMesh{NDIMS}(conn::Ptr{T8code.Libt8.p4est_connectivity}; kwargs...) where NDIMS +function T8codeMesh{NDIMS}(conn::Ptr{p4est_connectivity}; kwargs...) where NDIMS @assert NDIMS == 2 # Only support for NDIMS = 2 yet. From 7218b051d160f7af44e1db06b9fcd984a83a3ee7 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 13 Jun 2023 10:34:01 +0200 Subject: [PATCH 037/128] Added namespace qualifier. --- examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl | 2 +- examples/t8code_2d_dgsem/elixir_euler_free_stream.jl | 2 +- ...elixir_euler_source_terms_nonconforming_unstructured_flag.jl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl b/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl index 78c0e32c2d..ef42eb8d77 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl @@ -66,7 +66,7 @@ let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0 Trixi.t8_forest_set_adapt(new_forest, mesh.forest, @Trixi.t8_adapt_callback(adapt_callback), recursive) Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - Trixi.t8_forest_set_ghost(new_forest, 1, T8_GHOST_FACES); + Trixi.t8_forest_set_ghost(new_forest, 1, Trixi.T8_GHOST_FACES); Trixi.t8_forest_commit(new_forest) end diff --git a/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl b/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl index 8ba4255fed..38addfdcde 100644 --- a/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl +++ b/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl @@ -82,7 +82,7 @@ let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0 Trixi.t8_forest_set_adapt(new_forest, mesh.forest, @Trixi.t8_adapt_callback(adapt_callback), recursive) Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - Trixi.t8_forest_set_ghost(new_forest, 1, T8_GHOST_FACES); + Trixi.t8_forest_set_ghost(new_forest, 1, Trixi.T8_GHOST_FACES); Trixi.t8_forest_commit(new_forest) end diff --git a/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl b/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl index f62fafda3a..8d92fee6d5 100644 --- a/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl @@ -84,7 +84,7 @@ let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0 Trixi.t8_forest_set_adapt(new_forest, mesh.forest, @Trixi.t8_adapt_callback(adapt_callback), recursive) Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - Trixi.t8_forest_set_ghost(new_forest, 1, T8_GHOST_FACES); + Trixi.t8_forest_set_ghost(new_forest, 1, Trixi.T8_GHOST_FACES); Trixi.t8_forest_commit(new_forest) end From aad6321453094c00e753356fc795c33c5d7ebf4d Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 13 Jun 2023 10:42:54 +0200 Subject: [PATCH 038/128] Updated comments. --- src/meshes/t8code_mesh.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 5ad0571f17..5ba8d92876 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -270,7 +270,7 @@ Main mesh constructor for the `T8codeMesh` that imports an unstructured, conforming mesh from a `p4est_connectivity` data structure. # Arguments -- `conn::Ptr{P4est.LibP4est.p4est_connectivity}`: Pointer to a cmesh object. +- `conn::Ptr{p4est_connectivity}`: Pointer to a P4est connectivity object. - `mapping`: a function of `NDIMS` variables to describe the mapping that transforms the imported mesh to the physical domain. Use `nothing` for the identity map. - `polydeg::Integer`: polynomial degree used to store the geometry of the mesh. @@ -299,7 +299,7 @@ Main mesh constructor for the `T8codeMesh` that imports an unstructured, conform mesh from a Gmsh mesh file (`.msh`). # Arguments -- `meshfile::String`: path to a Gmsh mesh file. +- `meshfile::String`: path to a Gmsh mesh file. - `mapping`: a function of `NDIMS` variables to describe the mapping that transforms the imported mesh to the physical domain. Use `nothing` for the identity map. - `polydeg::Integer`: polynomial degree used to store the geometry of the mesh. From 3dea7a1f5592f13ed46c2890ba868c83df1b4cb0 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 13 Jun 2023 11:54:23 +0200 Subject: [PATCH 039/128] Refactored code and deleted lots of it. --- src/solvers/dgsem_p4est/containers.jl | 9 +- src/solvers/dgsem_t8code/containers.jl | 305 ++----------------------- 2 files changed, 24 insertions(+), 290 deletions(-) diff --git a/src/solvers/dgsem_p4est/containers.jl b/src/solvers/dgsem_p4est/containers.jl index ba582b0d47..a4ef08175a 100644 --- a/src/solvers/dgsem_p4est/containers.jl +++ b/src/solvers/dgsem_p4est/containers.jl @@ -67,9 +67,8 @@ function Base.resize!(elements::P4estElementContainer, capacity) return nothing end - # Create element container and initialize element data -function init_elements(mesh::P4estMesh{NDIMS, RealT}, equations, +function init_elements(mesh::Union{P4estMesh{NDIMS, RealT}, T8codeMesh{NDIMS, RealT}}, equations, basis, ::Type{uEltype}) where {NDIMS, RealT<:Real, uEltype<:Real} nelements = ncells(mesh) @@ -142,7 +141,7 @@ end # Create interface container and initialize interface data. -function init_interfaces(mesh::P4estMesh, equations, basis, elements) +function init_interfaces(mesh::Union{P4estMesh, T8codeMesh}, equations, basis, elements) NDIMS = ndims(elements) uEltype = eltype(elements) @@ -211,7 +210,7 @@ end # Create interface container and initialize interface data in `elements`. -function init_boundaries(mesh::P4estMesh, equations, basis, elements) +function init_boundaries(mesh::Union{P4estMesh, T8codeMesh}, equations, basis, elements) NDIMS = ndims(elements) uEltype = eltype(elements) @@ -340,7 +339,7 @@ end # Create mortar container and initialize mortar data. -function init_mortars(mesh::P4estMesh, equations, basis, elements) +function init_mortars(mesh::Union{P4estMesh, T8codeMesh}, equations, basis, elements) NDIMS = ndims(elements) uEltype = eltype(elements) diff --git a/src/solvers/dgsem_t8code/containers.jl b/src/solvers/dgsem_t8code/containers.jl index b4fcc5f5dc..e808a7336d 100644 --- a/src/solvers/dgsem_t8code/containers.jl +++ b/src/solvers/dgsem_t8code/containers.jl @@ -1,77 +1,4 @@ -mutable struct T8codeElementContainer{NDIMS, RealT<:Real, uEltype<:Real, NDIMSP1, NDIMSP2, NDIMSP3} <: AbstractContainer - # Physical coordinates at each node - node_coordinates :: Array{RealT, NDIMSP2} # [orientation, node_i, node_j, node_k, element] - # Jacobian matrix of the transformation - # [jacobian_i, jacobian_j, node_i, node_j, node_k, element] where jacobian_i is the first index of the Jacobian matrix,... - jacobian_matrix :: Array{RealT, NDIMSP3} - # Contravariant vectors, scaled by J, in Kopriva's blue book called Ja^i_n (i index, n dimension) - contravariant_vectors :: Array{RealT, NDIMSP3} # [dimension, index, node_i, node_j, node_k, element] - # 1/J where J is the Jacobian determinant (determinant of Jacobian matrix) - inverse_jacobian :: Array{RealT, NDIMSP1} # [node_i, node_j, node_k, element] - # Buffer for calculated surface flux - surface_flux_values :: Array{uEltype, NDIMSP2} # [variable, i, j, direction, element] - - # internal `resize!`able storage - _node_coordinates :: Vector{RealT} - _jacobian_matrix :: Vector{RealT} - _contravariant_vectors :: Vector{RealT} - _inverse_jacobian :: Vector{RealT} - _surface_flux_values :: Vector{uEltype} -end - -mutable struct T8codeInterfaceContainer{NDIMS, uEltype<:Real, NDIMSP2} <: AbstractContainer - u :: Array{uEltype, NDIMSP2} # [primary/secondary, variable, i, j, interface] - neighbor_ids :: Matrix{Int} # [primary/secondary, interface] - node_indices :: Matrix{NTuple{NDIMS, Symbol}} # [primary/secondary, interface] - - # Internal `resize!`able storage. - _u :: Vector{uEltype} - _neighbor_ids :: Vector{Int} - _node_indices :: Vector{NTuple{NDIMS, Symbol}} -end - -mutable struct T8codeMortarContainer{NDIMS, uEltype<:Real, NDIMSP1, NDIMSP3} <: AbstractContainer - u :: Array{uEltype, NDIMSP3} # [small/large side, variable, position, i, j, mortar] - neighbor_ids :: Matrix{Int} # [position, mortar] - node_indices :: Matrix{NTuple{NDIMS, Symbol}} # [small/large, mortar] - - # internal `resize!`able storage - _u :: Vector{uEltype} - _neighbor_ids :: Vector{Int} - _node_indices :: Vector{NTuple{NDIMS, Symbol}} -end - -mutable struct T8codeBoundaryContainer{NDIMS, uEltype<:Real, NDIMSP1} <: AbstractContainer - u :: Array{uEltype, NDIMSP1} # [variables, i, j, boundary] - neighbor_ids :: Vector{Int} # [boundary] - node_indices :: Vector{NTuple{NDIMS, Symbol}} # [boundary] - name :: Vector{Symbol} # [boundary] - - # Internal `resize!`able storage. - _u :: Vector{uEltype} -end - -# ============================================================================ # -# ============================================================================ # - -@inline nelements(elements::T8codeElementContainer) = size(elements.node_coordinates, ndims(elements) + 2) -@inline Base.ndims(::T8codeElementContainer{NDIMS}) where NDIMS = NDIMS -@inline Base.eltype(::T8codeElementContainer{NDIMS, RealT, uEltype}) where {NDIMS, RealT, uEltype} = uEltype - -@inline ninterfaces(interfaces::T8codeInterfaceContainer) = size(interfaces.neighbor_ids, 2) -@inline Base.ndims(::T8codeInterfaceContainer{NDIMS}) where NDIMS = NDIMS - -@inline nboundaries(boundaries::T8codeBoundaryContainer) = length(boundaries.neighbor_ids) -@inline Base.ndims(::T8codeBoundaryContainer{NDIMS}) where NDIMS = NDIMS - -@inline nmortars(mortars::T8codeMortarContainer) = size(mortars.neighbor_ids, 2) -@inline Base.ndims(::T8codeMortarContainer{NDIMS}) where NDIMS = NDIMS - -# ============================================================================ # -# ============================================================================ # - function reinitialize_containers!(mesh::T8codeMesh, equations, dg::DGSEM, cache) - # Re-initialize elements container. @unpack elements = cache resize!(elements, ncells(mesh)) @@ -96,227 +23,35 @@ function reinitialize_containers!(mesh::T8codeMesh, equations, dg::DGSEM, cache) return nothing end -# ============================================================================ # -# ============================================================================ # - -# Only one-dimensional `Array`s are `resize!`able in Julia. -# Hence, we use `Vector`s as internal storage and `resize!` -# them whenever needed. Then, we reuse the same memory by -# `unsafe_wrap`ping multi-dimensional `Array`s around the -# internal storage. -function Base.resize!(elements::T8codeElementContainer, capacity) - @unpack _node_coordinates, _jacobian_matrix, _contravariant_vectors, - _inverse_jacobian, _surface_flux_values = elements - - n_dims = ndims(elements) - n_nodes = size(elements.node_coordinates, 2) - n_variables = size(elements.surface_flux_values, 1) - - resize!(_node_coordinates, n_dims * n_nodes^n_dims * capacity) - elements.node_coordinates = unsafe_wrap(Array, pointer(_node_coordinates), - (n_dims, ntuple(_ -> n_nodes, n_dims)..., capacity)) - - resize!(_jacobian_matrix, n_dims^2 * n_nodes^n_dims * capacity) - elements.jacobian_matrix = unsafe_wrap(Array, pointer(_jacobian_matrix), - (n_dims, n_dims, ntuple(_ -> n_nodes, n_dims)..., capacity)) - - resize!(_contravariant_vectors, length(_jacobian_matrix)) - elements.contravariant_vectors = unsafe_wrap(Array, pointer(_contravariant_vectors), - size(elements.jacobian_matrix)) - - resize!(_inverse_jacobian, n_nodes^n_dims * capacity) - elements.inverse_jacobian = unsafe_wrap(Array, pointer(_inverse_jacobian), - (ntuple(_ -> n_nodes, n_dims)..., capacity)) - - resize!(_surface_flux_values, - n_variables * n_nodes^(n_dims-1) * (n_dims*2) * capacity) - elements.surface_flux_values = unsafe_wrap(Array, pointer(_surface_flux_values), - (n_variables, ntuple(_ -> n_nodes, n_dims-1)..., n_dims*2, capacity)) +function count_required_surfaces!(mesh::T8codeMesh) + counts = trixi_t8_count_interfaces(mesh.forest) - return nothing + mesh.nmortars = counts.mortars + mesh.ninterfaces = counts.interfaces + mesh.nboundaries = counts.boundaries end -# See explanation of Base.resize! for the element container. -function Base.resize!(interfaces::T8codeInterfaceContainer, capacity) - @unpack _u, _neighbor_ids, _node_indices = interfaces - - n_dims = ndims(interfaces) - n_nodes = size(interfaces.u, 3) - n_variables = size(interfaces.u, 2) - - resize!(_u, 2 * n_variables * n_nodes^(n_dims-1) * capacity) - interfaces.u = unsafe_wrap(Array, pointer(_u), - (2, n_variables, ntuple(_ -> n_nodes, n_dims-1)..., capacity)) - - resize!(_neighbor_ids, 2 * capacity) - interfaces.neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids), (2, capacity)) - - resize!(_node_indices, n_dims * capacity) - interfaces.node_indices = unsafe_wrap(Array, pointer(_node_indices), (n_dims, capacity)) - - return nothing +# Compatibility to `dgsem_p4est/containers.jl`. +function count_required_surfaces(mesh::T8codeMesh) + return (interfaces = mesh.ninterfaces, + mortars = mesh.nmortars, + boundaries = mesh.nboundaries) end -# See explanation of Base.resize! for the element container. -function Base.resize!(mortars::T8codeMortarContainer, capacity) - @unpack _u, _neighbor_ids, _node_indices = mortars - - n_dims = ndims(mortars) - n_nodes = size(mortars.u, 4) - n_variables = size(mortars.u, 2) - - resize!(_u, 2 * n_variables * 2^(n_dims-1) * n_nodes^(n_dims-1) * capacity) - mortars.u = unsafe_wrap(Array, pointer(_u), - (2, n_variables, 2^(n_dims-1), ntuple(_ -> n_nodes, n_dims-1)..., capacity)) - - resize!(_neighbor_ids, (2^(n_dims-1) + 1) * capacity) - mortars.neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids), - (2^(n_dims-1) + 1, capacity)) - - resize!(_node_indices, n_dims * capacity) - mortars.node_indices = unsafe_wrap(Array, pointer(_node_indices), (n_dims, capacity)) - +# Compatibility to `dgsem_p4est/containers.jl`. +function init_interfaces!(interfaces, mesh::T8codeMesh) + # Already computed. Do nothing. return nothing end -# See explanation of Base.resize! for the element container. -function Base.resize!(boundaries::T8codeBoundaryContainer, capacity) - @unpack _u, neighbor_ids, node_indices, name = boundaries - - n_dims = ndims(boundaries) - n_nodes = size(boundaries.u, 2) - n_variables = size(boundaries.u, 1) - - resize!(_u, n_variables * n_nodes^(n_dims-1) * capacity) - boundaries.u = unsafe_wrap(Array, pointer(_u), - (n_variables, ntuple(_ -> n_nodes, n_dims-1)..., capacity)) - - resize!(neighbor_ids, capacity) - - resize!(node_indices, capacity) - - resize!(name, capacity) - +# Compatibility to `dgsem_p4est/containers.jl`. +function init_mortars!(mortars, mesh::T8codeMesh) + # Already computed. Do nothing. return nothing end -# ============================================================================ # -# ============================================================================ # - -# Create element container and initialize element data. -function init_elements(mesh::T8codeMesh{NDIMS,RealT}, equations, - basis, ::Type{uEltype}) where {NDIMS, RealT<:Real, uEltype<:Real} - nelements = ncells(mesh) - - _node_coordinates = Vector{RealT}(undef, NDIMS * nnodes(basis)^NDIMS * nelements) - - node_coordinates = unsafe_wrap(Array, pointer(_node_coordinates), - (NDIMS, ntuple(_ -> nnodes(basis), NDIMS)..., nelements)) - - _jacobian_matrix = Vector{RealT}(undef, NDIMS^2 * nnodes(basis)^NDIMS * nelements) - jacobian_matrix = unsafe_wrap(Array, pointer(_jacobian_matrix), - (NDIMS, NDIMS, ntuple(_ -> nnodes(basis), NDIMS)..., nelements)) - - _contravariant_vectors = similar(_jacobian_matrix) - contravariant_vectors = unsafe_wrap(Array, pointer(_contravariant_vectors), - size(jacobian_matrix)) - - _inverse_jacobian = Vector{RealT}(undef, nnodes(basis)^NDIMS * nelements) - inverse_jacobian = unsafe_wrap(Array, pointer(_inverse_jacobian), - (ntuple(_ -> nnodes(basis), NDIMS)..., nelements)) - - _surface_flux_values = Vector{uEltype}(undef, - nvariables(equations) * nnodes(basis)^(NDIMS-1) * (NDIMS*2) * nelements) - surface_flux_values = unsafe_wrap(Array, pointer(_surface_flux_values), - (nvariables(equations), ntuple(_ -> nnodes(basis), NDIMS-1)..., NDIMS*2, nelements)) - - elements = T8codeElementContainer{NDIMS, RealT, uEltype, NDIMS+1, NDIMS+2, NDIMS+3}( - node_coordinates, jacobian_matrix, contravariant_vectors, - inverse_jacobian, surface_flux_values, - _node_coordinates, _jacobian_matrix, _contravariant_vectors, - _inverse_jacobian, _surface_flux_values) - - # Implementation is found in 'containers_{2,3}d.jl'. - init_elements!(elements, mesh, basis) - return elements -end - -# Create interface container and initialize interface data. -function init_interfaces(mesh::T8codeMesh, equations, basis, elements) - NDIMS = ndims(elements) - uEltype = eltype(elements) - - n_interfaces = ninterfaces(mesh) - - _u = Vector{uEltype}(undef, 2 * nvariables(equations) * nnodes(basis)^(NDIMS-1) * n_interfaces) - u = unsafe_wrap(Array, pointer(_u), - (2, nvariables(equations), ntuple(_ -> nnodes(basis), NDIMS-1)..., n_interfaces)) - - _neighbor_ids = Vector{Int}(undef, 2 * n_interfaces) - neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids), (2, n_interfaces)) - - _node_indices = Vector{NTuple{NDIMS, Symbol}}(undef, NDIMS * n_interfaces) - node_indices = unsafe_wrap(Array, pointer(_node_indices), (NDIMS, n_interfaces)) - - interfaces = T8codeInterfaceContainer{NDIMS, uEltype, NDIMS+2}(u, neighbor_ids, node_indices, - _u, _neighbor_ids, _node_indices) - - return interfaces -end - -# Create mortar container and initialize mortar data. -function init_mortars(mesh::T8codeMesh, equations, basis, elements) - NDIMS = ndims(elements) - uEltype = eltype(elements) - - n_mortars = nmortars(mesh) - - _u = Vector{uEltype}(undef, - 2 * nvariables(equations) * 2^(NDIMS-1) * nnodes(basis)^(NDIMS-1) * n_mortars) - u = unsafe_wrap(Array, pointer(_u), - (2, nvariables(equations), 2^(NDIMS-1), ntuple(_ -> nnodes(basis), NDIMS-1)..., n_mortars)) - - _neighbor_ids = Vector{Int}(undef, (2^(NDIMS-1) + 1) * n_mortars) - neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids), (2^(NDIMS-1) + 1, n_mortars)) - - _node_indices = Vector{NTuple{NDIMS, Symbol}}(undef, NDIMS * n_mortars) - node_indices = unsafe_wrap(Array, pointer(_node_indices), (NDIMS, n_mortars)) - - mortars = T8codeMortarContainer{NDIMS, uEltype, NDIMS+1, NDIMS+3}(u, neighbor_ids, node_indices, - _u, _neighbor_ids, _node_indices) - - return mortars -end - -# Create interface container and initialize interface data in `elements`. -function init_boundaries(mesh::T8codeMesh, equations, basis, elements) - NDIMS = ndims(elements) - uEltype = eltype(elements) - - # Initialize container. - n_boundaries = nboundaries(mesh) - - _u = Vector{uEltype}(undef, nvariables(equations) * nnodes(basis)^(NDIMS-1) * n_boundaries) - u = unsafe_wrap(Array, pointer(_u), - (nvariables(equations), ntuple(_ -> nnodes(basis), NDIMS-1)..., n_boundaries)) - - neighbor_ids = Vector{Int}(undef, n_boundaries) - node_indices = Vector{NTuple{NDIMS, Symbol}}(undef, n_boundaries) - names = Vector{Symbol}(undef, n_boundaries) - - boundaries = T8codeBoundaryContainer{NDIMS, uEltype, NDIMS+1}(u, neighbor_ids, - node_indices, names, _u) - - return boundaries -end - -# ============================================================================ # -# ============================================================================ # - -function count_required_surfaces!(mesh::T8codeMesh) - counts = trixi_t8_count_interfaces(mesh.forest) - - mesh.nmortars = counts.mortars - mesh.ninterfaces = counts.interfaces - mesh.nboundaries = counts.boundaries +# Compatibility to `dgsem_p4est/containers.jl`. +function init_boundaries!(boundaries, mesh::T8codeMesh) + # Already computed. Do nothing. + return nothing end From f6f67ad0d40ad5c6ae49e9d142d7fffda59c6f22 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 13 Jun 2023 12:37:48 +0200 Subject: [PATCH 040/128] Removed a copy operation. --- src/auxiliary/t8code.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index e402e9cf7a..d3a44d72c1 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -230,7 +230,7 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari mortars.neighbor_ids[end, mortar_id] = current_index + 1 # First `1:end-1` entries are the smaller elements. - mortars.neighbor_ids[1:end-1, mortar_id] .= neighbor_ielements[:] .+ 1 + mortars.neighbor_ids[1:end-1, mortar_id] .= neighbor_ielements .+ 1 for side = 1:2 # Align mortar in positive coordinate direction of small side. From e2069b4622e827d8121b8077347ca931b09dc25a Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 13 Jun 2023 16:37:33 +0200 Subject: [PATCH 041/128] Initial commit. --- src/auxiliary/t8code.jl | 80 +-------- src/meshes/t8code_mesh.jl | 187 ++++++++++++++++------ src/solvers/dgsem_p4est/containers_3d.jl | 4 +- src/solvers/dgsem_p4est/dg_3d.jl | 26 +-- src/solvers/dgsem_t8code/containers.jl | 2 +- src/solvers/dgsem_t8code/containers_3d.jl | 59 +++++++ 6 files changed, 222 insertions(+), 136 deletions(-) create mode 100644 src/solvers/dgsem_t8code/containers_3d.jl diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index d3a44d72c1..d992e73f8c 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -190,33 +190,8 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari interfaces.neighbor_ids[1, interface_id] = current_index + 1 interfaces.neighbor_ids[2, interface_id] = neighbor_ielements[1] + 1 - # Iterate over primary and secondary element. - for side = 1:2 - # Align interface in positive coordinate direction of primary element. - # For orientation == 1, the secondary element needs to be indexed backwards - # relative to the interface. - if side == 1 || orientation == 0 - # Forward indexing - indexing = :i_forward - else - # Backward indexing - indexing = :i_backward - end - - if faces[side] == 0 - # Index face in negative x-direction - interfaces.node_indices[side, interface_id] = (:begin, indexing) - elseif faces[side] == 1 - # Index face in positive x-direction - interfaces.node_indices[side, interface_id] = (:end, indexing) - elseif faces[side] == 2 - # Index face in negative y-direction - interfaces.node_indices[side, interface_id] = (indexing, :begin) - else # faces[side] == 3 - # Index face in positive y-direction - interfaces.node_indices[side, interface_id] = (indexing, :end) - end - end + # Save interfaces.node_indices dimension specific in containers_3d.jl. + init_interface_node_indices!(interfaces, faces, orientation, interface_id) # Non-conforming interface. elseif level < neighbor_level @@ -232,37 +207,8 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari # First `1:end-1` entries are the smaller elements. mortars.neighbor_ids[1:end-1, mortar_id] .= neighbor_ielements .+ 1 - for side = 1:2 - # Align mortar in positive coordinate direction of small side. - # For orientation == 1, the large side needs to be indexed backwards - # relative to the mortar. - if side == 1 || orientation == 0 - # Forward indexing for small side or orientation == 0. - indexing = :i_forward - else - # Backward indexing for large side with reversed orientation. - indexing = :i_backward - # Since the orientation is reversed we have to account for this - # when filling the `neighbor_ids` array. - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 - end - - if faces[side] == 0 - # Index face in negative x-direction - mortars.node_indices[side, mortar_id] = (:begin, indexing) - elseif faces[side] == 1 - # Index face in positive x-direction - mortars.node_indices[side, mortar_id] = (:end, indexing) - elseif faces[side] == 2 - # Index face in negative y-direction - mortars.node_indices[side, mortar_id] = (indexing, :begin) - else # faces[side] == 3 - # Index face in positive y-direction - mortars.node_indices[side, mortar_id] = (indexing, :end) - end - end - + init_mortar_node_indices!(mortars, faces, orientation, mortar_id) + # else: "level > neighbor_level" is skipped since we visit the mortar interface only once. end @@ -273,19 +219,7 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari boundaries.neighbor_ids[boundary_id] = current_index + 1 - if iface == 0 - # Index face in negative x-direction. - boundaries.node_indices[boundary_id] = (:begin, :i_forward) - elseif iface == 1 - # Index face in positive x-direction. - boundaries.node_indices[boundary_id] = (:end, :i_forward) - elseif iface == 2 - # Index face in negative y-direction. - boundaries.node_indices[boundary_id] = (:i_forward, :begin) - else # iface == 3 - # Index face in positive y-direction. - boundaries.node_indices[boundary_id] = (:i_forward, :end) - end + init_boundary_node_indices!(boundaries, iface, boundary_id) # One-based indexing. boundaries.name[boundary_id] = boundary_names[iface + 1, itree + 1] @@ -386,12 +320,12 @@ function trixi_t8_adapt_new(old_forest, indicators) t8_forest_init(new_forest_ref) new_forest = new_forest_ref[] - let set_from = C_NULL, recursive = 0, set_for_coarsening = 0, no_repartition = 0 + let set_from = C_NULL, recursive = 0, set_for_coarsening = 0, no_repartition = 0, do_ghost = 1 t8_forest_set_user_data(new_forest, pointer(indicators)) t8_forest_set_adapt(new_forest, old_forest, @t8_adapt_callback(adapt_callback), recursive) t8_forest_set_balance(new_forest, set_from, no_repartition) t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - t8_forest_set_ghost(new_forest, 1, T8_GHOST_FACES) # Note: MPI support not available yet so it is a dummy call. + t8_forest_set_ghost(new_forest, do_ghost, T8_GHOST_FACES) # Note: MPI support not available yet so it is a dummy call. t8_forest_commit(new_forest) end diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 5ba8d92876..1b7945ebac 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -110,12 +110,10 @@ Non-periodic boundaries will be called ':x_neg', ':x_pos', ':y_neg', ':y_pos', ' deciding for each dimension if the boundaries in this dimension are periodic. """ function T8codeMesh(trees_per_dimension; polydeg, - mapping=coordinates2mapping((-1.0,-1.0), (1.0,1.0)), RealT=Float64, initial_refinement_level=0, periodicity=true) + mapping=nothing, RealT=Float64, initial_refinement_level=0, periodicity=true) NDIMS = length(trees_per_dimension) - @assert NDIMS == 2 # Only support for NDIMS = 2 yet. - # Convert periodicity to a Tuple of a Bool for every dimension if all(periodicity) # Also catches case where periodicity = true @@ -128,10 +126,17 @@ function T8codeMesh(trees_per_dimension; polydeg, periodicity = Tuple(periodicity) end - conn = T8code.Libt8.p4est_connectivity_new_brick(trees_per_dimension..., periodicity...) - do_partition = 0 - cmesh = t8_cmesh_new_from_p4est(conn,mpi_comm(),do_partition) - T8code.Libt8.p4est_connectivity_destroy(conn) + if NDIMS == 2 + conn = T8code.Libt8.p4est_connectivity_new_brick(trees_per_dimension..., periodicity...) + do_partition = 0 + cmesh = t8_cmesh_new_from_p4est(conn,mpi_comm(),do_partition) + T8code.Libt8.p4est_connectivity_destroy(conn) + elseif NDIMS == 3 + conn = T8code.Libt8.p8est_connectivity_new_brick(trees_per_dimension..., periodicity...) + do_partition = 0 + cmesh = t8_cmesh_new_from_p8est(conn,mpi_comm(),do_partition) + T8code.Libt8.p8est_connectivity_destroy(conn) + end scheme = t8_scheme_new_default_cxx() forest = t8_forest_new_uniform(cmesh,scheme,initial_refinement_level,0,mpi_comm()) @@ -144,25 +149,42 @@ function T8codeMesh(trees_per_dimension; polydeg, prod(trees_per_dimension)) # Get cell length in reference mesh: Omega_ref = [-1,1]^2. - dx = 2 / trees_per_dimension[1] - dy = 2 / trees_per_dimension[2] + dx = [2 / n for n in trees_per_dimension] num_local_trees = t8_cmesh_get_num_local_trees(cmesh) # Non-periodic boundaries. boundary_names = fill(Symbol("---"), 2 * NDIMS, prod(trees_per_dimension)) + if mapping == nothing + mapping_ = coordinates2mapping(ntuple(_->-1.0,NDIMS), ntuple(_->1.0,NDIMS)) + else + mapping_ = mapping + end + for itree = 1:num_local_trees veptr = t8_cmesh_get_tree_vertices(cmesh, itree-1) verts = unsafe_wrap(Array,veptr,(3,1 << NDIMS)) - # Calculate node coordinates of reference mesh. - cell_x_offset = (verts[1,1] - 1/2*(trees_per_dimension[1]-1)) * dx - cell_y_offset = (verts[2,1] - 1/2*(trees_per_dimension[2]-1)) * dy - - for j in eachindex(nodes), i in eachindex(nodes) - tree_node_coordinates[:, i, j, itree] .= mapping(cell_x_offset + dx * nodes[i]/2, - cell_y_offset + dy * nodes[j]/2) + if NDIMS == 2 + # Calculate node coordinates of reference mesh. + cell_x_offset = (verts[1,1] - 0.5*(trees_per_dimension[1]-1)) * dx[1] + cell_y_offset = (verts[2,1] - 0.5*(trees_per_dimension[2]-1)) * dx[2] + + for j in eachindex(nodes), i in eachindex(nodes) + tree_node_coordinates[:, i, j, itree] .= mapping_(cell_x_offset + dx[1] * nodes[i]/2, + cell_y_offset + dx[2] * nodes[j]/2) + end + else + cell_x_offset = (verts[1,1] - 0.5*(trees_per_dimension[1]-1)) * dx[1] + cell_y_offset = (verts[2,1] - 0.5*(trees_per_dimension[2]-1)) * dx[2] + cell_z_offset = (verts[3,1] - 0.5*(trees_per_dimension[3]-1)) * dx[3] + + for k in eachindex(nodes), j in eachindex(nodes), i in eachindex(nodes) + tree_node_coordinates[:, i, j, k, itree] .= mapping_(cell_x_offset + dx[1] * nodes[i]/2, + cell_y_offset + dx[2] * nodes[j]/2, + cell_z_offset + dx[3] * nodes[k]/2) + end end if !periodicity[1] @@ -174,6 +196,13 @@ function T8codeMesh(trees_per_dimension; polydeg, boundary_names[3, itree] = :y_neg boundary_names[4, itree] = :y_pos end + + if NDIMS > 2 + if !periodicity[3] + boundary_names[5, itree] = :z_neg + boundary_names[6, itree] = :z_pos + end + end end return T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, boundary_names, "") @@ -203,8 +232,6 @@ function T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh}; mapping=nothing, polydeg=1, RealT=Float64, initial_refinement_level=0) where NDIMS - @assert NDIMS == 2 # Only support for NDIMS = 2 yet. - scheme = t8_scheme_new_default_cxx() forest = t8_forest_new_uniform(cmesh,scheme,initial_refinement_level,0,mpi_comm()) @@ -222,35 +249,74 @@ function T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh}; data_in = Array{RealT, 3}(undef, 2, 2, 2) tmp1 = zeros(RealT, 2, length(nodes), length(nodes_in)) - for itree in 0:num_local_trees-1 - - veptr = t8_cmesh_get_tree_vertices(cmesh, itree) - verts = unsafe_wrap(Array,veptr,(3,1 << NDIMS)) - - u = verts[:,2] - verts[:,1] - v = verts[:,3] - verts[:,1] - w = [0.0,0.0,1.0] - - vol = dot(cross(u,v),w) - - if vol < 0.0 - @warn "Discovered negative volumes in `cmesh`: vol = $vol" + if NDIMS == 2 + for itree in 0:num_local_trees-1 + veptr = t8_cmesh_get_tree_vertices(cmesh, itree) + verts = unsafe_wrap(Array,veptr,(3,1 << NDIMS)) + + u = verts[:,2] - verts[:,1] + v = verts[:,3] - verts[:,1] + w = [0.0,0.0,1.0] + + vol = dot(cross(u,v),w) + + if vol < 0.0 + @warn "Discovered negative volumes in `cmesh`: vol = $vol" + end + + # Tree vertices are stored in z-order. + @views data_in[:, 1, 1] .= verts[1:2,1] + @views data_in[:, 2, 1] .= verts[1:2,2] + @views data_in[:, 1, 2] .= verts[1:2,3] + @views data_in[:, 2, 2] .= verts[1:2,4] + + # Interpolate corner coordinates to specified nodes. + multiply_dimensionwise!( + view(tree_node_coordinates, :, :, :, itree+1), + matrix, matrix, + data_in, + tmp1 + ) end + + else - # Tree vertices are stored in z-order. - @views data_in[:, 1, 1] .= verts[1:2,1] - @views data_in[:, 2, 1] .= verts[1:2,2] - @views data_in[:, 1, 2] .= verts[1:2,3] - @views data_in[:, 2, 2] .= verts[1:2,4] - - # Interpolate corner coordinates to specified nodes. - multiply_dimensionwise!( - view(tree_node_coordinates, :, :, :, itree+1), - matrix, matrix, - data_in, - tmp1 - ) + for itree in 0:num_local_trees-1 + veptr = t8_cmesh_get_tree_vertices(cmesh, itree) + verts = unsafe_wrap(Array,veptr,(3,1 << NDIMS)) + + # TODO: Check for negative volumes. Probably not necessary since this is + # done within t8code. + # u = verts[:,2] - verts[:,1] + # v = verts[:,3] - verts[:,1] + # w = [0.0,0.0,1.0] + + # vol = dot(cross(u,v),w) + + # if vol < 0.0 + # @warn "Discovered negative volumes in `cmesh`: vol = $vol" + # end + + # Tree vertices are stored in z-order. + @views data_in[:, 1, 1, 1] .= verts[1:3,1] + @views data_in[:, 2, 1, 1] .= verts[1:3,2] + @views data_in[:, 1, 2, 1] .= verts[1:3,3] + @views data_in[:, 2, 2, 1] .= verts[1:3,4] + + @views data_in[:, 1, 1, 2] .= verts[1:3,5] + @views data_in[:, 2, 1, 2] .= verts[1:3,6] + @views data_in[:, 1, 2, 2] .= verts[1:3,7] + @views data_in[:, 2, 2, 2] .= verts[1:3,8] + + # Interpolate corner coordinates to specified nodes. + multiply_dimensionwise!( + view(tree_node_coordinates, :, :, :, :, itree+1), + matrix, matrix, + data_in, + tmp1 + ) + end end map_node_coordinates!(tree_node_coordinates, mapping) @@ -283,13 +349,42 @@ conforming mesh from a `p4est_connectivity` data structure. """ function T8codeMesh{NDIMS}(conn::Ptr{p4est_connectivity}; kwargs...) where NDIMS - @assert NDIMS == 2 # Only support for NDIMS = 2 yet. + @assert NDIMS == 2 # Only support for NDIMS = 2. cmesh = t8_cmesh_new_from_p4est(conn, mpi_comm(), 0) return T8codeMesh{NDIMS}(cmesh; kwargs...) end +""" + T8codeMesh{NDIMS}(conn::Ptr{p8est_connectivity}, + mapping=nothing, polydeg=1, RealT=Float64, + initial_refinement_level=0) + +Main mesh constructor for the `T8codeMesh` that imports an unstructured, +conforming mesh from a `p4est_connectivity` data structure. + +# Arguments +- `conn::Ptr{p4est_connectivity}`: Pointer to a P4est connectivity object. +- `mapping`: a function of `NDIMS` variables to describe the mapping that transforms + the imported mesh to the physical domain. Use `nothing` for the identity map. +- `polydeg::Integer`: polynomial degree used to store the geometry of the mesh. + The mapping will be approximated by an interpolation polynomial + of the specified degree for each tree. + The default of `1` creates an uncurved geometry. Use a higher value if the mapping + will curve the imported uncurved mesh. +- `RealT::Type`: the type that should be used for coordinates. +- `initial_refinement_level::Integer`: refine the mesh uniformly to this level before the simulation starts. +""" +function T8codeMesh{NDIMS}(conn::Ptr{p8est_connectivity}; kwargs...) where NDIMS + + @assert NDIMS == 3 # Only support for NDIMS = 3. + + cmesh = t8_cmesh_new_from_p8est(conn, mpi_comm(), 0) + + return T8codeMesh{NDIMS}(cmesh; kwargs...) +end + """ T8codeMesh{NDIMS}(meshfile::String; mapping=nothing, polydeg=1, RealT=Float64, @@ -312,8 +407,6 @@ mesh from a Gmsh mesh file (`.msh`). """ function T8codeMesh{NDIMS}(meshfile::String; kwargs...) where NDIMS - @assert NDIMS == 2 # Only support for NDIMS = 2 yet. - # Prevent `t8code` from crashing Julia if the file doesn't exist. @assert isfile(meshfile) diff --git a/src/solvers/dgsem_p4est/containers_3d.jl b/src/solvers/dgsem_p4est/containers_3d.jl index f5bffece22..f1243647dd 100644 --- a/src/solvers/dgsem_p4est/containers_3d.jl +++ b/src/solvers/dgsem_p4est/containers_3d.jl @@ -6,7 +6,7 @@ # Initialize data structures in element container -function init_elements!(elements, mesh::P4estMesh{3}, basis::LobattoLegendreBasis) +function init_elements!(elements, mesh::Union{P4estMesh{3}, T8codeMesh{3}}, basis::LobattoLegendreBasis) @unpack node_coordinates, jacobian_matrix, contravariant_vectors, inverse_jacobian = elements @@ -27,7 +27,7 @@ end # Interpolate tree_node_coordinates to each quadrant at the nodes of the specified basis function calc_node_coordinates!(node_coordinates, - mesh::P4estMesh{3}, + mesh::Union{P4estMesh{3}, T8codeMesh{3}}, basis::LobattoLegendreBasis) # Hanging nodes will cause holes in the mesh if its polydeg is higher # than the polydeg of the solver. diff --git a/src/solvers/dgsem_p4est/dg_3d.jl b/src/solvers/dgsem_p4est/dg_3d.jl index d5e32ca64e..e63ccc115e 100644 --- a/src/solvers/dgsem_p4est/dg_3d.jl +++ b/src/solvers/dgsem_p4est/dg_3d.jl @@ -7,7 +7,7 @@ # The methods below are specialized on the mortar type # and called from the basic `create_cache` method at the top. -function create_cache(mesh::P4estMesh{3}, equations, mortar_l2::LobattoLegendreMortarL2, uEltype) +function create_cache(mesh::Union{P4estMesh{3}, T8codeMesh{3}}, equations, mortar_l2::LobattoLegendreMortarL2, uEltype) # TODO: Taal compare performance of different types fstar_threaded = [Array{uEltype, 4}(undef, nvariables(equations), nnodes(mortar_l2), nnodes(mortar_l2), 4) for _ in 1:Threads.nthreads()] @@ -85,7 +85,7 @@ end # We pass the `surface_integral` argument solely for dispatch function prolong2interfaces!(cache, u, - mesh::P4estMesh{3}, + mesh::Union{P4estMesh{3}, T8codeMesh{3}}, equations, surface_integral, dg::DG) @unpack interfaces = cache index_range = eachnode(dg) @@ -152,7 +152,7 @@ end function calc_interface_flux!(surface_flux_values, - mesh::P4estMesh{3}, + mesh::Union{P4estMesh{3}, T8codeMesh{3}}, nonconservative_terms, equations, surface_integral, dg::DG, cache) @unpack neighbor_ids, node_indices = cache.interfaces @@ -226,7 +226,7 @@ end # Inlined function for interface flux computation for conservative flux terms @inline function calc_interface_flux!(surface_flux_values, - mesh::P4estMesh{3}, + mesh::Union{P4estMesh{3}, T8codeMesh{3}}, nonconservative_terms::False, equations, surface_integral, dg::DG, cache, interface_index, normal_direction, @@ -251,7 +251,7 @@ end # Inlined function for interface flux computation for flux + nonconservative terms @inline function calc_interface_flux!(surface_flux_values, - mesh::P4estMesh{3}, + mesh::Union{P4estMesh{3}, T8codeMesh{3}}, nonconservative_terms::True, equations, surface_integral, dg::DG, cache, interface_index, normal_direction, @@ -288,7 +288,7 @@ end function prolong2boundaries!(cache, u, - mesh::P4estMesh{3}, + mesh::Union{P4estMesh{3}, T8codeMesh{3}}, equations, surface_integral, dg::DG) @unpack boundaries = cache index_range = eachnode(dg) @@ -326,7 +326,7 @@ end function calc_boundary_flux!(cache, t, boundary_condition, boundary_indexing, - mesh::P4estMesh{3}, + mesh::Union{P4estMesh{3}, T8codeMesh{3}}, equations, surface_integral, dg::DG) @unpack boundaries = cache @unpack surface_flux_values, node_coordinates, contravariant_vectors = cache.elements @@ -384,7 +384,7 @@ end function prolong2mortars!(cache, u, - mesh::P4estMesh{3}, equations, + mesh::Union{P4estMesh{3}, T8codeMesh{3}}, equations, mortar_l2::LobattoLegendreMortarL2, surface_integral, dg::DGSEM) @unpack fstar_tmp_threaded = cache @@ -481,7 +481,7 @@ end function calc_mortar_flux!(surface_flux_values, - mesh::P4estMesh{3}, + mesh::Union{P4estMesh{3}, T8codeMesh{3}}, nonconservative_terms, equations, mortar_l2::LobattoLegendreMortarL2, surface_integral, dg::DG, cache) @@ -545,7 +545,7 @@ end # Inlined version of the mortar flux computation on small elements for conservation fluxes @inline function calc_mortar_flux!(fstar, - mesh::P4estMesh{3}, + mesh::Union{P4estMesh{3}, T8codeMesh{3}}, nonconservative_terms::False, equations, surface_integral, dg::DG, cache, mortar_index, position_index, normal_direction, @@ -564,7 +564,7 @@ end # Inlined version of the mortar flux computation on small elements for conservation fluxes # with nonconservative terms @inline function calc_mortar_flux!(fstar, - mesh::P4estMesh{3}, + mesh::Union{P4estMesh{3}, T8codeMesh{3}}, nonconservative_terms::True, equations, surface_integral, dg::DG, cache, mortar_index, position_index, normal_direction, @@ -589,7 +589,7 @@ end @inline function mortar_fluxes_to_elements!(surface_flux_values, - mesh::P4estMesh{3}, equations, + mesh::Union{P4estMesh{3}, T8codeMesh{3}}, equations, mortar_l2::LobattoLegendreMortarL2, dg::DGSEM, cache, mortar, fstar, u_buffer, fstar_tmp) @unpack neighbor_ids, node_indices = cache.mortars @@ -672,7 +672,7 @@ end function calc_surface_integral!(du, u, - mesh::P4estMesh{3}, + mesh::Union{P4estMesh{3}, T8codeMesh{3}}, equations, surface_integral::SurfaceIntegralWeakForm, dg::DGSEM, cache) diff --git a/src/solvers/dgsem_t8code/containers.jl b/src/solvers/dgsem_t8code/containers.jl index e808a7336d..a4506440d6 100644 --- a/src/solvers/dgsem_t8code/containers.jl +++ b/src/solvers/dgsem_t8code/containers.jl @@ -18,7 +18,7 @@ function reinitialize_containers!(mesh::T8codeMesh, equations, dg::DGSEM, cache) @unpack boundaries = cache resize!(boundaries, mesh.nboundaries) - trixi_t8_fill_mesh_info(mesh.forest, elements, interfaces, mortars, boundaries, mesh.boundary_names) + trixi_t8_fill_mesh_info(mesh.forest, elements, interfaces, mortars, boundaries, mesh.boundary_names, mesh) return nothing end diff --git a/src/solvers/dgsem_t8code/containers_3d.jl b/src/solvers/dgsem_t8code/containers_3d.jl new file mode 100644 index 0000000000..e6d87d5c03 --- /dev/null +++ b/src/solvers/dgsem_t8code/containers_3d.jl @@ -0,0 +1,59 @@ +# By default, Julia/LLVM does not use fused multiply-add operations (FMAs). +# Since these FMAs can increase the performance of many numerical algorithms, +# we need to opt-in explicitly. +# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details. +@muladd begin + +# Interpolate tree_node_coordinates to each quadrant at the specified nodes +function calc_node_coordinates!(node_coordinates, + mesh::T8codeMesh{3}, + nodes::AbstractVector) + # We use `StrideArray`s here since these buffers are used in performance-critical + # places and the additional information passed to the compiler makes them faster + # than native `Array`s. + tmp1 = StrideArray(undef, real(mesh), + StaticInt(2), static_length(nodes), static_length(mesh.nodes)) + matrix1 = StrideArray(undef, real(mesh), + static_length(nodes), static_length(mesh.nodes)) + matrix2 = similar(matrix1) + baryweights_in = barycentric_weights(mesh.nodes) + + num_local_trees = t8_forest_get_num_local_trees(mesh.forest) + + current_index = 0 + for itree = 0:num_local_trees-1 + + tree_class = t8_forest_get_tree_class(mesh.forest, itree) + eclass_scheme = t8_forest_get_eclass_scheme(mesh.forest, tree_class) + num_elements_in_tree = t8_forest_get_tree_num_elements(mesh.forest, itree) + + for ielement = 0:num_elements_in_tree-1 + element = t8_forest_get_element_in_tree(mesh.forest, itree, ielement) + element_level = t8_element_level(eclass_scheme, element) + + element_length = t8_hex_len(element_level) / t8_hex_root_len + + element_coords = Array{Float64}(undef, 3) + t8_element_vertex_reference_coords(eclass_scheme, element, 0, pointer(element_coords)) + + nodes_out_x = 2 * (element_length * 1/2 * (nodes .+ 1) .+ element_coords[1]) .- 1 + nodes_out_y = 2 * (element_length * 1/2 * (nodes .+ 1) .+ element_coords[2]) .- 1 + nodes_out_z = 2 * (element_length * 1/2 * (nodes .+ 1) .+ element_coords[3]) .- 1 + + polynomial_interpolation_matrix!(matrix1, mesh.nodes, nodes_out_x, baryweights_in) + polynomial_interpolation_matrix!(matrix2, mesh.nodes, nodes_out_y, baryweights_in) + polynomial_interpolation_matrix!(matrix3, mesh.nodes, nodes_out_z, baryweights_in) + + multiply_dimensionwise!( + view(node_coordinates, :, :, :, :, current_index += 1), + matrix1, matrix2, + view(mesh.tree_node_coordinates, :, :, :, :, itree+1), + tmp1 + ) + end + end + + return node_coordinates +end + +end # @muladd From 12b792c85ab5fb193e54242a84ef2b07bc36c90a Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Wed, 14 Jun 2023 14:41:12 +0200 Subject: [PATCH 042/128] Fxinig minor bugs. --- src/auxiliary/t8code.jl | 12 ++++++++++-- src/solvers/dgsem_t8code/containers.jl | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index d992e73f8c..e6d4c89f3f 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -204,8 +204,16 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari # Last entry is the large element. mortars.neighbor_ids[end, mortar_id] = current_index + 1 - # First `1:end-1` entries are the smaller elements. - mortars.neighbor_ids[1:end-1, mortar_id] .= neighbor_ielements .+ 1 + if orientation == 0 + # First `1:end-1` entries are the smaller elements. + mortars.neighbor_ids[1:end-1, mortar_id] .= neighbor_ielements .+ 1 + else + # Since the orientation is reversed we have to account for this + # when filling the `neighbor_ids` array. + # mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 + # mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[:, mortar_id] = reverse(neighbor_ielements) + 1 + end init_mortar_node_indices!(mortars, faces, orientation, mortar_id) diff --git a/src/solvers/dgsem_t8code/containers.jl b/src/solvers/dgsem_t8code/containers.jl index a4506440d6..e808a7336d 100644 --- a/src/solvers/dgsem_t8code/containers.jl +++ b/src/solvers/dgsem_t8code/containers.jl @@ -18,7 +18,7 @@ function reinitialize_containers!(mesh::T8codeMesh, equations, dg::DGSEM, cache) @unpack boundaries = cache resize!(boundaries, mesh.nboundaries) - trixi_t8_fill_mesh_info(mesh.forest, elements, interfaces, mortars, boundaries, mesh.boundary_names, mesh) + trixi_t8_fill_mesh_info(mesh.forest, elements, interfaces, mortars, boundaries, mesh.boundary_names) return nothing end From ca3797ed0c2d3eabf3453de1ca62ef0695e88722 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Wed, 14 Jun 2023 16:05:23 +0200 Subject: [PATCH 043/128] Fixed minor typo. --- src/auxiliary/t8code.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index e6d4c89f3f..edf65ee34a 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -212,7 +212,7 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari # when filling the `neighbor_ids` array. # mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 # mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 - mortars.neighbor_ids[:, mortar_id] = reverse(neighbor_ielements) + 1 + mortars.neighbor_ids[1:end-1, mortar_id] = reverse(neighbor_ielements) .+ 1 end init_mortar_node_indices!(mortars, faces, orientation, mortar_id) From bc7e219a3595fa94dcf1b5de273983c0c6a4c9ad Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Wed, 14 Jun 2023 17:09:44 +0200 Subject: [PATCH 044/128] Added first 3d example and fixed segfault. --- .../t8code_3d_dgsem/elixir_advection_basic.jl | 68 +++++++++++++++++++ src/solvers/dgsem_t8code/containers_3d.jl | 7 +- src/solvers/dgsem_t8code/dg.jl | 1 + 3 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 examples/t8code_3d_dgsem/elixir_advection_basic.jl diff --git a/examples/t8code_3d_dgsem/elixir_advection_basic.jl b/examples/t8code_3d_dgsem/elixir_advection_basic.jl new file mode 100644 index 0000000000..2c8bb5972d --- /dev/null +++ b/examples/t8code_3d_dgsem/elixir_advection_basic.jl @@ -0,0 +1,68 @@ +# The same setup as tree_3d_dgsem/elixir_advection_basic.jl +# to verify the T8codeMesh implementation against TreeMesh + +using OrdinaryDiffEq +using Trixi +# using Debugger + +############################################################################### +# semidiscretization of the linear advection equation + +advection_velocity = (0.2, -0.7, 0.5) +equations = LinearScalarAdvectionEquation3D(advection_velocity) + +# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux +solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) + +coordinates_min = (-1.0, -1.0, -1.0) # minimum coordinates (min(x), min(y), min(z)) +coordinates_max = ( 1.0, 1.0, 1.0) # maximum coordinates (max(x), max(y), max(z)) + +mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) + +# Create P4estMesh with 8 x 8 x 8 elements (note `refinement_level=1`) +trees_per_dimension = (4, 4, 4) +mesh = T8codeMesh(trees_per_dimension, polydeg=3, + mapping=mapping, + initial_refinement_level=1) + +# A semidiscretization collects data structures and functions for the spatial discretization +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_convergence_test, solver) + +############################################################################### +# ODE solvers, callbacks etc. + +# Create ODE problem with time span from 0.0 to 1.0 +tspan = (0.0, 1.0) +ode = semidiscretize(semi, tspan) + +# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup +# and resets the timers +summary_callback = SummaryCallback() + +# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results +analysis_callback = AnalysisCallback(semi, interval=100) + +# The SaveRestartCallback allows to save a file from which a Trixi.jl simulation can be restarted +save_restart = SaveRestartCallback(interval=100, + save_final_restart=true) + +# The SaveSolutionCallback allows to save the solution to a file in regular intervals +save_solution = SaveSolutionCallback(interval=100, + solution_variables=cons2prim) + +# The StepsizeCallback handles the re-calculation of the maximum Δt after each time step +stepsize_callback = StepsizeCallback(cfl=1.2) + +# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver +callbacks = CallbackSet(summary_callback, analysis_callback, save_restart, save_solution, stepsize_callback) + +############################################################################### +# run the simulation + +# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep=false, callback=callbacks); + +# Print the timer summary +summary_callback() diff --git a/src/solvers/dgsem_t8code/containers_3d.jl b/src/solvers/dgsem_t8code/containers_3d.jl index e6d87d5c03..503509fd58 100644 --- a/src/solvers/dgsem_t8code/containers_3d.jl +++ b/src/solvers/dgsem_t8code/containers_3d.jl @@ -12,10 +12,11 @@ function calc_node_coordinates!(node_coordinates, # places and the additional information passed to the compiler makes them faster # than native `Array`s. tmp1 = StrideArray(undef, real(mesh), - StaticInt(2), static_length(nodes), static_length(mesh.nodes)) + StaticInt(3), static_length(nodes), static_length(mesh.nodes), static_length(mesh.nodes)) matrix1 = StrideArray(undef, real(mesh), static_length(nodes), static_length(mesh.nodes)) matrix2 = similar(matrix1) + matrix3 = similar(matrix1) baryweights_in = barycentric_weights(mesh.nodes) num_local_trees = t8_forest_get_num_local_trees(mesh.forest) @@ -33,7 +34,7 @@ function calc_node_coordinates!(node_coordinates, element_length = t8_hex_len(element_level) / t8_hex_root_len - element_coords = Array{Float64}(undef, 3) + element_coords = Vector{Cdouble}(undef, 3) t8_element_vertex_reference_coords(eclass_scheme, element, 0, pointer(element_coords)) nodes_out_x = 2 * (element_length * 1/2 * (nodes .+ 1) .+ element_coords[1]) .- 1 @@ -46,7 +47,7 @@ function calc_node_coordinates!(node_coordinates, multiply_dimensionwise!( view(node_coordinates, :, :, :, :, current_index += 1), - matrix1, matrix2, + matrix1, matrix2, matrix3, view(mesh.tree_node_coordinates, :, :, :, :, itree+1), tmp1 ) diff --git a/src/solvers/dgsem_t8code/dg.jl b/src/solvers/dgsem_t8code/dg.jl index e92f9000c2..3a76042f41 100644 --- a/src/solvers/dgsem_t8code/dg.jl +++ b/src/solvers/dgsem_t8code/dg.jl @@ -24,5 +24,6 @@ end include("containers.jl") include("containers_2d.jl") +include("containers_3d.jl") end # @muladd From 9ee3c219d61f42d3665468a07f6386276cbfa767 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Thu, 15 Jun 2023 14:13:41 +0200 Subject: [PATCH 045/128] Added many 3D examples and tests. --- .../elixir_advection_nonconforming_flag.jl | 2 +- .../t8code_3d_dgsem/elixir_advection_amr.jl | 74 ++++++++++++ ...lixir_advection_amr_unstructured_curved.jl | 113 ++++++++++++++++++ .../t8code_3d_dgsem/elixir_advection_basic.jl | 18 +-- .../elixir_advection_cubed_sphere.jl | 60 ++++++++++ .../elixir_advection_nonconforming.jl | 105 ++++++++++++++++ .../elixir_advection_unstructured_curved.jl | 106 ++++++++++++++++ src/callbacks_step/amr_dg2d.jl | 2 +- src/callbacks_step/amr_dg3d.jl | 73 ++++++++++- src/callbacks_step/analysis_dg3d.jl | 14 +-- src/callbacks_step/stepsize_dg3d.jl | 4 +- src/meshes/t8code_mesh.jl | 33 +++-- src/solvers/dgsem_structured/dg_3d.jl | 12 +- src/solvers/dgsem_tree/dg_3d.jl | 18 +-- src/solvers/dgsem_tree/indicators_3d.jl | 2 +- test/test_t8code_3d.jl | 71 +++++++++++ 16 files changed, 653 insertions(+), 54 deletions(-) create mode 100644 examples/t8code_3d_dgsem/elixir_advection_amr.jl create mode 100644 examples/t8code_3d_dgsem/elixir_advection_amr_unstructured_curved.jl create mode 100644 examples/t8code_3d_dgsem/elixir_advection_cubed_sphere.jl create mode 100644 examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl create mode 100644 examples/t8code_3d_dgsem/elixir_advection_unstructured_curved.jl create mode 100644 test/test_t8code_3d.jl diff --git a/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl b/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl index ef42eb8d77..a595f708ab 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl @@ -20,7 +20,7 @@ f4(s) = SVector(s, 1.0 + sin(0.5 * pi * s)) faces = (f1, f2, f3, f4) mapping = Trixi.transfinite_mapping(faces) -# Create P4estMesh with 3 x 2 trees and 6 x 4 elements, +# Create T8codeMesh with 3 x 2 trees and 6 x 4 elements, # approximate the geometry with a smaller polydeg for testing. trees_per_dimension = (3, 2) mesh = T8codeMesh(trees_per_dimension, polydeg=3, diff --git a/examples/t8code_3d_dgsem/elixir_advection_amr.jl b/examples/t8code_3d_dgsem/elixir_advection_amr.jl new file mode 100644 index 0000000000..eff5688381 --- /dev/null +++ b/examples/t8code_3d_dgsem/elixir_advection_amr.jl @@ -0,0 +1,74 @@ +# The same setup as tree_3d_dgsem/elixir_advection_amr.jl +# to verify the T8codeMesh implementation against TreeMesh. + +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the linear advection equation + +advection_velocity = (0.2, -0.7, 0.5) +equations = LinearScalarAdvectionEquation3D(advection_velocity) + +initial_condition = initial_condition_gauss +solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) + +coordinates_min = (-5.0, -5.0, -5.0) +coordinates_max = ( 5.0, 5.0, 5.0) +trees_per_dimension = (1, 1, 1) + +mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) + +# Note that it is not necessary to use mesh polydeg lower than the solver polydeg +# on a Cartesian mesh. +# See https://doi.org/10.1007/s10915-018-00897-9, Section 6. +mesh = T8codeMesh(trees_per_dimension, polydeg=1, + mapping=mapping, + initial_refinement_level=4) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 0.3) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval=analysis_interval, + extra_analysis_integrals=(entropy,)) + +alive_callback = AliveCallback(analysis_interval=analysis_interval) + +# save_solution = SaveSolutionCallback(interval=100, +# save_initial_solution=true, +# save_final_solution=true, +# solution_variables=cons2prim) + +amr_controller = ControllerThreeLevel(semi, IndicatorMax(semi, variable=first), + base_level=4, + med_level=5, med_threshold=0.1, + max_level=6, max_threshold=0.6) +amr_callback = AMRCallback(semi, amr_controller, + interval=5, + adapt_initial_condition=true, + adapt_initial_condition_only_refine=true) + +stepsize_callback = StepsizeCallback(cfl=1.2) + +callbacks = CallbackSet(summary_callback, + analysis_callback, + alive_callback, + # save_solution, + amr_callback, + stepsize_callback) + +############################################################################### +# run the simulation + +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep=false, callback=callbacks); +summary_callback() # print the timer summary diff --git a/examples/t8code_3d_dgsem/elixir_advection_amr_unstructured_curved.jl b/examples/t8code_3d_dgsem/elixir_advection_amr_unstructured_curved.jl new file mode 100644 index 0000000000..57d8de47cb --- /dev/null +++ b/examples/t8code_3d_dgsem/elixir_advection_amr_unstructured_curved.jl @@ -0,0 +1,113 @@ +using Downloads: download +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the linear advection equation + +advection_velocity = (0.2, -0.7, 0.5) +equations = LinearScalarAdvectionEquation3D(advection_velocity) + +# Solver with polydeg=4 to ensure free stream preservation (FSP) on non-conforming meshes. +# The polydeg of the solver must be at least twice as big as the polydeg of the mesh. +# See https://doi.org/10.1007/s10915-018-00897-9, Section 6. +solver = DGSEM(polydeg=4, surface_flux=flux_lax_friedrichs) + +initial_condition = initial_condition_gauss +boundary_condition = BoundaryConditionDirichlet(initial_condition) + +boundary_conditions = Dict( + :all => boundary_condition +) + +# Mapping as described in https://arxiv.org/abs/2012.12040 but with less warping. +# The mapping will be interpolated at tree level, and then refined without changing +# the geometry interpolant. The original mapping applied to this unstructured mesh +# causes some Jacobians to be negative, which makes the mesh invalid. +function mapping(xi, eta, zeta) + # Don't transform input variables between -1 and 1 onto [0,3] to obtain curved boundaries + # xi = 1.5 * xi_ + 1.5 + # eta = 1.5 * eta_ + 1.5 + # zeta = 1.5 * zeta_ + 1.5 + + y = eta + 1/4 * (cos(1.5 * pi * (2 * xi - 3)/3) * + cos(0.5 * pi * (2 * eta - 3)/3) * + cos(0.5 * pi * (2 * zeta - 3)/3)) + + x = xi + 1/4 * (cos(0.5 * pi * (2 * xi - 3)/3) * + cos(2 * pi * (2 * y - 3)/3) * + cos(0.5 * pi * (2 * zeta - 3)/3)) + + z = zeta + 1/4 * (cos(0.5 * pi * (2 * x - 3)/3) * + cos(pi * (2 * y - 3)/3) * + cos(0.5 * pi * (2 * zeta - 3)/3)) + + # Transform the weird deformed cube to be approximately the size of [-5,5]^3 to match IC + return SVector(5 * x, 5 * y, 5 * z) +end + +# Unstructured mesh with 48 cells of the cube domain [-1, 1]^3 +mesh_file = joinpath(@__DIR__, "cube_unstructured_2.inp") +isfile(mesh_file) || download("https://gist.githubusercontent.com/efaulhaber/b8df0033798e4926dec515fc045e8c2c/raw/b9254cde1d1fb64b6acc8416bc5ccdd77a240227/cube_unstructured_2.inp", + mesh_file) + +# INP mesh files are only support by p4est. Hence, we +# create a p4est connecvity object first from which +# we can create a t8code mesh. +conn = Trixi.read_inp_p4est(mesh_file,Val(3)) + +mesh = T8codeMesh{3}(conn, polydeg=2, + mapping=mapping, + initial_refinement_level=1) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, boundary_conditions=boundary_conditions) + + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 8.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval=analysis_interval, + extra_analysis_integrals=(entropy,)) + +alive_callback = AliveCallback(analysis_interval=analysis_interval) + +# save_restart = SaveRestartCallback(interval=100, +# save_final_restart=true) +# +# save_solution = SaveSolutionCallback(interval=100, +# save_initial_solution=true, +# save_final_solution=true, +# solution_variables=cons2prim) + +amr_controller = ControllerThreeLevel(semi, IndicatorMax(semi, variable=first), + base_level=1, + med_level=2, med_threshold=0.1, + max_level=3, max_threshold=0.6) +amr_callback = AMRCallback(semi, amr_controller, + interval=5, + adapt_initial_condition=true, + adapt_initial_condition_only_refine=true) + +stepsize_callback = StepsizeCallback(cfl=1.2) + +callbacks = CallbackSet(summary_callback, + analysis_callback, + alive_callback, + # save_restart, + # save_solution, + amr_callback, + stepsize_callback) + +############################################################################### +# run the simulation + +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep=false, callback=callbacks); +summary_callback() # print the timer summary diff --git a/examples/t8code_3d_dgsem/elixir_advection_basic.jl b/examples/t8code_3d_dgsem/elixir_advection_basic.jl index 2c8bb5972d..2d1358f56d 100644 --- a/examples/t8code_3d_dgsem/elixir_advection_basic.jl +++ b/examples/t8code_3d_dgsem/elixir_advection_basic.jl @@ -42,19 +42,23 @@ summary_callback = SummaryCallback() # The AnalysisCallback allows to analyse the solution in regular intervals and prints the results analysis_callback = AnalysisCallback(semi, interval=100) -# The SaveRestartCallback allows to save a file from which a Trixi.jl simulation can be restarted -save_restart = SaveRestartCallback(interval=100, - save_final_restart=true) +# Not supported yet. +# # The SaveRestartCallback allows to save a file from which a Trixi.jl simulation can be restarted +# save_restart = SaveRestartCallback(interval=100, +# save_final_restart=true) -# The SaveSolutionCallback allows to save the solution to a file in regular intervals -save_solution = SaveSolutionCallback(interval=100, - solution_variables=cons2prim) +# Not supported yet. +# # The SaveSolutionCallback allows to save the solution to a file in regular intervals +# save_solution = SaveSolutionCallback(interval=100, +# solution_variables=cons2prim) # The StepsizeCallback handles the re-calculation of the maximum Δt after each time step stepsize_callback = StepsizeCallback(cfl=1.2) # Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver -callbacks = CallbackSet(summary_callback, analysis_callback, save_restart, save_solution, stepsize_callback) +callbacks = CallbackSet(summary_callback, analysis_callback, + # save_restart, save_solution, + stepsize_callback) ############################################################################### # run the simulation diff --git a/examples/t8code_3d_dgsem/elixir_advection_cubed_sphere.jl b/examples/t8code_3d_dgsem/elixir_advection_cubed_sphere.jl new file mode 100644 index 0000000000..fe8937a09e --- /dev/null +++ b/examples/t8code_3d_dgsem/elixir_advection_cubed_sphere.jl @@ -0,0 +1,60 @@ +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the linear advection equation + +advection_velocity = (0.2, -0.7, 0.5) +equations = LinearScalarAdvectionEquation3D(advection_velocity) + +# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux +solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) + +initial_condition = initial_condition_convergence_test + +boundary_condition = BoundaryConditionDirichlet(initial_condition) +boundary_conditions = Dict( + :inside => boundary_condition, + :outside => boundary_condition, +) + +mesh = Trixi.P4estMeshCubedSphere(5, 3, 0.5, 0.5, + polydeg=3, initial_refinement_level=0) + +# A semidiscretization collects data structures and functions for the spatial discretization +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, boundary_conditions=boundary_conditions) + +############################################################################### +# ODE solvers, callbacks etc. + +# Create ODE problem with time span from 0.0 to 1.0 +tspan = (0.0, 1.0) +ode = semidiscretize(semi, tspan) + +# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup +# and resets the timers +summary_callback = SummaryCallback() + +# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results +analysis_callback = AnalysisCallback(semi, interval=100) + +# The SaveSolutionCallback allows to save the solution to a file in regular intervals +save_solution = SaveSolutionCallback(interval=100, + solution_variables=cons2prim) + +# The StepsizeCallback handles the re-calculation of the maximum Δt after each time step +stepsize_callback = StepsizeCallback(cfl=1.2) + +# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver +callbacks = CallbackSet(summary_callback, analysis_callback, save_solution, stepsize_callback) + +############################################################################### +# run the simulation + +# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep=false, callback=callbacks); + +# Print the timer summary +summary_callback() diff --git a/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl b/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl new file mode 100644 index 0000000000..71fb178f61 --- /dev/null +++ b/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl @@ -0,0 +1,105 @@ +using OrdinaryDiffEq +using Trixi + +############################################################################### +# Semidiscretization of the linear advection equation. + +advection_velocity = (0.2, -0.7, 0.5) +equations = LinearScalarAdvectionEquation3D(advection_velocity) + +# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux. +solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) + +coordinates_min = (-1.0, -1.0, -1.0) # minimum coordinates (min(x), min(y), min(z)) +coordinates_max = ( 1.0, 1.0, 1.0) # maximum coordinates (max(x), max(y), max(z)) +trees_per_dimension = (1, 1, 1) + +mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) + +# Note that it is not necessary to use mesh polydeg lower than the solver polydeg +# on a Cartesian mesh. +# See https://doi.org/10.1007/s10915-018-00897-9, Section 6. +mesh = T8codeMesh(trees_per_dimension, polydeg=3, + mapping=mapping, + initial_refinement_level=2) + +function adapt_callback(forest, + forest_from, + which_tree, + lelement_id, + ts, + is_family, + num_elements, + elements_ptr) :: Cint + + vertex = Vector{Cdouble}(undef,3) + elements = unsafe_wrap(Array, elements_ptr, num_elements) + Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) + level = Trixi.t8_element_level(ts,elements[1]) + + if all(vertex .< 1e-8) && level < 3 + # return true (refine) + return 1 + else + # return false (don't refine) + return 0 + end +end + +@assert(Trixi.t8_forest_is_committed(mesh.forest) != 0); + +# Init new forest. +new_forest_ref = Ref{Trixi.t8_forest_t}() +Trixi.t8_forest_init(new_forest_ref); +new_forest = new_forest_ref[] + +let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0, do_ghost = 1 + Trixi.t8_forest_set_user_data(new_forest, C_NULL) + Trixi.t8_forest_set_adapt(new_forest, mesh.forest, @Trixi.t8_adapt_callback(adapt_callback), recursive) + Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) + Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) + Trixi.t8_forest_set_ghost(new_forest, do_ghost, Trixi.T8_GHOST_FACES); + Trixi.t8_forest_commit(new_forest) +end + +mesh.forest = new_forest + +# A semidiscretization collects data structures and functions for the spatial discretization +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_convergence_test, solver) + +############################################################################### +# ODE solvers, callbacks etc. + +# Create ODE problem with time span from 0.0 to 1.0 +tspan = (0.0, 1.0) +ode = semidiscretize(semi, tspan) + +# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup +# and resets the timers +summary_callback = SummaryCallback() + +# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results +analysis_callback = AnalysisCallback(semi, interval=100) + +# Not supported yet. +# # The SaveSolutionCallback allows to save the solution to a file in regular intervals +# save_solution = SaveSolutionCallback(interval=100, +# solution_variables=cons2prim) + +# The StepsizeCallback handles the re-calculation of the maximum Δt after each time step +stepsize_callback = StepsizeCallback(cfl=1.6) + +# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver +callbacks = CallbackSet(summary_callback, analysis_callback, # save_solution, + stepsize_callback) + +############################################################################### +# run the simulation + +# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep=false, callback=callbacks); + +# Print the timer summary +summary_callback() diff --git a/examples/t8code_3d_dgsem/elixir_advection_unstructured_curved.jl b/examples/t8code_3d_dgsem/elixir_advection_unstructured_curved.jl new file mode 100644 index 0000000000..e1b1488fe5 --- /dev/null +++ b/examples/t8code_3d_dgsem/elixir_advection_unstructured_curved.jl @@ -0,0 +1,106 @@ +using Downloads: download +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the linear advection equation + +advection_velocity = (0.2, -0.7, 0.5) +equations = LinearScalarAdvectionEquation3D(advection_velocity) + +# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux +solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) + +initial_condition = initial_condition_convergence_test + +boundary_condition = BoundaryConditionDirichlet(initial_condition) +boundary_conditions = Dict( + :all => boundary_condition +) + +# Mapping as described in https://arxiv.org/abs/2012.12040 but with less warping. +# The mapping will be interpolated at tree level, and then refined without changing +# the geometry interpolant. The original mapping applied to this unstructured mesh +# causes some Jacobians to be negative, which makes the mesh invalid. +function mapping(xi, eta, zeta) + # Don't transform input variables between -1 and 1 onto [0,3] to obtain curved boundaries + # xi = 1.5 * xi_ + 1.5 + # eta = 1.5 * eta_ + 1.5 + # zeta = 1.5 * zeta_ + 1.5 + + y = eta + 1/6 * (cos(1.5 * pi * (2 * xi - 3)/3) * + cos(0.5 * pi * (2 * eta - 3)/3) * + cos(0.5 * pi * (2 * zeta - 3)/3)) + + x = xi + 1/6 * (cos(0.5 * pi * (2 * xi - 3)/3) * + cos(2 * pi * (2 * y - 3)/3) * + cos(0.5 * pi * (2 * zeta - 3)/3)) + + z = zeta + 1/6 * (cos(0.5 * pi * (2 * x - 3)/3) * + cos(pi * (2 * y - 3)/3) * + cos(0.5 * pi * (2 * zeta - 3)/3)) + + return SVector(x, y, z) +end + +# Unstructured mesh with 68 cells of the cube domain [-1, 1]^3 +mesh_file = joinpath(@__DIR__, "cube_unstructured_1.inp") +isfile(mesh_file) || download("https://gist.githubusercontent.com/efaulhaber/d45c8ac1e248618885fa7cc31a50ab40/raw/37fba24890ab37cfa49c39eae98b44faf4502882/cube_unstructured_1.inp", + mesh_file) + +# INP mesh files are only support by p4est. Hence, we +# create a p4est connecvity object first from which +# we can create a t8code mesh. +conn = Trixi.read_inp_p4est(mesh_file,Val(3)) + +mesh = T8codeMesh{3}(conn, polydeg=3, + mapping=mapping, + initial_refinement_level=2) + +# A semidiscretization collects data structures and functions for the spatial discretization +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, boundary_conditions=boundary_conditions) + +############################################################################### +# ODE solvers, callbacks etc. + +# Create ODE problem with time span from 0.0 to 0.1 +ode = semidiscretize(semi, (0.0, 0.1)); + +# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup +# and resets the timers +summary_callback = SummaryCallback() + +# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval=analysis_interval) + +alive_callback = AliveCallback(analysis_interval=analysis_interval) + +# Not supported yet. +# # The SaveRestartCallback allows to save a file from which a Trixi.jl simulation can be restarted +# save_restart = SaveRestartCallback(interval=100, +# save_final_restart=true) + +# Not supported yet. +# # The SaveSolutionCallback allows to save the solution to a file in regular intervals +# save_solution = SaveSolutionCallback(interval=100, +# solution_variables=cons2prim) + +# The StepsizeCallback handles the re-calculation of the maximum Δt after each time step +stepsize_callback = StepsizeCallback(cfl=1.2) + +# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver +callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, + # save_restart, save_solution, + stepsize_callback) + +############################################################################### +# run the simulation + +# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep=false, callback=callbacks); + +# Print the timer summary +summary_callback() diff --git a/src/callbacks_step/amr_dg2d.jl b/src/callbacks_step/amr_dg2d.jl index 620b1f05da..03189a25e1 100644 --- a/src/callbacks_step/amr_dg2d.jl +++ b/src/callbacks_step/amr_dg2d.jl @@ -348,7 +348,7 @@ function adapt!(u_ode::AbstractVector, adaptor, mesh::T8codeMesh{2}, equations, old_index = 1 new_index = 1 - # Note: This is true for `quads` only. + # Note: This is true for `quads`. T8_CHILDREN = 4 # Retain current solution data. diff --git a/src/callbacks_step/amr_dg3d.jl b/src/callbacks_step/amr_dg3d.jl index 44f73547ef..7661ab86c7 100644 --- a/src/callbacks_step/amr_dg3d.jl +++ b/src/callbacks_step/amr_dg3d.jl @@ -293,12 +293,83 @@ end # this method is called when an `ControllerThreeLevel` is constructed function create_cache(::Type{ControllerThreeLevel}, - mesh::Union{TreeMesh{3}, P4estMesh{3}}, + mesh::Union{TreeMesh{3}, P4estMesh{3}, T8codeMesh{3}}, equations, dg::DG, cache) controller_value = Vector{Int}(undef, nelements(dg, cache)) return (; controller_value) end +# Coarsen and refine elements in the DG solver based on a difference list. +function adapt!(u_ode::AbstractVector, adaptor, mesh::T8codeMesh{3}, equations, dg::DGSEM, cache, difference) + + # Return early if there is nothing to do. + if !any(difference .!= 0) + return nothing + end + + # Number of (local) cells/elements. + old_nelems = nelements(dg, cache) + new_nelems = ncells(mesh) + + # Local element indices. + old_index = 1 + new_index = 1 + + # Note: This is true for `hexs`. + T8_CHILDREN = 8 + + # Retain current solution data. + old_u_ode = copy(u_ode) + + GC.@preserve old_u_ode begin + old_u = wrap_array(old_u_ode, mesh, equations, dg, cache) + + reinitialize_containers!(mesh, equations, dg, cache) + + resize!(u_ode, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) + u = wrap_array(u_ode, mesh, equations, dg, cache) + + u_tmp1 = Array{eltype(u), 4}(undef, nvariables(equations), nnodes(dg), nnodes(dg), nnodes(dg)) + u_tmp2 = Array{eltype(u), 4}(undef, nvariables(equations), nnodes(dg), nnodes(dg), nnodes(dg)) + + while old_index <= old_nelems && new_index <= new_nelems + + if difference[old_index] > 0 # Refine. + + # Refine element and store solution directly in new data structure. + refine_element!(u, new_index, old_u, old_index, adaptor, equations, dg, u_tmp1, u_tmp2) + + old_index += 1 + new_index += T8_CHILDREN + + elseif difference[old_index] < 0 # Coarsen. + + # If an element is to be removed, sanity check if the following elements + # are also marked - otherwise there would be an error in the way the + # cells/elements are sorted. + @assert all(difference[old_index:(old_index+T8_CHILDREN-1)] .< 0) "bad cell/element order" + + # Coarsen elements and store solution directly in new data structure. + coarsen_elements!(u, new_index, old_u, old_index, adaptor, equations, dg, u_tmp1, u_tmp2) + + old_index += T8_CHILDREN + new_index += 1 + + else # No changes. + + # Copy old element data to new element container. + @views u[:, .., new_index] .= old_u[:, .., old_index] + + old_index += 1 + new_index += 1 + end + + end # while + + end # GC.@preserve old_u_ode + + return nothing +end end # @muladd diff --git a/src/callbacks_step/analysis_dg3d.jl b/src/callbacks_step/analysis_dg3d.jl index 77cf1f819e..4819dcbf8f 100644 --- a/src/callbacks_step/analysis_dg3d.jl +++ b/src/callbacks_step/analysis_dg3d.jl @@ -30,7 +30,7 @@ function create_cache_analysis(analyzer, mesh::TreeMesh{3}, end -function create_cache_analysis(analyzer, mesh::Union{StructuredMesh{3}, P4estMesh{3}}, +function create_cache_analysis(analyzer, mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, equations, dg::DG, cache, RealT, uEltype) @@ -98,7 +98,7 @@ end function calc_error_norms(func, u, t, analyzer, - mesh::Union{StructuredMesh{3}, P4estMesh{3}}, + mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, equations, initial_condition, dg::DGSEM, cache, cache_analysis) @unpack vandermonde, weights = analyzer @@ -162,7 +162,7 @@ end function integrate_via_indices(func::Func, u, - mesh::Union{StructuredMesh{3}, P4estMesh{3}}, + mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, equations, dg::DGSEM, cache, args...; normalize=true) where {Func} @unpack weights = dg.basis @@ -190,7 +190,7 @@ end function integrate(func::Func, u, - mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}}, + mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, equations, dg::DG, cache; normalize=true) where {Func} integrate_via_indices(u, mesh, equations, dg, cache; normalize=normalize) do u, i, j, k, element, equations, dg u_local = get_node_vars(u, equations, dg, i, j, k, element) @@ -216,7 +216,7 @@ end function analyze(::typeof(entropy_timederivative), du, u, t, - mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}}, + mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, equations, dg::DG, cache) # Calculate ∫(∂S/∂u ⋅ ∂u/∂t)dΩ integrate_via_indices(u, mesh, equations, dg, cache, du) do u, i, j, k, element, equations, dg, du @@ -244,7 +244,7 @@ function analyze(::Val{:l2_divb}, du, u, t, end function analyze(::Val{:l2_divb}, du, u, t, - mesh::Union{StructuredMesh{3}, P4estMesh{3}}, equations::IdealGlmMhdEquations3D, + mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, equations::IdealGlmMhdEquations3D, dg::DGSEM, cache) @unpack contravariant_vectors = cache.elements integrate_via_indices(u, mesh, equations, dg, cache, cache, dg.basis.derivative_matrix) do u, i, j, k, element, equations, dg, cache, derivative_matrix @@ -289,7 +289,7 @@ function analyze(::Val{:linf_divb}, du, u, t, end function analyze(::Val{:linf_divb}, du, u, t, - mesh::Union{StructuredMesh{3}, P4estMesh{3}}, equations::IdealGlmMhdEquations3D, + mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, equations::IdealGlmMhdEquations3D, dg::DGSEM, cache) @unpack derivative_matrix, weights = dg.basis @unpack contravariant_vectors = cache.elements diff --git a/src/callbacks_step/stepsize_dg3d.jl b/src/callbacks_step/stepsize_dg3d.jl index 492ee3d9a0..1a9cfc9022 100644 --- a/src/callbacks_step/stepsize_dg3d.jl +++ b/src/callbacks_step/stepsize_dg3d.jl @@ -44,7 +44,7 @@ function max_dt(u, t, mesh::TreeMesh{3}, end -function max_dt(u, t, mesh::Union{StructuredMesh{3}, P4estMesh{3}}, +function max_dt(u, t, mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, constant_speed::False, equations, dg::DG, cache) # to avoid a division by zero if the speed vanishes everywhere, # e.g. for steady-state linear advection @@ -79,7 +79,7 @@ function max_dt(u, t, mesh::Union{StructuredMesh{3}, P4estMesh{3}}, end -function max_dt(u, t, mesh::Union{StructuredMesh{3}, P4estMesh{3}}, +function max_dt(u, t, mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, constant_speed::True, equations, dg::DG, cache) # to avoid a division by zero if the speed vanishes everywhere, # e.g. for steady-state linear advection diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 1b7945ebac..855515c348 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -11,6 +11,8 @@ mutable struct T8codeMesh{NDIMS, RealT<:Real, IsParallel, NDIMSP2, NNODES} <: Ab forest :: Ptr{t8_forest} # cpointer to forest is_parallel :: IsParallel + unsaved_changes ::Bool + # This specifies the geometry interpolation for each tree. tree_node_coordinates :: Array{RealT, NDIMSP2} # [dimension, i, j, k, tree] @@ -175,7 +177,7 @@ function T8codeMesh(trees_per_dimension; polydeg, tree_node_coordinates[:, i, j, itree] .= mapping_(cell_x_offset + dx[1] * nodes[i]/2, cell_y_offset + dx[2] * nodes[j]/2) end - else + elseif NDIMS == 3 cell_x_offset = (verts[1,1] - 0.5*(trees_per_dimension[1]-1)) * dx[1] cell_y_offset = (verts[2,1] - 0.5*(trees_per_dimension[2]-1)) * dx[2] cell_z_offset = (verts[3,1] - 0.5*(trees_per_dimension[3]-1)) * dx[3] @@ -185,6 +187,8 @@ function T8codeMesh(trees_per_dimension; polydeg, cell_y_offset + dx[2] * nodes[j]/2, cell_z_offset + dx[3] * nodes[k]/2) end + else + throw(ArgumentError("NDIMS should be 2 or 3.")) end if !periodicity[1] @@ -246,10 +250,11 @@ function T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh}; nodes_in = [-1.0, 1.0] matrix = polynomial_interpolation_matrix(nodes_in, nodes) - data_in = Array{RealT, 3}(undef, 2, 2, 2) - tmp1 = zeros(RealT, 2, length(nodes), length(nodes_in)) if NDIMS == 2 + data_in = Array{RealT, 3}(undef, 2, 2, 2) + tmp1 = zeros(RealT, 2, length(nodes), length(nodes_in)) + for itree in 0:num_local_trees-1 veptr = t8_cmesh_get_tree_vertices(cmesh, itree) verts = unsafe_wrap(Array,veptr,(3,1 << NDIMS)) @@ -278,25 +283,14 @@ function T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh}; tmp1 ) end - - else + elseif NDIMS == 3 + data_in = Array{RealT, 4}(undef, 3, 2, 2, 2) + tmp1 = zeros(RealT, 3, length(nodes), length(nodes_in), length(nodes_in)) for itree in 0:num_local_trees-1 veptr = t8_cmesh_get_tree_vertices(cmesh, itree) verts = unsafe_wrap(Array,veptr,(3,1 << NDIMS)) - # TODO: Check for negative volumes. Probably not necessary since this is - # done within t8code. - # u = verts[:,2] - verts[:,1] - # v = verts[:,3] - verts[:,1] - # w = [0.0,0.0,1.0] - - # vol = dot(cross(u,v),w) - - # if vol < 0.0 - # @warn "Discovered negative volumes in `cmesh`: vol = $vol" - # end - # Tree vertices are stored in z-order. @views data_in[:, 1, 1, 1] .= verts[1:3,1] @views data_in[:, 2, 1, 1] .= verts[1:3,2] @@ -311,12 +305,13 @@ function T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh}; # Interpolate corner coordinates to specified nodes. multiply_dimensionwise!( view(tree_node_coordinates, :, :, :, :, itree+1), - matrix, matrix, + matrix, matrix, matrix, data_in, tmp1 ) - end + else + throw(ArgumentError("NDIMS should be 2 or 3.")) end map_node_coordinates!(tree_node_coordinates, mapping) diff --git a/src/solvers/dgsem_structured/dg_3d.jl b/src/solvers/dgsem_structured/dg_3d.jl index 6c27e20632..393d662edf 100644 --- a/src/solvers/dgsem_structured/dg_3d.jl +++ b/src/solvers/dgsem_structured/dg_3d.jl @@ -45,7 +45,7 @@ end @inline function weak_form_kernel!(du, u, - element, mesh::Union{StructuredMesh{3}, P4estMesh{3}}, + element, mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, nonconservative_terms::False, equations, dg::DGSEM, cache, alpha=true) # true * [some floating point value] == [exactly the same floating point value] @@ -93,7 +93,7 @@ end # mapping terms, stored in `contravariant_vectors`, is peeled apart from the evaluation of # the physical fluxes in each Cartesian direction @inline function flux_differencing_kernel!(du, u, - element, mesh::Union{StructuredMesh{3}, P4estMesh{3}}, + element, mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, nonconservative_terms::False, equations, volume_flux, dg::DGSEM, cache, alpha=true) # true * [some floating point value] == [exactly the same floating point value] @@ -160,7 +160,7 @@ end end @inline function flux_differencing_kernel!(du, u, - element, mesh::Union{StructuredMesh{3}, P4estMesh{3}}, + element, mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, nonconservative_terms::True, equations, volume_flux, dg::DGSEM, cache, alpha=true) @unpack derivative_split = dg.basis @@ -234,7 +234,7 @@ end # "A provably entropy stable subcell shock capturing approach for high order split form DG for the compressible Euler equations" # [arXiv: 2008.12044v2](https://arxiv.org/pdf/2008.12044) @inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, fstar3_L, fstar3_R, u, - mesh::Union{StructuredMesh{3}, P4estMesh{3}}, nonconservative_terms::False, + mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, nonconservative_terms::False, equations, volume_flux_fv, dg::DGSEM, element, cache) @unpack contravariant_vectors = cache.elements @unpack weights, derivative_matrix = dg.basis @@ -318,7 +318,7 @@ end # # Calculate the finite volume fluxes inside curvilinear elements (**with non-conservative terms**). @inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, fstar3_L, fstar3_R, u, - mesh::Union{StructuredMesh{3}, P4estMesh{3}}, nonconservative_terms::True, + mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, nonconservative_terms::True, equations, volume_flux_fv, dg::DGSEM, element, cache) @unpack contravariant_vectors = cache.elements @unpack weights, derivative_matrix = dg.basis @@ -689,7 +689,7 @@ end function apply_jacobian!(du, - mesh::Union{StructuredMesh{3}, P4estMesh{3}}, + mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, equations, dg::DG, cache) @threaded for element in eachelement(dg, cache) diff --git a/src/solvers/dgsem_tree/dg_3d.jl b/src/solvers/dgsem_tree/dg_3d.jl index aef86e3de7..682092969d 100644 --- a/src/solvers/dgsem_tree/dg_3d.jl +++ b/src/solvers/dgsem_tree/dg_3d.jl @@ -36,13 +36,13 @@ end # The methods below are specialized on the volume integral type # and called from the basic `create_cache` method at the top. -function create_cache(mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}}, +function create_cache(mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, equations, volume_integral::VolumeIntegralFluxDifferencing, dg::DG, uEltype) NamedTuple() end -function create_cache(mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}}, equations, +function create_cache(mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, equations, volume_integral::VolumeIntegralShockCapturingHG, dg::DG, uEltype) element_ids_dg = Int[] element_ids_dgfv = Int[] @@ -72,7 +72,7 @@ function create_cache(mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}}, end -function create_cache(mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}}, equations, +function create_cache(mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, equations, volume_integral::VolumeIntegralPureLGLFiniteVolume, dg::DG, uEltype) A4dp1_x = Array{uEltype, 4} @@ -98,7 +98,7 @@ end # The methods below are specialized on the mortar type # and called from the basic `create_cache` method at the top. -function create_cache(mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}}, equations, mortar_l2::LobattoLegendreMortarL2, uEltype) +function create_cache(mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, equations, mortar_l2::LobattoLegendreMortarL2, uEltype) # TODO: Taal compare performance of different types A3d = Array{uEltype, 3} fstar_upper_left_threaded = A3d[A3d(undef, nvariables(equations), nnodes(mortar_l2), nnodes(mortar_l2)) @@ -121,7 +121,7 @@ end # TODO: Taal discuss/refactor timer, allowing users to pass a custom timer? function rhs!(du, u, t, - mesh::Union{TreeMesh{3}, P4estMesh{3}}, equations, + mesh::Union{TreeMesh{3}, P4estMesh{3}, T8codeMesh{3}}, equations, initial_condition, boundary_conditions, source_terms::Source, dg::DG, cache) where {Source} # Reset du @@ -178,7 +178,7 @@ end function calc_volume_integral!(du, u, - mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}}, + mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, nonconservative_terms, equations, volume_integral::VolumeIntegralWeakForm, dg::DGSEM, cache) @@ -224,7 +224,7 @@ end function calc_volume_integral!(du, u, - mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}}, + mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, nonconservative_terms, equations, volume_integral::VolumeIntegralFluxDifferencing, dg::DGSEM, cache) @@ -327,7 +327,7 @@ end # TODO: Taal dimension agnostic function calc_volume_integral!(du, u, - mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}}, + mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, nonconservative_terms, equations, volume_integral::VolumeIntegralShockCapturingHG, dg::DGSEM, cache) @@ -385,7 +385,7 @@ end @inline function fv_kernel!(du, u, - mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}}, + mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, nonconservative_terms, equations, volume_flux_fv, dg::DGSEM, cache, element, alpha=true) @unpack fstar1_L_threaded, fstar1_R_threaded, fstar2_L_threaded, fstar2_R_threaded, fstar3_L_threaded, fstar3_R_threaded = cache diff --git a/src/solvers/dgsem_tree/indicators_3d.jl b/src/solvers/dgsem_tree/indicators_3d.jl index c1e7aee886..0db20466ae 100644 --- a/src/solvers/dgsem_tree/indicators_3d.jl +++ b/src/solvers/dgsem_tree/indicators_3d.jl @@ -95,7 +95,7 @@ end end -function apply_smoothing!(mesh::Union{TreeMesh{3}, P4estMesh{3}}, alpha, alpha_tmp, dg, cache) +function apply_smoothing!(mesh::Union{TreeMesh{3}, P4estMesh{3}, T8codeMesh{3}}, alpha, alpha_tmp, dg, cache) # Diffuse alpha values by setting each alpha to at least 50% of neighboring elements' alpha # Copy alpha values such that smoothing is indpedenent of the element access order diff --git a/test/test_t8code_3d.jl b/test/test_t8code_3d.jl new file mode 100644 index 0000000000..b68707f2de --- /dev/null +++ b/test/test_t8code_3d.jl @@ -0,0 +1,71 @@ +module TestExamplesT8codeMesh3D + +using Test +using Trixi + +include("test_trixi.jl") + +EXAMPLES_DIR = joinpath(examples_dir(), "t8code_3d_dgsem") + +# Start with a clean environment: remove Trixi.jl output directory if it exists +outdir = "out" +isdir(outdir) && rm(outdir, recursive=true) +mkdir(outdir) + +@testset "T8codeMesh3D" begin + @trixi_testset "elixir_advection_basic.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_basic.jl"), + # Expected errors are exactly the same as with TreeMesh! + l2 = [0.00016263963870641478], + linf = [0.0014537194925779984]) + end + + @trixi_testset "elixir_advection_unstructured_curved.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_unstructured_curved.jl"), + l2 = [0.0004750004258546538], + linf = [0.026527551737137167]) + end + + @trixi_testset "elixir_advection_nonconforming.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_nonconforming.jl"), + l2 = [0.00253595715323843], + linf = [0.016486952252155795]) + + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000 + end + end + + @trixi_testset "elixir_advection_amr.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_amr.jl"), + # Expected errors are exactly the same as with TreeMesh! + l2 = [1.1302812803902801e-5], + linf = [0.0007889950196294793], + coverage_override = (maxiters=6, initial_refinement_level=1, base_level=1, med_level=2, max_level=3)) + end + + @trixi_testset "elixir_advection_amr_unstructured_curved.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_amr_unstructured_curved.jl"), + l2 = [1.6236411810065552e-5], + linf = [0.0010554006923731395], + tspan = (0.0, 1.0), + coverage_override = (maxiters=6, initial_refinement_level=0, base_level=0, med_level=1, max_level=2)) + end + + # @trixi_testset "elixir_advection_cubed_sphere.jl" begin + # @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_cubed_sphere.jl"), + # l2 = [0.002006918015656413], + # linf = [0.027655117058380085]) + # end + +end + +# Clean up afterwards: delete Trixi.jl output directory +@test_nowarn rm(outdir, recursive=true) + +end # module From 2639fabf711192b52ab79f1d56f5fabb2b63bcef Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Mon, 19 Jun 2023 15:54:37 +0200 Subject: [PATCH 046/128] Backup. --- ...terms_nonconforming_unstructured_curved.jl | 19 ++ ...terms_nonconforming_unstructured_curved.jl | 152 ++++++++++++++ src/auxiliary/t8code.jl | 186 ++++++++++++++++-- src/solvers/dgsem_p4est/containers_3d.jl | 1 - test/test_t8code_3d.jl | 11 +- 5 files changed, 355 insertions(+), 14 deletions(-) create mode 100644 examples/t8code_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl diff --git a/examples/p4est_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl b/examples/p4est_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl index dd39409175..8b6b359ea4 100644 --- a/examples/p4est_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl +++ b/examples/p4est_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl @@ -112,3 +112,22 @@ sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback save_everystep=false, callback=callbacks); summary_callback() # print the timer summary + + + + +data = [sol.u[2]] + +dataname = ["test"] +filename = "final.vtu" +write_data = true + +num_cells = Trixi.ncells(mesh) + +coords = reshape(semi.cache.elements.node_coordinates,size(semi.cache.elements.node_coordinates,1),prod(size(semi.cache.elements.node_coordinates)[2:end])) + +rd = Trixi.StartUpDG.RefElemData(Trixi.StartUpDG.Hex(), 4) + +Trixi.StartUpDG.MeshData_to_vtk(rd, num_cells, coords, data, dataname, filename, write_data); + + diff --git a/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl b/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl new file mode 100644 index 0000000000..a8e54770a1 --- /dev/null +++ b/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl @@ -0,0 +1,152 @@ +using Downloads: download +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations + +equations = CompressibleEulerEquations3D(1.4) + +initial_condition = initial_condition_convergence_test + +boundary_condition = BoundaryConditionDirichlet(initial_condition) +boundary_conditions = Dict( + :all => boundary_condition +) + +# Solver with polydeg=4 to ensure free stream preservation (FSP) on non-conforming meshes. +# The polydeg of the solver must be at least twice as big as the polydeg of the mesh. +# See https://doi.org/10.1007/s10915-018-00897-9, Section 6. +solver = DGSEM(polydeg=4, surface_flux=flux_lax_friedrichs, + volume_integral=VolumeIntegralWeakForm()) + +# Mapping as described in https://arxiv.org/abs/2012.12040 but with less warping. +# The mapping will be interpolated at tree level, and then refined without changing +# the geometry interpolant. The original mapping applied to this unstructured mesh +# causes some Jacobians to be negative, which makes the mesh invalid. +function mapping(xi, eta, zeta) + # Don't transform input variables between -1 and 1 onto [0,3] to obtain curved boundaries + # xi = 1.5 * xi_ + 1.5 + # eta = 1.5 * eta_ + 1.5 + # zeta = 1.5 * zeta_ + 1.5 + + y = eta + 1/6 * (cos(1.5 * pi * (2 * xi - 3)/3) * + cos(0.5 * pi * (2 * eta - 3)/3) * + cos(0.5 * pi * (2 * zeta - 3)/3)) + + x = xi + 1/6 * (cos(0.5 * pi * (2 * xi - 3)/3) * + cos(2 * pi * (2 * y - 3)/3) * + cos(0.5 * pi * (2 * zeta - 3)/3)) + + z = zeta + 1/6 * (cos(0.5 * pi * (2 * x - 3)/3) * + cos(pi * (2 * y - 3)/3) * + cos(0.5 * pi * (2 * zeta - 3)/3)) + + # Transform the weird deformed cube to be approximately the cube [0,2]^3 + return SVector(x + 1, y + 1, z + 1) +end + +# Unstructured mesh with 68 cells of the cube domain [-1, 1]^3 +mesh_file = joinpath(@__DIR__, "cube_unstructured_1.inp") +isfile(mesh_file) || download("https://gist.githubusercontent.com/efaulhaber/d45c8ac1e248618885fa7cc31a50ab40/raw/37fba24890ab37cfa49c39eae98b44faf4502882/cube_unstructured_1.inp", + mesh_file) + +# INP mesh files are only support by p4est. Hence, we +# create a p4est connecvity object first from which +# we can create a t8code mesh. +conn = Trixi.read_inp_p4est(mesh_file,Val(3)) + +# Mesh polydeg of 2 (half the solver polydeg) to ensure FSP (see above). +mesh = T8codeMesh{3}(conn, polydeg=2, + mapping=mapping, + initial_refinement_level=0) + +# Note: This is actually a `p8est_quadrant_t` which is much bigger than the +# following struct. But we only need the first four fields for our purpose. +struct t8_dhex_t + x :: Int32 + y :: Int32 + z :: Int32 + level :: Int8 + # [...] # See `p8est.h` in `p4est` for more info. +end + +function adapt_callback(forest, + forest_from, + which_tree, + lelement_id, + ts, + is_family, + num_elements, + elements_ptr) :: Cint + + vertex = Vector{Cdouble}(undef,3) + elements = unsafe_wrap(Array, elements_ptr, num_elements) + Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) + + el = unsafe_load(Ptr{t8_dhex_t}(elements[1])) + + if el.x == 0 && el.y == 0 && el.z == 0 && el.level < 2 + # return true (refine) + return 1 + else + # return false (don't refine) + return 0 + end +end + +@assert(Trixi.t8_forest_is_committed(mesh.forest) != 0); + +# Init new forest. +new_forest_ref = Ref{Trixi.t8_forest_t}() +Trixi.t8_forest_init(new_forest_ref); +new_forest = new_forest_ref[] + +let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0, do_ghost = 1 + Trixi.t8_forest_set_user_data(new_forest, C_NULL) + Trixi.t8_forest_set_adapt(new_forest, mesh.forest, @Trixi.t8_adapt_callback(adapt_callback), recursive) + Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) + Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) + Trixi.t8_forest_set_ghost(new_forest, do_ghost, Trixi.T8_GHOST_FACES); + Trixi.t8_forest_commit(new_forest) +end + +mesh.forest = new_forest + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, + source_terms=source_terms_convergence_test, + boundary_conditions=boundary_conditions) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 0.045) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval=analysis_interval) + +alive_callback = AliveCallback(analysis_interval=analysis_interval) + +# Not supported yet. +# save_solution = SaveSolutionCallback(interval=100, +# save_initial_solution=true, +# save_final_solution=true, +# solution_variables=cons2prim) + +stepsize_callback = StepsizeCallback(cfl=0.6) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + # save_solution, + stepsize_callback); + +############################################################################### +# run the simulation + +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep=false, callback=callbacks); +summary_callback() # print the timer summary diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index edf65ee34a..bf59ea379f 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -204,17 +204,10 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari # Last entry is the large element. mortars.neighbor_ids[end, mortar_id] = current_index + 1 - if orientation == 0 - # First `1:end-1` entries are the smaller elements. - mortars.neighbor_ids[1:end-1, mortar_id] .= neighbor_ielements .+ 1 - else - # Since the orientation is reversed we have to account for this - # when filling the `neighbor_ids` array. - # mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 - # mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 - mortars.neighbor_ids[1:end-1, mortar_id] = reverse(neighbor_ielements) .+ 1 - end - + # Fill in the `mortars.neighbor_ids` array and reorder if necessary. + trixi_t8_init_mortar_neighbor_ids!(mortars, faces[2], faces[1], orientation, neighbor_ielements, mortar_id) + + # Fill in the `mortars.node_indices` array. init_mortar_node_indices!(mortars, faces, orientation, mortar_id) # else: "level > neighbor_level" is skipped since we visit the mortar interface only once. @@ -248,6 +241,177 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari boundaries = local_num_boundary) end +function trixi_t8_init_mortar_neighbor_ids!(mortars::P4estMortarContainer{2}, my_face, other_face, orientation, neighbor_ielements, mortar_id) + if orientation == 0 + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[2] + 1 + else + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 + end +end + +# This routine was copied and adapated from `src/dgsem_p4est/containers_3d.jl`: `orientation_to_indices_p4est`. +function trixi_t8_init_mortar_neighbor_ids!(mortars::P4estMortarContainer{3}, my_face, other_face, orientation, neighbor_ielements, mortar_id) + # my_face and other_face are the face directions (zero-based) + # of "my side" and "other side" respectively. + # Face corner 0 of the face with the lower face direction connects to a corner of the other face. + # The number of this corner is the orientation code in `p4est`. + lower = my_face <= other_face + + # x_pos, y_neg, and z_pos are the directions in which the face has right-handed coordinates + # when looked at from the outside. + my_right_handed = my_face in (1, 2, 5) + other_right_handed = other_face in (1, 2, 5) + + # If both or none are right-handed when looked at from the outside, they will have different + # orientations when looked at from the same side of the interface. + flipped = my_right_handed == other_right_handed + + # In the following illustrations, the face corner numbering of `p4est` is shown. + # ξ and η are the local coordinates of the respective face. + # We're looking at both faces from the same side of the interface, so that "other side" + # (in the illustrations on the left) has right-handed coordinates. + if !flipped + if orientation == 0 + # Corner 0 of other side matches corner 0 of my side + # 2┌──────┐3 2┌──────┐3 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 0└──────┘1 + # η η + # ↑ ↑ + # │ │ + # └───> ξ └───> ξ + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[3] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[4] + 1 + + elseif ((lower && orientation == 2) # Corner 0 of my side matches corner 2 of other side + || (!lower && orientation == 1)) # Corner 0 of other side matches corner 1 of my side + # 2┌──────┐3 0┌──────┐2 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 1└──────┘3 + # η ┌───> η + # ↑ │ + # │ ↓ + # └───> ξ ξ + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[4] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[3] + 1 + + elseif ((lower && orientation == 1) # Corner 0 of my side matches corner 1 of other side + || (!lower && orientation == 2)) # Corner 0 of other side matches corner 2 of my side + # 2┌──────┐3 3┌──────┐1 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 2└──────┘0 + # η ξ + # ↑ ↑ + # │ │ + # └───> ξ η <───┘ + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[3] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[4] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[2] + 1 + + else # orientation == 3 + # Corner 0 of my side matches corner 3 of other side and + # corner 0 of other side matches corner 3 of my side. + # 2┌──────┐3 1┌──────┐0 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 3└──────┘2 + # η ξ <───┐ + # ↑ │ + # │ ↓ + # └───> ξ η + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[4] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[3] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[1] + 1 + + end + else # flipped + if orientation == 0 + # Corner 0 of other side matches corner 0 of my side + # 2┌──────┐3 1┌──────┐3 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 0└──────┘2 + # η ξ + # ↑ ↑ + # │ │ + # └───> ξ └───> η + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[3] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[4] + 1 + + elseif orientation == 2 + # Corner 0 of my side matches corner 2 of other side and + # corner 0 of other side matches corner 2 of my side. + # 2┌──────┐3 0┌──────┐1 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 2└──────┘3 + # η ┌───> ξ + # ↑ │ + # │ ↓ + # └───> ξ η + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[3] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[4] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[2] + 1 + + elseif orientation == 1 + # Corner 0 of my side matches corner 1 of other side and + # corner 0 of other side matches corner 1 of my side. + # 2┌──────┐3 3┌──────┐2 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 1└──────┘0 + # η η + # ↑ ↑ + # │ │ + # └───> ξ ξ <───┘ + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[4] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[3] + 1 + + else # orientation == 3 + # Corner 0 of my side matches corner 3 of other side and + # corner 0 of other side matches corner 3 of my side. + # 2┌──────┐3 2┌──────┐0 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 3└──────┘1 + # η η <───┐ + # ↑ │ + # │ ↓ + # └───> ξ ξ + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[4] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[3] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[1] + 1 + + end + end + +end + function trixi_t8_get_local_element_levels(forest) # Check that forest is a committed, that is valid and usable, forest. @assert t8_forest_is_committed(forest) != 0 diff --git a/src/solvers/dgsem_p4est/containers_3d.jl b/src/solvers/dgsem_p4est/containers_3d.jl index f1243647dd..3d3e25c28b 100644 --- a/src/solvers/dgsem_p4est/containers_3d.jl +++ b/src/solvers/dgsem_p4est/containers_3d.jl @@ -313,5 +313,4 @@ function orientation_to_indices_p4est(my_face, other_face, orientation_code) return surface_index1, surface_index2 end - end # @muladd diff --git a/test/test_t8code_3d.jl b/test/test_t8code_3d.jl index b68707f2de..ce88d07197 100644 --- a/test/test_t8code_3d.jl +++ b/test/test_t8code_3d.jl @@ -51,8 +51,8 @@ mkdir(outdir) @trixi_testset "elixir_advection_amr_unstructured_curved.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_amr_unstructured_curved.jl"), - l2 = [1.6236411810065552e-5], - linf = [0.0010554006923731395], + l2 = [2.0556575425846923e-5], + linf = [0.00105682693484822], tspan = (0.0, 1.0), coverage_override = (maxiters=6, initial_refinement_level=0, base_level=0, med_level=1, max_level=2)) end @@ -63,6 +63,13 @@ mkdir(outdir) # linf = [0.027655117058380085]) # end + @trixi_testset "elixir_euler_source_terms_nonconforming_unstructured_curved.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_source_terms_nonconforming_unstructured_curved.jl"), + l2 = [4.070355207909268e-5, 4.4993257426833716e-5, 5.10588457841744e-5, 5.102840924036687e-5, 0.00019986264001630542], + linf = [0.0016987332417202072, 0.003622956808262634, 0.002029576258317789, 0.0024206977281964193, 0.008526972236273522], + tspan = (0.0, 0.01)) + end + end # Clean up afterwards: delete Trixi.jl output directory From 8df37d1911afd6ecf8fb7871e403b119327c3c6e Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Mon, 19 Jun 2023 19:17:28 +0200 Subject: [PATCH 047/128] Fixed merging issues. --- src/auxiliary/t8code.jl | 171 ---------------------- src/callbacks_step/amr_dg3d.jl | 3 - src/callbacks_step/analysis_dg3d.jl | 25 ---- src/callbacks_step/stepsize_dg3d.jl | 5 - src/meshes/mesh_io.jl | 1 - src/solvers/dgsem_p4est/containers.jl | 39 ----- src/solvers/dgsem_structured/dg_3d.jl | 19 --- src/solvers/dgsem_t8code/containers_2d.jl | 10 ++ src/solvers/dgsem_t8code/containers_3d.jl | 161 ++++++++++++++++++++ 9 files changed, 171 insertions(+), 263 deletions(-) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index bf59ea379f..67ac0ca32d 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -241,177 +241,6 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari boundaries = local_num_boundary) end -function trixi_t8_init_mortar_neighbor_ids!(mortars::P4estMortarContainer{2}, my_face, other_face, orientation, neighbor_ielements, mortar_id) - if orientation == 0 - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[1] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[2] + 1 - else - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 - end -end - -# This routine was copied and adapated from `src/dgsem_p4est/containers_3d.jl`: `orientation_to_indices_p4est`. -function trixi_t8_init_mortar_neighbor_ids!(mortars::P4estMortarContainer{3}, my_face, other_face, orientation, neighbor_ielements, mortar_id) - # my_face and other_face are the face directions (zero-based) - # of "my side" and "other side" respectively. - # Face corner 0 of the face with the lower face direction connects to a corner of the other face. - # The number of this corner is the orientation code in `p4est`. - lower = my_face <= other_face - - # x_pos, y_neg, and z_pos are the directions in which the face has right-handed coordinates - # when looked at from the outside. - my_right_handed = my_face in (1, 2, 5) - other_right_handed = other_face in (1, 2, 5) - - # If both or none are right-handed when looked at from the outside, they will have different - # orientations when looked at from the same side of the interface. - flipped = my_right_handed == other_right_handed - - # In the following illustrations, the face corner numbering of `p4est` is shown. - # ξ and η are the local coordinates of the respective face. - # We're looking at both faces from the same side of the interface, so that "other side" - # (in the illustrations on the left) has right-handed coordinates. - if !flipped - if orientation == 0 - # Corner 0 of other side matches corner 0 of my side - # 2┌──────┐3 2┌──────┐3 - # │ │ │ │ - # │ │ │ │ - # 0└──────┘1 0└──────┘1 - # η η - # ↑ ↑ - # │ │ - # └───> ξ └───> ξ - - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[1] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[2] + 1 - mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[3] + 1 - mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[4] + 1 - - elseif ((lower && orientation == 2) # Corner 0 of my side matches corner 2 of other side - || (!lower && orientation == 1)) # Corner 0 of other side matches corner 1 of my side - # 2┌──────┐3 0┌──────┐2 - # │ │ │ │ - # │ │ │ │ - # 0└──────┘1 1└──────┘3 - # η ┌───> η - # ↑ │ - # │ ↓ - # └───> ξ ξ - - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[4] + 1 - mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[1] + 1 - mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[3] + 1 - - elseif ((lower && orientation == 1) # Corner 0 of my side matches corner 1 of other side - || (!lower && orientation == 2)) # Corner 0 of other side matches corner 2 of my side - # 2┌──────┐3 3┌──────┐1 - # │ │ │ │ - # │ │ │ │ - # 0└──────┘1 2└──────┘0 - # η ξ - # ↑ ↑ - # │ │ - # └───> ξ η <───┘ - - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[3] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 - mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[4] + 1 - mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[2] + 1 - - else # orientation == 3 - # Corner 0 of my side matches corner 3 of other side and - # corner 0 of other side matches corner 3 of my side. - # 2┌──────┐3 1┌──────┐0 - # │ │ │ │ - # │ │ │ │ - # 0└──────┘1 3└──────┘2 - # η ξ <───┐ - # ↑ │ - # │ ↓ - # └───> ξ η - - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[4] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[3] + 1 - mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[2] + 1 - mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[1] + 1 - - end - else # flipped - if orientation == 0 - # Corner 0 of other side matches corner 0 of my side - # 2┌──────┐3 1┌──────┐3 - # │ │ │ │ - # │ │ │ │ - # 0└──────┘1 0└──────┘2 - # η ξ - # ↑ ↑ - # │ │ - # └───> ξ └───> η - - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[1] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[3] + 1 - mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[2] + 1 - mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[4] + 1 - - elseif orientation == 2 - # Corner 0 of my side matches corner 2 of other side and - # corner 0 of other side matches corner 2 of my side. - # 2┌──────┐3 0┌──────┐1 - # │ │ │ │ - # │ │ │ │ - # 0└──────┘1 2└──────┘3 - # η ┌───> ξ - # ↑ │ - # │ ↓ - # └───> ξ η - - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[3] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[4] + 1 - mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[1] + 1 - mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[2] + 1 - - elseif orientation == 1 - # Corner 0 of my side matches corner 1 of other side and - # corner 0 of other side matches corner 1 of my side. - # 2┌──────┐3 3┌──────┐2 - # │ │ │ │ - # │ │ │ │ - # 0└──────┘1 1└──────┘0 - # η η - # ↑ ↑ - # │ │ - # └───> ξ ξ <───┘ - - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 - mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[4] + 1 - mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[3] + 1 - - else # orientation == 3 - # Corner 0 of my side matches corner 3 of other side and - # corner 0 of other side matches corner 3 of my side. - # 2┌──────┐3 2┌──────┐0 - # │ │ │ │ - # │ │ │ │ - # 0└──────┘1 3└──────┘1 - # η η <───┐ - # ↑ │ - # │ ↓ - # └───> ξ ξ - - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[4] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[2] + 1 - mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[3] + 1 - mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[1] + 1 - - end - end - -end - function trixi_t8_get_local_element_levels(forest) # Check that forest is a committed, that is valid and usable, forest. @assert t8_forest_is_committed(forest) != 0 diff --git a/src/callbacks_step/amr_dg3d.jl b/src/callbacks_step/amr_dg3d.jl index 4547aaa4b3..a06fc0483d 100644 --- a/src/callbacks_step/amr_dg3d.jl +++ b/src/callbacks_step/amr_dg3d.jl @@ -309,7 +309,6 @@ function create_cache(::Type{ControllerThreeLevel}, controller_value = Vector{Int}(undef, nelements(dg, cache)) return (; controller_value) end -<<<<<<< HEAD # Coarsen and refine elements in the DG solver based on a difference list. function adapt!(u_ode::AbstractVector, adaptor, mesh::T8codeMesh{3}, equations, dg::DGSEM, cache, difference) @@ -383,6 +382,4 @@ function adapt!(u_ode::AbstractVector, adaptor, mesh::T8codeMesh{3}, equations, return nothing end -======= ->>>>>>> feature-t8code end # @muladd diff --git a/src/callbacks_step/analysis_dg3d.jl b/src/callbacks_step/analysis_dg3d.jl index d0def94e54..534136ca75 100644 --- a/src/callbacks_step/analysis_dg3d.jl +++ b/src/callbacks_step/analysis_dg3d.jl @@ -35,12 +35,7 @@ function create_cache_analysis(analyzer, mesh::TreeMesh{3}, return (; u_local, u_tmp1, u_tmp2, x_local, x_tmp1, x_tmp2) end -<<<<<<< HEAD - function create_cache_analysis(analyzer, mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, -======= -function create_cache_analysis(analyzer, mesh::Union{StructuredMesh{3}, P4estMesh{3}}, ->>>>>>> feature-t8code equations, dg::DG, cache, RealT, uEltype) @@ -223,22 +218,12 @@ function integrate_via_indices(func::Func, u, end function integrate(func::Func, u, -<<<<<<< HEAD mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, equations, dg::DG, cache; normalize=true) where {Func} integrate_via_indices(u, mesh, equations, dg, cache; normalize=normalize) do u, i, j, k, element, equations, dg u_local = get_node_vars(u, equations, dg, i, j, k, element) return func(u_local, equations) end -======= - mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}}, - equations, dg::DG, cache; normalize = true) where {Func} - integrate_via_indices(u, mesh, equations, dg, cache; - normalize = normalize) do u, i, j, k, element, equations, dg - u_local = get_node_vars(u, equations, dg, i, j, k, element) - return func(u_local, equations) - end ->>>>>>> feature-t8code end function integrate(func::Func, u, @@ -291,12 +276,7 @@ function analyze(::Val{:l2_divb}, du, u, t, end function analyze(::Val{:l2_divb}, du, u, t, -<<<<<<< HEAD mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, equations::IdealGlmMhdEquations3D, -======= - mesh::Union{StructuredMesh{3}, P4estMesh{3}}, - equations::IdealGlmMhdEquations3D, ->>>>>>> feature-t8code dg::DGSEM, cache) @unpack contravariant_vectors = cache.elements integrate_via_indices(u, mesh, equations, dg, cache, cache, @@ -351,12 +331,7 @@ function analyze(::Val{:linf_divb}, du, u, t, end function analyze(::Val{:linf_divb}, du, u, t, -<<<<<<< HEAD mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, equations::IdealGlmMhdEquations3D, -======= - mesh::Union{StructuredMesh{3}, P4estMesh{3}}, - equations::IdealGlmMhdEquations3D, ->>>>>>> feature-t8code dg::DGSEM, cache) @unpack derivative_matrix, weights = dg.basis @unpack contravariant_vectors = cache.elements diff --git a/src/callbacks_step/stepsize_dg3d.jl b/src/callbacks_step/stepsize_dg3d.jl index 7b748afd88..822ab2f87e 100644 --- a/src/callbacks_step/stepsize_dg3d.jl +++ b/src/callbacks_step/stepsize_dg3d.jl @@ -82,12 +82,7 @@ function max_dt(u, t, mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3} return 2 / (nnodes(dg) * max_scaled_speed) end -<<<<<<< HEAD - function max_dt(u, t, mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, -======= -function max_dt(u, t, mesh::Union{StructuredMesh{3}, P4estMesh{3}}, ->>>>>>> feature-t8code constant_speed::True, equations, dg::DG, cache) # to avoid a division by zero if the speed vanishes everywhere, # e.g. for steady-state linear advection diff --git a/src/meshes/mesh_io.jl b/src/meshes/mesh_io.jl index be27e832ce..042d35e704 100644 --- a/src/meshes/mesh_io.jl +++ b/src/meshes/mesh_io.jl @@ -8,7 +8,6 @@ # Save current mesh with some context information as an HDF5 file. function save_mesh_file(mesh::Union{TreeMesh, P4estMesh, T8codeMesh}, output_directory, timestep=0) save_mesh_file(mesh, output_directory, timestep, mpi_parallel(mesh)) ->>>>>>> main end function save_mesh_file(mesh::TreeMesh, output_directory, timestep, diff --git a/src/solvers/dgsem_p4est/containers.jl b/src/solvers/dgsem_p4est/containers.jl index ddbf453f7c..cdebb75c3f 100644 --- a/src/solvers/dgsem_p4est/containers.jl +++ b/src/solvers/dgsem_p4est/containers.jl @@ -164,15 +164,9 @@ function Base.resize!(interfaces::P4estInterfaceContainer, capacity) end # Create interface container and initialize interface data. -<<<<<<< HEAD function init_interfaces(mesh::Union{P4estMesh, T8codeMesh}, equations, basis, elements) NDIMS = ndims(elements) uEltype = eltype(elements) -======= -function init_interfaces(mesh::P4estMesh, equations, basis, elements) - NDIMS = ndims(elements) - uEltype = eltype(elements) ->>>>>>> main # Initialize container n_interfaces = count_required_surfaces(mesh).interfaces @@ -245,15 +239,9 @@ function Base.resize!(boundaries::P4estBoundaryContainer, capacity) end # Create interface container and initialize interface data in `elements`. -<<<<<<< HEAD function init_boundaries(mesh::Union{P4estMesh, T8codeMesh}, equations, basis, elements) NDIMS = ndims(elements) uEltype = eltype(elements) -======= -function init_boundaries(mesh::P4estMesh, equations, basis, elements) - NDIMS = ndims(elements) - uEltype = eltype(elements) ->>>>>>> main # Initialize container n_boundaries = count_required_surfaces(mesh).boundaries @@ -382,33 +370,7 @@ function Base.resize!(mortars::P4estMortarContainer, capacity) end # Create mortar container and initialize mortar data. -<<<<<<< HEAD function init_mortars(mesh::Union{P4estMesh, T8codeMesh}, equations, basis, elements) - NDIMS = ndims(elements) - uEltype = eltype(elements) - - # Initialize container - n_mortars = count_required_surfaces(mesh).mortars - - _u = Vector{uEltype}(undef, - 2 * nvariables(equations) * 2^(NDIMS-1) * nnodes(basis)^(NDIMS-1) * n_mortars) - u = unsafe_wrap(Array, pointer(_u), - (2, nvariables(equations), 2^(NDIMS-1), ntuple(_ -> nnodes(basis), NDIMS-1)..., n_mortars)) - - _neighbor_ids = Vector{Int}(undef, (2^(NDIMS-1) + 1) * n_mortars) - neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids), (2^(NDIMS-1) + 1, n_mortars)) - - _node_indices = Vector{NTuple{NDIMS, Symbol}}(undef, 2 * n_mortars) - node_indices = unsafe_wrap(Array, pointer(_node_indices), (2, n_mortars)) - - mortars = P4estMortarContainer{NDIMS, uEltype, NDIMS+1, NDIMS+3}(u, neighbor_ids, node_indices, - _u, _neighbor_ids, _node_indices) - - if n_mortars > 0 - init_mortars!(mortars, mesh) - end -======= -function init_mortars(mesh::P4estMesh, equations, basis, elements) NDIMS = ndims(elements) uEltype = eltype(elements) @@ -439,7 +401,6 @@ function init_mortars(mesh::P4estMesh, equations, basis, elements) if n_mortars > 0 init_mortars!(mortars, mesh) end ->>>>>>> main return mortars end diff --git a/src/solvers/dgsem_structured/dg_3d.jl b/src/solvers/dgsem_structured/dg_3d.jl index 59c5d5daea..8c4714f1fb 100644 --- a/src/solvers/dgsem_structured/dg_3d.jl +++ b/src/solvers/dgsem_structured/dg_3d.jl @@ -180,12 +180,7 @@ end end @inline function flux_differencing_kernel!(du, u, -<<<<<<< HEAD element, mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, -======= - element, - mesh::Union{StructuredMesh{3}, P4estMesh{3}}, ->>>>>>> feature-t8code nonconservative_terms::True, equations, volume_flux, dg::DGSEM, cache, alpha = true) @unpack derivative_split = dg.basis @@ -268,15 +263,8 @@ end # by Hennemann, Rueda-Ramirez, Hindenlang, Gassner (2020) # "A provably entropy stable subcell shock capturing approach for high order split form DG for the compressible Euler equations" # [arXiv: 2008.12044v2](https://arxiv.org/pdf/2008.12044) -<<<<<<< HEAD @inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, fstar3_L, fstar3_R, u, mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, nonconservative_terms::False, -======= -@inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, fstar3_L, - fstar3_R, u, - mesh::Union{StructuredMesh{3}, P4estMesh{3}}, - nonconservative_terms::False, ->>>>>>> feature-t8code equations, volume_flux_fv, dg::DGSEM, element, cache) @unpack contravariant_vectors = cache.elements @unpack weights, derivative_matrix = dg.basis @@ -368,15 +356,8 @@ end end # # Calculate the finite volume fluxes inside curvilinear elements (**with non-conservative terms**). -<<<<<<< HEAD @inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, fstar3_L, fstar3_R, u, mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, nonconservative_terms::True, -======= -@inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, fstar3_L, - fstar3_R, u, - mesh::Union{StructuredMesh{3}, P4estMesh{3}}, - nonconservative_terms::True, ->>>>>>> feature-t8code equations, volume_flux_fv, dg::DGSEM, element, cache) @unpack contravariant_vectors = cache.elements @unpack weights, derivative_matrix = dg.basis diff --git a/src/solvers/dgsem_t8code/containers_2d.jl b/src/solvers/dgsem_t8code/containers_2d.jl index aeb0d2fac4..305c8c4f27 100644 --- a/src/solvers/dgsem_t8code/containers_2d.jl +++ b/src/solvers/dgsem_t8code/containers_2d.jl @@ -50,4 +50,14 @@ function calc_node_coordinates!(node_coordinates, return node_coordinates end +function trixi_t8_init_mortar_neighbor_ids!(mortars::P4estMortarContainer{2}, my_face, other_face, orientation, neighbor_ielements, mortar_id) + if orientation == 0 + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[2] + 1 + else + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 + end +end + end # @muladd diff --git a/src/solvers/dgsem_t8code/containers_3d.jl b/src/solvers/dgsem_t8code/containers_3d.jl index 503509fd58..25368972f1 100644 --- a/src/solvers/dgsem_t8code/containers_3d.jl +++ b/src/solvers/dgsem_t8code/containers_3d.jl @@ -57,4 +57,165 @@ function calc_node_coordinates!(node_coordinates, return node_coordinates end +# This routine was copied and adapated from `src/dgsem_p4est/containers_3d.jl`: `orientation_to_indices_p4est`. +function trixi_t8_init_mortar_neighbor_ids!(mortars::P4estMortarContainer{3}, my_face, other_face, orientation, neighbor_ielements, mortar_id) + # my_face and other_face are the face directions (zero-based) + # of "my side" and "other side" respectively. + # Face corner 0 of the face with the lower face direction connects to a corner of the other face. + # The number of this corner is the orientation code in `p4est`. + lower = my_face <= other_face + + # x_pos, y_neg, and z_pos are the directions in which the face has right-handed coordinates + # when looked at from the outside. + my_right_handed = my_face in (1, 2, 5) + other_right_handed = other_face in (1, 2, 5) + + # If both or none are right-handed when looked at from the outside, they will have different + # orientations when looked at from the same side of the interface. + flipped = my_right_handed == other_right_handed + + # In the following illustrations, the face corner numbering of `p4est` is shown. + # ξ and η are the local coordinates of the respective face. + # We're looking at both faces from the same side of the interface, so that "other side" + # (in the illustrations on the left) has right-handed coordinates. + if !flipped + if orientation == 0 + # Corner 0 of other side matches corner 0 of my side + # 2┌──────┐3 2┌──────┐3 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 0└──────┘1 + # η η + # ↑ ↑ + # │ │ + # └───> ξ └───> ξ + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[3] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[4] + 1 + + elseif ((lower && orientation == 2) # Corner 0 of my side matches corner 2 of other side + || (!lower && orientation == 1)) # Corner 0 of other side matches corner 1 of my side + # 2┌──────┐3 0┌──────┐2 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 1└──────┘3 + # η ┌───> η + # ↑ │ + # │ ↓ + # └───> ξ ξ + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[4] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[3] + 1 + + elseif ((lower && orientation == 1) # Corner 0 of my side matches corner 1 of other side + || (!lower && orientation == 2)) # Corner 0 of other side matches corner 2 of my side + # 2┌──────┐3 3┌──────┐1 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 2└──────┘0 + # η ξ + # ↑ ↑ + # │ │ + # └───> ξ η <───┘ + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[3] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[4] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[2] + 1 + + else # orientation == 3 + # Corner 0 of my side matches corner 3 of other side and + # corner 0 of other side matches corner 3 of my side. + # 2┌──────┐3 1┌──────┐0 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 3└──────┘2 + # η ξ <───┐ + # ↑ │ + # │ ↓ + # └───> ξ η + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[4] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[3] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[1] + 1 + + end + else # flipped + if orientation == 0 + # Corner 0 of other side matches corner 0 of my side + # 2┌──────┐3 1┌──────┐3 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 0└──────┘2 + # η ξ + # ↑ ↑ + # │ │ + # └───> ξ └───> η + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[3] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[4] + 1 + + elseif orientation == 2 + # Corner 0 of my side matches corner 2 of other side and + # corner 0 of other side matches corner 2 of my side. + # 2┌──────┐3 0┌──────┐1 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 2└──────┘3 + # η ┌───> ξ + # ↑ │ + # │ ↓ + # └───> ξ η + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[3] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[4] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[2] + 1 + + elseif orientation == 1 + # Corner 0 of my side matches corner 1 of other side and + # corner 0 of other side matches corner 1 of my side. + # 2┌──────┐3 3┌──────┐2 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 1└──────┘0 + # η η + # ↑ ↑ + # │ │ + # └───> ξ ξ <───┘ + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[4] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[3] + 1 + + else # orientation == 3 + # Corner 0 of my side matches corner 3 of other side and + # corner 0 of other side matches corner 3 of my side. + # 2┌──────┐3 2┌──────┐0 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 3└──────┘1 + # η η <───┐ + # ↑ │ + # │ ↓ + # └───> ξ ξ + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[4] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[3] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[1] + 1 + + end + end + +end + end # @muladd From 6d67075b3d7492975b99a2e94d1d0ee889526406 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 20 Jun 2023 09:00:22 +0200 Subject: [PATCH 048/128] Adding more tests. --- .../elixir_euler_free_stream.jl | 149 ++++++++++++++++++ .../elixir_euler_source_terms_nonperiodic.jl | 71 +++++++++ test/test_t8code_3d.jl | 21 +++ 3 files changed, 241 insertions(+) create mode 100644 examples/t8code_3d_dgsem/elixir_euler_free_stream.jl create mode 100644 examples/t8code_3d_dgsem/elixir_euler_source_terms_nonperiodic.jl diff --git a/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl b/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl new file mode 100644 index 0000000000..96385d1ce8 --- /dev/null +++ b/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl @@ -0,0 +1,149 @@ + +using Downloads: download +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations + +equations = CompressibleEulerEquations3D(1.4) + +initial_condition = initial_condition_constant + +boundary_conditions = Dict( + :all => BoundaryConditionDirichlet(initial_condition) +) + +# Solver with polydeg=4 to ensure free stream preservation (FSP) on non-conforming meshes. +# The polydeg of the solver must be at least twice as big as the polydeg of the mesh. +# See https://doi.org/10.1007/s10915-018-00897-9, Section 6. +solver = DGSEM(polydeg=4, surface_flux=flux_lax_friedrichs, + volume_integral=VolumeIntegralWeakForm()) + +# Mapping as described in https://arxiv.org/abs/2012.12040 but with less warping. +# The mapping will be interpolated at tree level, and then refined without changing +# the geometry interpolant. This can yield problematic geometries if the unrefined mesh +# is not fine enough. +function mapping(xi_, eta_, zeta_) + # Transform input variables between -1 and 1 onto [0,3] + xi = 1.5 * xi_ + 1.5 + eta = 1.5 * eta_ + 1.5 + zeta = 1.5 * zeta_ + 1.5 + + y = eta + 1/6 * (cos(1.5 * pi * (2 * xi - 3)/3) * + cos(0.5 * pi * (2 * eta - 3)/3) * + cos(0.5 * pi * (2 * zeta - 3)/3)) + + x = xi + 1/6 * (cos(0.5 * pi * (2 * xi - 3)/3) * + cos(2 * pi * (2 * y - 3)/3) * + cos(0.5 * pi * (2 * zeta - 3)/3)) + + z = zeta + 1/6 * (cos(0.5 * pi * (2 * x - 3)/3) * + cos(pi * (2 * y - 3)/3) * + cos(0.5 * pi * (2 * zeta - 3)/3)) + + return SVector(x, y, z) +end + +# Unstructured mesh with 68 cells of the cube domain [-1, 1]^3 +mesh_file = joinpath(@__DIR__, "cube_unstructured_1.inp") +isfile(mesh_file) || download("https://gist.githubusercontent.com/efaulhaber/d45c8ac1e248618885fa7cc31a50ab40/raw/37fba24890ab37cfa49c39eae98b44faf4502882/cube_unstructured_1.inp", + mesh_file) + +# INP mesh files are only support by p4est. Hence, we +# create a p4est connecvity object first from which +# we can create a t8code mesh. +conn = Trixi.read_inp_p4est(mesh_file,Val(3)) + +mesh = T8codeMesh{3}(conn, polydeg=2, + mapping=mapping, + initial_refinement_level=0) + +# Note: This is actually a `p8est_quadrant_t` which is much bigger than the +# following struct. But we only need the first four fields for our purpose. +struct t8_dhex_t + x :: Int32 + y :: Int32 + z :: Int32 + level :: Int8 + # [...] # See `p8est.h` in `p4est` for more info. +end + +# Refine bottom left quadrant of each second tree to level 2 +function adapt_callback(forest, + forest_from, + which_tree, + lelement_id, + ts, + is_family, + num_elements, + elements_ptr) :: Cint + + vertex = Vector{Cdouble}(undef,3) + elements = unsafe_wrap(Array, elements_ptr, num_elements) + Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) + + el = unsafe_load(Ptr{t8_dhex_t}(elements[1])) + + if iseven(convert(Int, which_tree)) && el.x == 0 && el.y == 0 && el.z == 0 && el.level < 2 + # return true (refine) + return 1 + else + # return false (don't refine) + return 0 + end +end + +@assert(Trixi.t8_forest_is_committed(mesh.forest) != 0); + +# Init new forest. +new_forest_ref = Ref{Trixi.t8_forest_t}() +Trixi.t8_forest_init(new_forest_ref); +new_forest = new_forest_ref[] + +let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0, do_ghost = 1 + Trixi.t8_forest_set_user_data(new_forest, C_NULL) + Trixi.t8_forest_set_adapt(new_forest, mesh.forest, @Trixi.t8_adapt_callback(adapt_callback), recursive) + Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) + Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) + Trixi.t8_forest_set_ghost(new_forest, do_ghost, Trixi.T8_GHOST_FACES); + Trixi.t8_forest_commit(new_forest) +end + +mesh.forest = new_forest + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, boundary_conditions=boundary_conditions) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 1.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval=analysis_interval) + +alive_callback = AliveCallback(analysis_interval=analysis_interval) + +# save_solution = SaveSolutionCallback(interval=100, +# save_initial_solution=true, +# save_final_solution=true, +# solution_variables=cons2prim) + +stepsize_callback = StepsizeCallback(cfl=1.2) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + # save_solution, + stepsize_callback) + + +############################################################################### +# run the simulation + +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep=false, callback=callbacks); +summary_callback() # print the timer summary diff --git a/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonperiodic.jl b/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonperiodic.jl new file mode 100644 index 0000000000..b503da3c13 --- /dev/null +++ b/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonperiodic.jl @@ -0,0 +1,71 @@ +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations + +equations = CompressibleEulerEquations3D(1.4) + +initial_condition = initial_condition_convergence_test + +boundary_condition = BoundaryConditionDirichlet(initial_condition) +boundary_conditions = Dict( + :x_neg => boundary_condition, + :x_pos => boundary_condition, + :y_neg => boundary_condition, + :y_pos => boundary_condition, + :z_neg => boundary_condition, + :z_pos => boundary_condition +) + +solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs, + volume_integral=VolumeIntegralWeakForm()) + +coordinates_min = (0.0, 0.0, 0.0) +coordinates_max = (2.0, 2.0, 2.0) + +trees_per_dimension = (2, 2, 2) + +mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) + +mesh = T8codeMesh(trees_per_dimension, polydeg=1, + mapping=mapping, + periodicity=false, initial_refinement_level=1) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, + source_terms=source_terms_convergence_test, + boundary_conditions=boundary_conditions) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 5.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval=analysis_interval) + +alive_callback = AliveCallback(analysis_interval=analysis_interval) + +# save_solution = SaveSolutionCallback(interval=100, +# save_initial_solution=true, +# save_final_solution=true, +# solution_variables=cons2prim) + +stepsize_callback = StepsizeCallback(cfl=0.6) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + # save_solution, + stepsize_callback) + + +############################################################################### +# run the simulation + +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep=false, callback=callbacks); +summary_callback() # print the timer summary diff --git a/test/test_t8code_3d.jl b/test/test_t8code_3d.jl index ce88d07197..33c430dfc9 100644 --- a/test/test_t8code_3d.jl +++ b/test/test_t8code_3d.jl @@ -70,6 +70,27 @@ mkdir(outdir) tspan = (0.0, 0.01)) end + @trixi_testset "elixir_euler_source_terms_nonperiodic.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_source_terms_nonperiodic.jl"), + l2 = [0.0015106060984283647, 0.0014733349038567685, 0.00147333490385685, 0.001473334903856929, 0.0028149479453087093], + linf = [0.008070806335238156, 0.009007245083113125, 0.009007245083121784, 0.009007245083102688, 0.01562861968368434], + tspan = (0.0, 1.0)) + end + + @trixi_testset "elixir_euler_free_stream.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_free_stream.jl"), + l2 = [5.162664597942288e-15, 1.941857343642486e-14, 2.0232366394187278e-14, 2.3381518645408552e-14, 7.083114561232324e-14], + linf = [7.269740365245525e-13, 3.289868377720495e-12, 4.440087186807773e-12, 3.8686831516088205e-12, 9.412914891981927e-12], + tspan = (0.0, 0.03)) + end + + @trixi_testset "elixir_euler_free_stream_extruded.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_free_stream_extruded.jl"), + l2 = [8.444868392439035e-16, 4.889826056731442e-15, 2.2921260987087585e-15, 4.268460455702414e-15, 1.1356712092620279e-14], + linf = [7.749356711883593e-14, 2.8792246364872653e-13, 1.1121659149182506e-13, 3.3228975127030935e-13, 9.592326932761353e-13], + tspan=(0.0, 0.1)) + end + end # Clean up afterwards: delete Trixi.jl output directory From c9f431db5fdf104e042d27b73755ae73bb6fa47d Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 20 Jun 2023 09:42:08 +0200 Subject: [PATCH 049/128] Fixed some merging issues and formatting. --- src/callbacks_step/amr_dg2d.jl | 100 +++++++++++++------------- src/callbacks_step/analysis_dg2d.jl | 56 +++------------ src/meshes/mesh_io.jl | 10 +-- src/solvers/dgsem_p4est/containers.jl | 45 ++---------- 4 files changed, 66 insertions(+), 145 deletions(-) diff --git a/src/callbacks_step/amr_dg2d.jl b/src/callbacks_step/amr_dg2d.jl index 737251e557..c3cf499198 100644 --- a/src/callbacks_step/amr_dg2d.jl +++ b/src/callbacks_step/amr_dg2d.jl @@ -335,86 +335,84 @@ end # this method is called when an `ControllerThreeLevel` is constructed function create_cache(::Type{ControllerThreeLevel}, - mesh::Union{TreeMesh{2}, P4estMesh{2}, T8codeMesh{2}}, equations, dg::DG, cache) + mesh::Union{TreeMesh{2}, P4estMesh{2}, T8codeMesh{2}}, equations, + dg::DG, cache) controller_value = Vector{Int}(undef, nelements(dg, cache)) return (; controller_value) end -<<<<<<< HEAD # Coarsen and refine elements in the DG solver based on a difference list. -function adapt!(u_ode::AbstractVector, adaptor, mesh::T8codeMesh{2}, equations, dg::DGSEM, cache, difference) +function adapt!(u_ode::AbstractVector, adaptor, mesh::T8codeMesh{2}, equations, + dg::DGSEM, cache, difference) - # Return early if there is nothing to do. - if !any(difference .!= 0) - return nothing - end - - # Number of (local) cells/elements. - old_nelems = nelements(dg, cache) - new_nelems = ncells(mesh) - - # Local element indices. - old_index = 1 - new_index = 1 - - # Note: This is true for `quads` only. - T8_CHILDREN = 4 + # Return early if there is nothing to do. + if !any(difference .!= 0) + return nothing + end - # Retain current solution data. - old_u_ode = copy(u_ode) + # Number of (local) cells/elements. + old_nelems = nelements(dg, cache) + new_nelems = ncells(mesh) - GC.@preserve old_u_ode begin - old_u = wrap_array(old_u_ode, mesh, equations, dg, cache) + # Local element indices. + old_index = 1 + new_index = 1 - reinitialize_containers!(mesh, equations, dg, cache) + # Note: This is true for `quads` only. + T8_CHILDREN = 4 - resize!(u_ode, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) - u = wrap_array(u_ode, mesh, equations, dg, cache) + # Retain current solution data. + old_u_ode = copy(u_ode) - while old_index <= old_nelems && new_index <= new_nelems + GC.@preserve old_u_ode begin + old_u = wrap_array(old_u_ode, mesh, equations, dg, cache) - if difference[old_index] > 0 # Refine. + reinitialize_containers!(mesh, equations, dg, cache) - # Refine element and store solution directly in new data structure. - refine_element!(u, new_index, old_u, old_index, adaptor, equations, dg) + resize!(u_ode, + nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) + u = wrap_array(u_ode, mesh, equations, dg, cache) - old_index += 1 - new_index += T8_CHILDREN + while old_index <= old_nelems && new_index <= new_nelems + if difference[old_index] > 0 # Refine. - elseif difference[old_index] < 0 # Coarsen. + # Refine element and store solution directly in new data structure. + refine_element!(u, new_index, old_u, old_index, adaptor, equations, dg) - # If an element is to be removed, sanity check if the following elements - # are also marked - otherwise there would be an error in the way the - # cells/elements are sorted. - @assert all(difference[old_index:(old_index+T8_CHILDREN-1)] .< 0) "bad cell/element order" + old_index += 1 + new_index += T8_CHILDREN - # Coarsen elements and store solution directly in new data structure. - coarsen_elements!(u, new_index, old_u, old_index, adaptor, equations, dg) + elseif difference[old_index] < 0 # Coarsen. - old_index += T8_CHILDREN - new_index += 1 + # If an element is to be removed, sanity check if the following elements + # are also marked - otherwise there would be an error in the way the + # cells/elements are sorted. + @assert all(difference[old_index:(old_index + T8_CHILDREN - 1)] .< 0) "bad cell/element order" - else # No changes. + # Coarsen elements and store solution directly in new data structure. + coarsen_elements!(u, new_index, old_u, old_index, adaptor, equations, + dg) - # Copy old element data to new element container. - @views u[:, .., new_index] .= old_u[:, .., old_index] + old_index += T8_CHILDREN + new_index += 1 - old_index += 1 - new_index += 1 - end + else # No changes. - end # while + # Copy old element data to new element container. + @views u[:, .., new_index] .= old_u[:, .., old_index] - end # GC.@preserve old_u_ode + old_index += 1 + new_index += 1 + end + end # while + end # GC.@preserve old_u_ode - return nothing + return nothing end -======= function create_cache(::Type{ControllerThreeLevelCombined}, mesh::TreeMesh{2}, equations, dg::DG, cache) controller_value = Vector{Int}(undef, nelements(dg, cache)) return (; controller_value) end ->>>>>>> main end # @muladd diff --git a/src/callbacks_step/analysis_dg2d.jl b/src/callbacks_step/analysis_dg2d.jl index 1def6f8d09..4e456f7987 100644 --- a/src/callbacks_step/analysis_dg2d.jl +++ b/src/callbacks_step/analysis_dg2d.jl @@ -107,12 +107,8 @@ function calc_error_norms(func, u, t, analyzer, end function calc_error_norms(func, u, t, analyzer, -<<<<<<< HEAD - mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, equations, -======= mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, - P4estMesh{2}}, equations, ->>>>>>> main + P4estMesh{2}, T8codeMesh{2}}, equations, initial_condition, dg::DGSEM, cache, cache_analysis) @unpack vandermonde, weights = analyzer @unpack node_coordinates, inverse_jacobian = cache.elements @@ -179,24 +175,8 @@ function integrate_via_indices(func::Func, u, end function integrate_via_indices(func::Func, u, -<<<<<<< HEAD - mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, equations, - dg::DGSEM, cache, args...; normalize=true) where {Func} - @unpack weights = dg.basis - - # Initialize integral with zeros of the right shape - integral = zero(func(u, 1, 1, 1, equations, dg, args...)) - total_volume = zero(real(mesh)) - - # Use quadrature to numerically integrate over entire domain - for element in eachelement(dg, cache) - for j in eachnode(dg), i in eachnode(dg) - volume_jacobian = abs(inv(cache.elements.inverse_jacobian[i, j, element])) - integral += volume_jacobian * weights[i] * weights[j] * func(u, i, j, element, equations, dg, args...) - total_volume += volume_jacobian * weights[i] * weights[j] -======= mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, - P4estMesh{2}}, equations, + P4estMesh{2}, T8codeMesh{2}}, equations, dg::DGSEM, cache, args...; normalize = true) where {Func} @unpack weights = dg.basis @@ -212,7 +192,6 @@ function integrate_via_indices(func::Func, u, func(u, i, j, element, equations, dg, args...) total_volume += volume_jacobian * weights[i] * weights[j] end ->>>>>>> main end # Normalize with total volume @@ -224,32 +203,19 @@ function integrate_via_indices(func::Func, u, end function integrate(func::Func, u, -<<<<<<< HEAD - mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, - equations, dg::DG, cache; normalize=true) where {Func} - integrate_via_indices(u, mesh, equations, dg, cache; normalize=normalize) do u, i, j, element, equations, dg - u_local = get_node_vars(u, equations, dg, i, j, element) - return func(u_local, equations) - end -======= mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, - P4estMesh{2}}, + P4estMesh{2}, T8codeMesh{2}}, equations, dg::DG, cache; normalize = true) where {Func} integrate_via_indices(u, mesh, equations, dg, cache; normalize = normalize) do u, i, j, element, equations, dg u_local = get_node_vars(u, equations, dg, i, j, element) return func(u_local, equations) end ->>>>>>> main end function analyze(::typeof(entropy_timederivative), du, u, t, -<<<<<<< HEAD - mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, -======= mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, - P4estMesh{2}}, ->>>>>>> main + P4estMesh{2}, T8codeMesh{2}}, equations, dg::DG, cache) # Calculate ∫(∂S/∂u ⋅ ∂u/∂t)dΩ integrate_via_indices(u, mesh, equations, dg, cache, @@ -293,11 +259,8 @@ function analyze(::Val{:l2_divb}, du, u, t, end function analyze(::Val{:l2_divb}, du, u, t, -<<<<<<< HEAD - mesh::Union{StructuredMesh{2},UnstructuredMesh2D,P4estMesh{2}, T8codeMesh{2}}, -======= - mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, ->>>>>>> main + mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, + T8codeMesh{2}}, equations::IdealGlmMhdEquations2D, dg::DGSEM, cache) @unpack contravariant_vectors = cache.elements integrate_via_indices(u, mesh, equations, dg, cache, cache, @@ -364,11 +327,8 @@ function analyze(::Val{:linf_divb}, du, u, t, end function analyze(::Val{:linf_divb}, du, u, t, -<<<<<<< HEAD - mesh::Union{StructuredMesh{2},UnstructuredMesh2D,P4estMesh{2}, T8codeMesh{2}}, -======= - mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, ->>>>>>> main + mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, + T8codeMesh{2}}, equations::IdealGlmMhdEquations2D, dg::DGSEM, cache) @unpack derivative_matrix, weights = dg.basis @unpack contravariant_vectors = cache.elements diff --git a/src/meshes/mesh_io.jl b/src/meshes/mesh_io.jl index be27e832ce..b479bda0c4 100644 --- a/src/meshes/mesh_io.jl +++ b/src/meshes/mesh_io.jl @@ -6,9 +6,9 @@ #! format: noindent # Save current mesh with some context information as an HDF5 file. -function save_mesh_file(mesh::Union{TreeMesh, P4estMesh, T8codeMesh}, output_directory, timestep=0) - save_mesh_file(mesh, output_directory, timestep, mpi_parallel(mesh)) ->>>>>>> main +function save_mesh_file(mesh::Union{TreeMesh, P4estMesh, T8codeMesh}, output_directory, + timestep = 0) + save_mesh_file(mesh, output_directory, timestep, mpi_parallel(mesh)) end function save_mesh_file(mesh::TreeMesh, output_directory, timestep, @@ -222,9 +222,9 @@ end # TODO: Implement this function as soon as there is support for this in `t8code`. function save_mesh_file(mesh::T8codeMesh, output_directory, timestep, mpi_parallel) - @warn "Mesh file output not supported yet for `T8codeMesh`." + @warn "Mesh file output not supported yet for `T8codeMesh`." - return joinpath(output_directory, "dummy_mesh.h5") + return joinpath(output_directory, "dummy_mesh.h5") end """ diff --git a/src/solvers/dgsem_p4est/containers.jl b/src/solvers/dgsem_p4est/containers.jl index ddbf453f7c..0176f5c634 100644 --- a/src/solvers/dgsem_p4est/containers.jl +++ b/src/solvers/dgsem_p4est/containers.jl @@ -81,8 +81,10 @@ function Base.resize!(elements::P4estElementContainer, capacity) end # Create element container and initialize element data -function init_elements(mesh::Union{P4estMesh{NDIMS, RealT}, T8codeMesh{NDIMS, RealT}}, equations, - basis, ::Type{uEltype}) where {NDIMS, RealT<:Real, uEltype<:Real} +function init_elements(mesh::Union{P4estMesh{NDIMS, RealT}, T8codeMesh{NDIMS, RealT}}, + equations, + basis, + ::Type{uEltype}) where {NDIMS, RealT <: Real, uEltype <: Real} nelements = ncells(mesh) _node_coordinates = Vector{RealT}(undef, NDIMS * nnodes(basis)^NDIMS * nelements) @@ -164,15 +166,9 @@ function Base.resize!(interfaces::P4estInterfaceContainer, capacity) end # Create interface container and initialize interface data. -<<<<<<< HEAD function init_interfaces(mesh::Union{P4estMesh, T8codeMesh}, equations, basis, elements) - NDIMS = ndims(elements) - uEltype = eltype(elements) -======= -function init_interfaces(mesh::P4estMesh, equations, basis, elements) NDIMS = ndims(elements) uEltype = eltype(elements) ->>>>>>> main # Initialize container n_interfaces = count_required_surfaces(mesh).interfaces @@ -245,15 +241,9 @@ function Base.resize!(boundaries::P4estBoundaryContainer, capacity) end # Create interface container and initialize interface data in `elements`. -<<<<<<< HEAD function init_boundaries(mesh::Union{P4estMesh, T8codeMesh}, equations, basis, elements) - NDIMS = ndims(elements) - uEltype = eltype(elements) -======= -function init_boundaries(mesh::P4estMesh, equations, basis, elements) NDIMS = ndims(elements) uEltype = eltype(elements) ->>>>>>> main # Initialize container n_boundaries = count_required_surfaces(mesh).boundaries @@ -382,33 +372,7 @@ function Base.resize!(mortars::P4estMortarContainer, capacity) end # Create mortar container and initialize mortar data. -<<<<<<< HEAD function init_mortars(mesh::Union{P4estMesh, T8codeMesh}, equations, basis, elements) - NDIMS = ndims(elements) - uEltype = eltype(elements) - - # Initialize container - n_mortars = count_required_surfaces(mesh).mortars - - _u = Vector{uEltype}(undef, - 2 * nvariables(equations) * 2^(NDIMS-1) * nnodes(basis)^(NDIMS-1) * n_mortars) - u = unsafe_wrap(Array, pointer(_u), - (2, nvariables(equations), 2^(NDIMS-1), ntuple(_ -> nnodes(basis), NDIMS-1)..., n_mortars)) - - _neighbor_ids = Vector{Int}(undef, (2^(NDIMS-1) + 1) * n_mortars) - neighbor_ids = unsafe_wrap(Array, pointer(_neighbor_ids), (2^(NDIMS-1) + 1, n_mortars)) - - _node_indices = Vector{NTuple{NDIMS, Symbol}}(undef, 2 * n_mortars) - node_indices = unsafe_wrap(Array, pointer(_node_indices), (2, n_mortars)) - - mortars = P4estMortarContainer{NDIMS, uEltype, NDIMS+1, NDIMS+3}(u, neighbor_ids, node_indices, - _u, _neighbor_ids, _node_indices) - - if n_mortars > 0 - init_mortars!(mortars, mesh) - end -======= -function init_mortars(mesh::P4estMesh, equations, basis, elements) NDIMS = ndims(elements) uEltype = eltype(elements) @@ -439,7 +403,6 @@ function init_mortars(mesh::P4estMesh, equations, basis, elements) if n_mortars > 0 init_mortars!(mortars, mesh) end ->>>>>>> main return mortars end From ced89df7bcaaf5d8959c214789949bf4668956f2 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 20 Jun 2023 09:52:46 +0200 Subject: [PATCH 050/128] Fixed spelling. --- ...e_terms_nonconforming_unstructured_flag.jl | 88 +++++++++---------- 1 file changed, 43 insertions(+), 45 deletions(-) diff --git a/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl b/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl index 8d92fee6d5..2f1baaa144 100644 --- a/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl @@ -13,18 +13,16 @@ source_terms = source_terms_convergence_test # BCs must be passed as Dict boundary_condition = BoundaryConditionDirichlet(initial_condition) -boundary_conditions = Dict( - :all => boundary_condition -) +boundary_conditions = Dict(:all => boundary_condition) -solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) +solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs) # Deformed rectangle that looks like a waving flag, # lower and upper faces are sinus curves, left and right are vertical lines. f1(s) = SVector(-1.0, s - 1.0) -f2(s) = SVector( 1.0, s + 1.0) +f2(s) = SVector(1.0, s + 1.0) f3(s) = SVector(s, -1.0 + sin(0.5 * pi * s)) -f4(s) = SVector(s, 1.0 + sin(0.5 * pi * s)) +f4(s) = SVector(s, 1.0 + sin(0.5 * pi * s)) faces = (f1, f2, f3, f4) Trixi.validate_faces(faces) @@ -33,46 +31,46 @@ mapping_flag = Trixi.transfinite_mapping(faces) # Get the uncurved mesh from a file (downloads the file if not available locally) # Unstructured mesh with 24 cells of the square domain [-1, 1]^n mesh_file = joinpath(@__DIR__, "square_unstructured_2.inp") -isfile(mesh_file) || download("https://gist.githubusercontent.com/efaulhaber/63ff2ea224409e55ee8423b3a33e316a/raw/7db58af7446d1479753ae718930741c47a3b79b7/square_unstructured_2.inp", - mesh_file) +isfile(mesh_file) || + download("https://gist.githubusercontent.com/efaulhaber/63ff2ea224409e55ee8423b3a33e316a/raw/7db58af7446d1479753ae718930741c47a3b79b7/square_unstructured_2.inp", + mesh_file) # INP mesh files are only support by p4est. Hence, we # create a p4est connecvity object first from which # we can create a t8code mesh. -conn = Trixi.read_inp_p4est(mesh_file,Val(2)) +conn = Trixi.read_inp_p4est(mesh_file, Val(2)) -mesh = T8codeMesh{2}(conn, polydeg=3, - mapping=mapping_flag, - initial_refinement_level=1) +mesh = T8codeMesh{2}(conn, polydeg = 3, + mapping = mapping_flag, + initial_refinement_level = 1) function adapt_callback(forest, forest_from, which_tree, lelement_id, ts, - is_family, + is_family, num_elements, - elements_ptr) :: Cint + elements_ptr)::Cint + vertex = Vector{Cdouble}(undef, 3) - vertex = Vector{Cdouble}(undef,3) + elements = unsafe_wrap(Array, elements_ptr, num_elements) - elements = unsafe_wrap(Array, elements_ptr, num_elements) + Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) - Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) + level = Trixi.t8_element_level(ts, elements[1]) - level = Trixi.t8_element_level(ts,elements[1]) - - # TODO: Make this condition more general. - if vertex[1] < 1e-8 && vertex[2] < 1e-8 && level < 2 - # return true (refine) - return 1 - else - # return false (don't refine) - return 0 - end + # TODO: Make this condition more general. + if vertex[1] < 1e-8 && vertex[2] < 1e-8 && level < 2 + # return true (refine) + return 1 + else + # return false (don't refine) + return 0 + end end -@Trixi.T8_ASSERT(Trixi.t8_forest_is_committed(mesh.forest) != 0); +Trixi.@T8_ASSERT(Trixi.t8_forest_is_committed(mesh.forest)!=0); # Init new forest. new_forest_ref = Ref{Trixi.t8_forest_t}() @@ -80,19 +78,20 @@ Trixi.t8_forest_init(new_forest_ref); new_forest = new_forest_ref[] let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0 - Trixi.t8_forest_set_user_data(new_forest, C_NULL) - Trixi.t8_forest_set_adapt(new_forest, mesh.forest, @Trixi.t8_adapt_callback(adapt_callback), recursive) - Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) - Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - Trixi.t8_forest_set_ghost(new_forest, 1, Trixi.T8_GHOST_FACES); - Trixi.t8_forest_commit(new_forest) + Trixi.t8_forest_set_user_data(new_forest, C_NULL) + Trixi.t8_forest_set_adapt(new_forest, mesh.forest, + Trixi.@t8_adapt_callback(adapt_callback), recursive) + Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) + Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) + Trixi.t8_forest_set_ghost(new_forest, 1, Trixi.T8_GHOST_FACES) + Trixi.t8_forest_commit(new_forest) end mesh.forest = new_forest semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, - source_terms=source_terms, - boundary_conditions=boundary_conditions) + source_terms = source_terms, + boundary_conditions = boundary_conditions) ############################################################################### # ODE solvers, callbacks etc. @@ -103,11 +102,11 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() analysis_interval = 100 -analysis_callback = AnalysisCallback(semi, interval=analysis_interval) +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) -alive_callback = AliveCallback(analysis_interval=analysis_interval) +alive_callback = AliveCallback(analysis_interval = analysis_interval) -# Not implemted yet. +# Not implemeted yet. # save_restart = SaveRestartCallback(interval=100, # save_final_restart=true) # @@ -117,17 +116,16 @@ alive_callback = AliveCallback(analysis_interval=analysis_interval) # save_final_solution=true, # solution_variables=cons2prim) -stepsize_callback = StepsizeCallback(cfl=0.8) +stepsize_callback = StepsizeCallback(cfl = 0.8) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, # save_restart, save_solution, - stepsize_callback, -) + stepsize_callback) ############################################################################### # run the simulation -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep = false, callback = callbacks); summary_callback() # print the timer summary From 32b6711c1bd58993f2b3d54c94c62ab70a3b8175 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 20 Jun 2023 09:56:06 +0200 Subject: [PATCH 051/128] Fixed spelling and changed assert macro. --- ...ixir_euler_source_terms_nonconforming_unstructured_flag.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl b/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl index 2f1baaa144..d6d37a1cdc 100644 --- a/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl @@ -70,7 +70,7 @@ function adapt_callback(forest, end end -Trixi.@T8_ASSERT(Trixi.t8_forest_is_committed(mesh.forest)!=0); +@assert(Trixi.t8_forest_is_committed(mesh.forest)!=0); # Init new forest. new_forest_ref = Ref{Trixi.t8_forest_t}() @@ -106,7 +106,7 @@ analysis_callback = AnalysisCallback(semi, interval = analysis_interval) alive_callback = AliveCallback(analysis_interval = analysis_interval) -# Not implemeted yet. +# Not implemented yet. # save_restart = SaveRestartCallback(interval=100, # save_final_restart=true) # From c450680c904829459cefcbb97fcbfb39e371f9a4 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 20 Jun 2023 10:07:47 +0200 Subject: [PATCH 052/128] Applied automatic formatting. --- src/Trixi.jl | 4 +- src/auxiliary/t8code.jl | 695 +++++++++++----------- src/callbacks_step/amr.jl | 85 ++- src/callbacks_step/analysis.jl | 34 +- src/callbacks_step/save_restart_dg.jl | 3 +- src/callbacks_step/save_solution_dg.jl | 3 +- src/callbacks_step/stepsize_dg2d.jl | 8 +- src/meshes/t8code_mesh.jl | 355 +++++------ src/solvers/dg.jl | 4 +- src/solvers/dgsem_p4est/containers_2d.jl | 7 +- src/solvers/dgsem_p4est/dg_2d.jl | 6 +- src/solvers/dgsem_structured/dg_2d.jl | 22 +- src/solvers/dgsem_t8code/containers.jl | 59 +- src/solvers/dgsem_t8code/containers_2d.jl | 102 ++-- src/solvers/dgsem_t8code/dg.jl | 40 +- src/solvers/dgsem_tree/dg_2d.jl | 37 +- src/solvers/dgsem_tree/indicators_2d.jl | 5 +- src/solvers/dgsem_unstructured/dg_2d.jl | 10 +- 18 files changed, 767 insertions(+), 712 deletions(-) diff --git a/src/Trixi.jl b/src/Trixi.jl index 270aecb870..e38d27508e 100644 --- a/src/Trixi.jl +++ b/src/Trixi.jl @@ -271,8 +271,8 @@ export PlotData1D, PlotData2D, ScalarPlotData2D, getmesh, adapt_to_mesh_level!, function __init__() init_mpi() - init_p4est() - init_t8code() + init_p4est() + init_t8code() register_error_hints() diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index d3a44d72c1..4c6d069ef0 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -7,331 +7,343 @@ is already initialized and if yes, do nothing, thus it is safe to call it multiple times. """ function init_t8code() - t8code_package_id = t8_get_package_id() - if t8code_package_id >= 0 - return nothing - end + t8code_package_id = t8_get_package_id() + if t8code_package_id >= 0 + return nothing + end - # Initialize the sc library, has to happen before we initialize t8code. - T8code.Libt8.sc_init(mpi_comm(), 1, 1, C_NULL, T8code.Libt8.SC_LP_ERROR) + # Initialize the sc library, has to happen before we initialize t8code. + T8code.Libt8.sc_init(mpi_comm(), 1, 1, C_NULL, T8code.Libt8.SC_LP_ERROR) - # Initialize `p4est` with log level ERROR to prevent a lot of output in AMR simulations - T8code.Libt8.p4est_init(C_NULL, SC_LP_ERROR) - # Initialize t8code with log level ERROR to prevent a lot of output in AMR simulations. - t8_init(T8code.Libt8.SC_LP_ERROR) + # Initialize `p4est` with log level ERROR to prevent a lot of output in AMR simulations + T8code.Libt8.p4est_init(C_NULL, SC_LP_ERROR) + # Initialize t8code with log level ERROR to prevent a lot of output in AMR simulations. + t8_init(T8code.Libt8.SC_LP_ERROR) - return nothing + return nothing end function trixi_t8_unref_forest(forest) - t8_forest_unref(Ref(forest)) + t8_forest_unref(Ref(forest)) end function t8_free(ptr) - T8code.Libt8.sc_free(t8_get_package_id(), ptr) + T8code.Libt8.sc_free(t8_get_package_id(), ptr) end function trixi_t8_count_interfaces(forest) - # Check that forest is a committed, that is valid and usable, forest. - @assert t8_forest_is_committed(forest) != 0 + # Check that forest is a committed, that is valid and usable, forest. + @assert t8_forest_is_committed(forest) != 0 - # Get the number of local elements of forest. - num_local_elements = t8_forest_get_local_num_elements(forest) - # Get the number of ghost elements of forest. - num_ghost_elements = t8_forest_get_num_ghosts(forest) - # Get the number of trees that have elements of this process. - num_local_trees = t8_forest_get_num_local_trees(forest) + # Get the number of local elements of forest. + num_local_elements = t8_forest_get_local_num_elements(forest) + # Get the number of ghost elements of forest. + num_ghost_elements = t8_forest_get_num_ghosts(forest) + # Get the number of trees that have elements of this process. + num_local_trees = t8_forest_get_num_local_trees(forest) - current_index = t8_locidx_t(0) + current_index = t8_locidx_t(0) - local_num_conform = 0 - local_num_mortars = 0 - local_num_boundary = 0 + local_num_conform = 0 + local_num_mortars = 0 + local_num_boundary = 0 - for itree = 0:num_local_trees-1 - tree_class = t8_forest_get_tree_class(forest, itree) - eclass_scheme = t8_forest_get_eclass_scheme(forest, tree_class) + for itree in 0:(num_local_trees - 1) + tree_class = t8_forest_get_tree_class(forest, itree) + eclass_scheme = t8_forest_get_eclass_scheme(forest, tree_class) - # Get the number of elements of this tree. - num_elements_in_tree = t8_forest_get_tree_num_elements(forest, itree) + # Get the number of elements of this tree. + num_elements_in_tree = t8_forest_get_tree_num_elements(forest, itree) - for ielement = 0:num_elements_in_tree-1 - element = t8_forest_get_element_in_tree(forest, itree, ielement) + for ielement in 0:(num_elements_in_tree - 1) + element = t8_forest_get_element_in_tree(forest, itree, ielement) - level = t8_element_level(eclass_scheme, element) + level = t8_element_level(eclass_scheme, element) - num_faces = t8_element_num_faces(eclass_scheme, element) + num_faces = t8_element_num_faces(eclass_scheme, element) - for iface = 0:num_faces-1 - pelement_indices_ref = Ref{Ptr{t8_locidx_t}}() - pneighbor_leafs_ref = Ref{Ptr{Ptr{t8_element}}}() - pneigh_scheme_ref = Ref{Ptr{t8_eclass_scheme}}() + for iface in 0:(num_faces - 1) + pelement_indices_ref = Ref{Ptr{t8_locidx_t}}() + pneighbor_leafs_ref = Ref{Ptr{Ptr{t8_element}}}() + pneigh_scheme_ref = Ref{Ptr{t8_eclass_scheme}}() - dual_faces_ref = Ref{Ptr{Cint}}() - num_neighbors_ref = Ref{Cint}() + dual_faces_ref = Ref{Ptr{Cint}}() + num_neighbors_ref = Ref{Cint}() - forest_is_balanced = Cint(1) + forest_is_balanced = Cint(1) - t8_forest_leaf_face_neighbors(forest, itree, element, - pneighbor_leafs_ref, iface, dual_faces_ref, num_neighbors_ref, - pelement_indices_ref, pneigh_scheme_ref, forest_is_balanced) + t8_forest_leaf_face_neighbors(forest, itree, element, + pneighbor_leafs_ref, iface, dual_faces_ref, + num_neighbors_ref, + pelement_indices_ref, pneigh_scheme_ref, + forest_is_balanced) - num_neighbors = num_neighbors_ref[] - neighbor_ielements = unsafe_wrap(Array, pelement_indices_ref[], num_neighbors) - neighbor_leafs = unsafe_wrap(Array, pneighbor_leafs_ref[], num_neighbors) - neighbor_scheme = pneigh_scheme_ref[] + num_neighbors = num_neighbors_ref[] + neighbor_ielements = unsafe_wrap(Array, pelement_indices_ref[], + num_neighbors) + neighbor_leafs = unsafe_wrap(Array, pneighbor_leafs_ref[], num_neighbors) + neighbor_scheme = pneigh_scheme_ref[] - if num_neighbors > 0 - neighbor_level = t8_element_level(neighbor_scheme, neighbor_leafs[1]) + if num_neighbors > 0 + neighbor_level = t8_element_level(neighbor_scheme, neighbor_leafs[1]) - # Conforming interface: The second condition ensures we only visit the interface once. - if level == neighbor_level && current_index <= neighbor_ielements[1] - local_num_conform += 1 - elseif level < neighbor_level - local_num_mortars += 1 - end + # Conforming interface: The second condition ensures we only visit the interface once. + if level == neighbor_level && current_index <= neighbor_ielements[1] + local_num_conform += 1 + elseif level < neighbor_level + local_num_mortars += 1 + end - else + else + local_num_boundary += 1 + end - local_num_boundary += 1 - end - - t8_free(dual_faces_ref[]) - t8_free(pneighbor_leafs_ref[]) - t8_free(pelement_indices_ref[]) + t8_free(dual_faces_ref[]) + t8_free(pneighbor_leafs_ref[]) + t8_free(pelement_indices_ref[]) + end # for - end # for - - current_index += 1 + current_index += 1 + end # for end # for - end # for - return (interfaces = local_num_conform, - mortars = local_num_mortars, - boundaries = local_num_boundary) + return (interfaces = local_num_conform, + mortars = local_num_mortars, + boundaries = local_num_boundary) end -function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundaries, boundary_names) - # Check that forest is a committed, that is valid and usable, forest. - @assert t8_forest_is_committed(forest) != 0 - - # Get the number of local elements of forest. - num_local_elements = t8_forest_get_local_num_elements(forest) - # Get the number of ghost elements of forest. - num_ghost_elements = t8_forest_get_num_ghosts(forest) - # Get the number of trees that have elements of this process. - num_local_trees = t8_forest_get_num_local_trees(forest) - - current_index = t8_locidx_t(0) - - local_num_conform = 0 - local_num_mortars = 0 - local_num_boundary = 0 - - for itree = 0:num_local_trees-1 - tree_class = t8_forest_get_tree_class(forest, itree) - eclass_scheme = t8_forest_get_eclass_scheme(forest, tree_class) - - # Get the number of elements of this tree. - num_elements_in_tree = t8_forest_get_tree_num_elements(forest, itree) - - for ielement = 0:num_elements_in_tree-1 - element = t8_forest_get_element_in_tree(forest, itree, ielement) - - level = t8_element_level(eclass_scheme, element) - - num_faces = t8_element_num_faces(eclass_scheme,element) - - for iface = 0:num_faces-1 - - # Compute the `orientation` of the touching faces. - if t8_element_is_root_boundary(eclass_scheme, element, iface) == 1 - cmesh = t8_forest_get_cmesh(forest) - itree_in_cmesh = t8_forest_ltreeid_to_cmesh_ltreeid(forest, itree) - iface_in_tree = t8_element_tree_face(eclass_scheme, element, iface) - orientation_ref = Ref{Cint}() - - t8_cmesh_get_face_neighbor(cmesh, itree_in_cmesh, iface_in_tree, C_NULL, orientation_ref) - orientation = orientation_ref[] - else - orientation = zero(Cint) - end - - pelement_indices_ref = Ref{Ptr{t8_locidx_t}}() - pneighbor_leafs_ref = Ref{Ptr{Ptr{t8_element}}}() - pneigh_scheme_ref = Ref{Ptr{t8_eclass_scheme}}() - - dual_faces_ref = Ref{Ptr{Cint}}() - num_neighbors_ref = Ref{Cint}() - - forest_is_balanced = Cint(1) - - t8_forest_leaf_face_neighbors(forest,itree,element, - pneighbor_leafs_ref, iface, dual_faces_ref, num_neighbors_ref, - pelement_indices_ref, pneigh_scheme_ref, forest_is_balanced) - - num_neighbors = num_neighbors_ref[] - dual_faces = unsafe_wrap(Array, dual_faces_ref[], num_neighbors) - neighbor_ielements = unsafe_wrap(Array, pelement_indices_ref[], num_neighbors) - neighbor_leafs = unsafe_wrap(Array, pneighbor_leafs_ref[], num_neighbors) - neighbor_scheme = pneigh_scheme_ref[] - - if num_neighbors > 0 - neighbor_level = t8_element_level(neighbor_scheme, neighbor_leafs[1]) - - # Conforming interface: The second condition ensures we only visit the interface once. - if level == neighbor_level && current_index <= neighbor_ielements[1] - local_num_conform += 1 - - faces = (iface, dual_faces[1]) - interface_id = local_num_conform - - # Write data to interfaces container. - interfaces.neighbor_ids[1, interface_id] = current_index + 1 - interfaces.neighbor_ids[2, interface_id] = neighbor_ielements[1] + 1 - - # Iterate over primary and secondary element. - for side = 1:2 - # Align interface in positive coordinate direction of primary element. - # For orientation == 1, the secondary element needs to be indexed backwards - # relative to the interface. - if side == 1 || orientation == 0 - # Forward indexing - indexing = :i_forward - else - # Backward indexing - indexing = :i_backward - end - - if faces[side] == 0 - # Index face in negative x-direction - interfaces.node_indices[side, interface_id] = (:begin, indexing) - elseif faces[side] == 1 - # Index face in positive x-direction - interfaces.node_indices[side, interface_id] = (:end, indexing) - elseif faces[side] == 2 - # Index face in negative y-direction - interfaces.node_indices[side, interface_id] = (indexing, :begin) - else # faces[side] == 3 - # Index face in positive y-direction - interfaces.node_indices[side, interface_id] = (indexing, :end) - end - end - - # Non-conforming interface. - elseif level < neighbor_level - local_num_mortars += 1 - - faces = (dual_faces[1],iface) - - mortar_id = local_num_mortars - - # Last entry is the large element. - mortars.neighbor_ids[end, mortar_id] = current_index + 1 - - # First `1:end-1` entries are the smaller elements. - mortars.neighbor_ids[1:end-1, mortar_id] .= neighbor_ielements .+ 1 - - for side = 1:2 - # Align mortar in positive coordinate direction of small side. - # For orientation == 1, the large side needs to be indexed backwards - # relative to the mortar. - if side == 1 || orientation == 0 - # Forward indexing for small side or orientation == 0. - indexing = :i_forward - else - # Backward indexing for large side with reversed orientation. - indexing = :i_backward - # Since the orientation is reversed we have to account for this - # when filling the `neighbor_ids` array. - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 - end - - if faces[side] == 0 - # Index face in negative x-direction - mortars.node_indices[side, mortar_id] = (:begin, indexing) - elseif faces[side] == 1 - # Index face in positive x-direction - mortars.node_indices[side, mortar_id] = (:end, indexing) - elseif faces[side] == 2 - # Index face in negative y-direction - mortars.node_indices[side, mortar_id] = (indexing, :begin) - else # faces[side] == 3 - # Index face in positive y-direction - mortars.node_indices[side, mortar_id] = (indexing, :end) - end - end - - # else: "level > neighbor_level" is skipped since we visit the mortar interface only once. - end - - # Domain boundary. - else - local_num_boundary += 1 - boundary_id = local_num_boundary - - boundaries.neighbor_ids[boundary_id] = current_index + 1 - - if iface == 0 - # Index face in negative x-direction. - boundaries.node_indices[boundary_id] = (:begin, :i_forward) - elseif iface == 1 - # Index face in positive x-direction. - boundaries.node_indices[boundary_id] = (:end, :i_forward) - elseif iface == 2 - # Index face in negative y-direction. - boundaries.node_indices[boundary_id] = (:i_forward, :begin) - else # iface == 3 - # Index face in positive y-direction. - boundaries.node_indices[boundary_id] = (:i_forward, :end) - end - - # One-based indexing. - boundaries.name[boundary_id] = boundary_names[iface + 1, itree + 1] - end - - t8_free(dual_faces_ref[]) - t8_free(pneighbor_leafs_ref[]) - t8_free(pelement_indices_ref[]) - - end # for iface = ... - - current_index += 1 +function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundaries, + boundary_names) + # Check that forest is a committed, that is valid and usable, forest. + @assert t8_forest_is_committed(forest) != 0 + + # Get the number of local elements of forest. + num_local_elements = t8_forest_get_local_num_elements(forest) + # Get the number of ghost elements of forest. + num_ghost_elements = t8_forest_get_num_ghosts(forest) + # Get the number of trees that have elements of this process. + num_local_trees = t8_forest_get_num_local_trees(forest) + + current_index = t8_locidx_t(0) + + local_num_conform = 0 + local_num_mortars = 0 + local_num_boundary = 0 + + for itree in 0:(num_local_trees - 1) + tree_class = t8_forest_get_tree_class(forest, itree) + eclass_scheme = t8_forest_get_eclass_scheme(forest, tree_class) + + # Get the number of elements of this tree. + num_elements_in_tree = t8_forest_get_tree_num_elements(forest, itree) + + for ielement in 0:(num_elements_in_tree - 1) + element = t8_forest_get_element_in_tree(forest, itree, ielement) + + level = t8_element_level(eclass_scheme, element) + + num_faces = t8_element_num_faces(eclass_scheme, element) + + for iface in 0:(num_faces - 1) + + # Compute the `orientation` of the touching faces. + if t8_element_is_root_boundary(eclass_scheme, element, iface) == 1 + cmesh = t8_forest_get_cmesh(forest) + itree_in_cmesh = t8_forest_ltreeid_to_cmesh_ltreeid(forest, itree) + iface_in_tree = t8_element_tree_face(eclass_scheme, element, iface) + orientation_ref = Ref{Cint}() + + t8_cmesh_get_face_neighbor(cmesh, itree_in_cmesh, iface_in_tree, C_NULL, + orientation_ref) + orientation = orientation_ref[] + else + orientation = zero(Cint) + end + + pelement_indices_ref = Ref{Ptr{t8_locidx_t}}() + pneighbor_leafs_ref = Ref{Ptr{Ptr{t8_element}}}() + pneigh_scheme_ref = Ref{Ptr{t8_eclass_scheme}}() + + dual_faces_ref = Ref{Ptr{Cint}}() + num_neighbors_ref = Ref{Cint}() + + forest_is_balanced = Cint(1) + + t8_forest_leaf_face_neighbors(forest, itree, element, + pneighbor_leafs_ref, iface, dual_faces_ref, + num_neighbors_ref, + pelement_indices_ref, pneigh_scheme_ref, + forest_is_balanced) + + num_neighbors = num_neighbors_ref[] + dual_faces = unsafe_wrap(Array, dual_faces_ref[], num_neighbors) + neighbor_ielements = unsafe_wrap(Array, pelement_indices_ref[], + num_neighbors) + neighbor_leafs = unsafe_wrap(Array, pneighbor_leafs_ref[], num_neighbors) + neighbor_scheme = pneigh_scheme_ref[] + + if num_neighbors > 0 + neighbor_level = t8_element_level(neighbor_scheme, neighbor_leafs[1]) + + # Conforming interface: The second condition ensures we only visit the interface once. + if level == neighbor_level && current_index <= neighbor_ielements[1] + local_num_conform += 1 + + faces = (iface, dual_faces[1]) + interface_id = local_num_conform + + # Write data to interfaces container. + interfaces.neighbor_ids[1, interface_id] = current_index + 1 + interfaces.neighbor_ids[2, interface_id] = neighbor_ielements[1] + 1 + + # Iterate over primary and secondary element. + for side in 1:2 + # Align interface in positive coordinate direction of primary element. + # For orientation == 1, the secondary element needs to be indexed backwards + # relative to the interface. + if side == 1 || orientation == 0 + # Forward indexing + indexing = :i_forward + else + # Backward indexing + indexing = :i_backward + end + + if faces[side] == 0 + # Index face in negative x-direction + interfaces.node_indices[side, interface_id] = (:begin, + indexing) + elseif faces[side] == 1 + # Index face in positive x-direction + interfaces.node_indices[side, interface_id] = (:end, + indexing) + elseif faces[side] == 2 + # Index face in negative y-direction + interfaces.node_indices[side, interface_id] = (indexing, + :begin) + else # faces[side] == 3 + # Index face in positive y-direction + interfaces.node_indices[side, interface_id] = (indexing, + :end) + end + end + + # Non-conforming interface. + elseif level < neighbor_level + local_num_mortars += 1 + + faces = (dual_faces[1], iface) + + mortar_id = local_num_mortars + + # Last entry is the large element. + mortars.neighbor_ids[end, mortar_id] = current_index + 1 + + # First `1:end-1` entries are the smaller elements. + mortars.neighbor_ids[1:(end - 1), mortar_id] .= neighbor_ielements .+ + 1 + + for side in 1:2 + # Align mortar in positive coordinate direction of small side. + # For orientation == 1, the large side needs to be indexed backwards + # relative to the mortar. + if side == 1 || orientation == 0 + # Forward indexing for small side or orientation == 0. + indexing = :i_forward + else + # Backward indexing for large side with reversed orientation. + indexing = :i_backward + # Since the orientation is reversed we have to account for this + # when filling the `neighbor_ids` array. + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + + 1 + end + + if faces[side] == 0 + # Index face in negative x-direction + mortars.node_indices[side, mortar_id] = (:begin, indexing) + elseif faces[side] == 1 + # Index face in positive x-direction + mortars.node_indices[side, mortar_id] = (:end, indexing) + elseif faces[side] == 2 + # Index face in negative y-direction + mortars.node_indices[side, mortar_id] = (indexing, :begin) + else # faces[side] == 3 + # Index face in positive y-direction + mortars.node_indices[side, mortar_id] = (indexing, :end) + end + end + + # else: "level > neighbor_level" is skipped since we visit the mortar interface only once. + end + + # Domain boundary. + else + local_num_boundary += 1 + boundary_id = local_num_boundary + + boundaries.neighbor_ids[boundary_id] = current_index + 1 + + if iface == 0 + # Index face in negative x-direction. + boundaries.node_indices[boundary_id] = (:begin, :i_forward) + elseif iface == 1 + # Index face in positive x-direction. + boundaries.node_indices[boundary_id] = (:end, :i_forward) + elseif iface == 2 + # Index face in negative y-direction. + boundaries.node_indices[boundary_id] = (:i_forward, :begin) + else # iface == 3 + # Index face in positive y-direction. + boundaries.node_indices[boundary_id] = (:i_forward, :end) + end + + # One-based indexing. + boundaries.name[boundary_id] = boundary_names[iface + 1, itree + 1] + end + + t8_free(dual_faces_ref[]) + t8_free(pneighbor_leafs_ref[]) + t8_free(pelement_indices_ref[]) + end # for iface = ... + + current_index += 1 + end # for end # for - end # for - return (interfaces = local_num_conform, - mortars = local_num_mortars, - boundaries = local_num_boundary) + return (interfaces = local_num_conform, + mortars = local_num_mortars, + boundaries = local_num_boundary) end function trixi_t8_get_local_element_levels(forest) - # Check that forest is a committed, that is valid and usable, forest. - @assert t8_forest_is_committed(forest) != 0 + # Check that forest is a committed, that is valid and usable, forest. + @assert t8_forest_is_committed(forest) != 0 - levels = Vector{Int}(undef, t8_forest_get_local_num_elements(forest)) + levels = Vector{Int}(undef, t8_forest_get_local_num_elements(forest)) - # Get the number of trees that have elements of this process. - num_local_trees = t8_forest_get_num_local_trees(forest) + # Get the number of trees that have elements of this process. + num_local_trees = t8_forest_get_num_local_trees(forest) - current_index = 0 + current_index = 0 - for itree = 0:num_local_trees-1 - tree_class = t8_forest_get_tree_class(forest, itree) - eclass_scheme = t8_forest_get_eclass_scheme(forest, tree_class) + for itree in 0:(num_local_trees - 1) + tree_class = t8_forest_get_tree_class(forest, itree) + eclass_scheme = t8_forest_get_eclass_scheme(forest, tree_class) - # Get the number of elements of this tree. - num_elements_in_tree = t8_forest_get_tree_num_elements(forest, itree) + # Get the number of elements of this tree. + num_elements_in_tree = t8_forest_get_tree_num_elements(forest, itree) - for ielement = 0:num_elements_in_tree-1 - element = t8_forest_get_element_in_tree(forest, itree, ielement) - current_index += 1 - levels[current_index] = t8_element_level(eclass_scheme, element) + for ielement in 0:(num_elements_in_tree - 1) + element = t8_forest_get_element_in_tree(forest, itree, ielement) + current_index += 1 + levels[current_index] = t8_element_level(eclass_scheme, element) + end # for end # for - end # for - return levels + return levels end # Callback function prototype to decide for refining and coarsening. @@ -358,103 +370,100 @@ function adapt_callback(forest, which_tree, lelement_id, ts, - is_family, + is_family, num_elements, - elements) :: Cint - - num_levels = t8_forest_get_local_num_elements(forest_from) + elements)::Cint + num_levels = t8_forest_get_local_num_elements(forest_from) - indicator_ptr = Ptr{Int}(t8_forest_get_user_data(forest)) - indicators = unsafe_wrap(Array,indicator_ptr,num_levels) + indicator_ptr = Ptr{Int}(t8_forest_get_user_data(forest)) + indicators = unsafe_wrap(Array, indicator_ptr, num_levels) - offset = t8_forest_get_tree_element_offset(forest_from, which_tree) + offset = t8_forest_get_tree_element_offset(forest_from, which_tree) - # Only allow coarsening for complete families. - if indicators[offset + lelement_id + 1] < 0 && is_family == 0 - return Cint(0) - end + # Only allow coarsening for complete families. + if indicators[offset + lelement_id + 1] < 0 && is_family == 0 + return Cint(0) + end - return Cint(indicators[offset + lelement_id + 1]) + return Cint(indicators[offset + lelement_id + 1]) end function trixi_t8_adapt_new(old_forest, indicators) - # Check that forest is a committed, that is valid and usable, forest. - @assert t8_forest_is_committed(old_forest) != 0 - - # Init new forest. - new_forest_ref = Ref{t8_forest_t}() - t8_forest_init(new_forest_ref) - new_forest = new_forest_ref[] - - let set_from = C_NULL, recursive = 0, set_for_coarsening = 0, no_repartition = 0 - t8_forest_set_user_data(new_forest, pointer(indicators)) - t8_forest_set_adapt(new_forest, old_forest, @t8_adapt_callback(adapt_callback), recursive) - t8_forest_set_balance(new_forest, set_from, no_repartition) - t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - t8_forest_set_ghost(new_forest, 1, T8_GHOST_FACES) # Note: MPI support not available yet so it is a dummy call. - t8_forest_commit(new_forest) - end - - return new_forest + # Check that forest is a committed, that is valid and usable, forest. + @assert t8_forest_is_committed(old_forest) != 0 + + # Init new forest. + new_forest_ref = Ref{t8_forest_t}() + t8_forest_init(new_forest_ref) + new_forest = new_forest_ref[] + + let set_from = C_NULL, recursive = 0, set_for_coarsening = 0, no_repartition = 0 + t8_forest_set_user_data(new_forest, pointer(indicators)) + t8_forest_set_adapt(new_forest, old_forest, @t8_adapt_callback(adapt_callback), + recursive) + t8_forest_set_balance(new_forest, set_from, no_repartition) + t8_forest_set_partition(new_forest, set_from, set_for_coarsening) + t8_forest_set_ghost(new_forest, 1, T8_GHOST_FACES) # Note: MPI support not available yet so it is a dummy call. + t8_forest_commit(new_forest) + end + + return new_forest end function trixi_t8_get_difference(old_levels, new_levels, num_children) + old_nelems = length(old_levels) + new_nelems = length(new_levels) - old_nelems = length(old_levels) - new_nelems = length(new_levels) + changes = Vector{Int}(undef, old_nelems) - changes = Vector{Int}(undef, old_nelems) + # Local element indices. + old_index = 1 + new_index = 1 - # Local element indices. - old_index = 1 - new_index = 1 + while old_index <= old_nelems && new_index <= new_nelems + if old_levels[old_index] < new_levels[new_index] + # Refined. - while old_index <= old_nelems && new_index <= new_nelems + changes[old_index] = 1 - if old_levels[old_index] < new_levels[new_index] - # Refined. - - changes[old_index] = 1 + old_index += 1 + new_index += num_children - old_index += 1 - new_index += num_children + elseif old_levels[old_index] > new_levels[new_index] + # Coarsend. - elseif old_levels[old_index] > new_levels[new_index] - # Coarsend. + for child_index in old_index:(old_index + num_children - 1) + changes[child_index] = -1 + end - for child_index = old_index:old_index+num_children-1 - changes[child_index] = -1 - end + old_index += num_children + new_index += 1 - old_index += num_children - new_index += 1 + else + # No changes. - else - # No changes. - - changes[old_index] = 0 + changes[old_index] = 0 - old_index += 1 - new_index += 1 + old_index += 1 + new_index += 1 + end end - end - return changes + return changes end # Coarsen or refine marked cells and rebalance forest. Return a difference between # old and new mesh. function trixi_t8_adapt!(mesh, indicators) + old_levels = trixi_t8_get_local_element_levels(mesh.forest) - old_levels = trixi_t8_get_local_element_levels(mesh.forest) - - forest_cached = trixi_t8_adapt_new(mesh.forest, indicators) + forest_cached = trixi_t8_adapt_new(mesh.forest, indicators) - new_levels = trixi_t8_get_local_element_levels(forest_cached) + new_levels = trixi_t8_get_local_element_levels(forest_cached) - differences = trixi_t8_get_difference(old_levels, new_levels, 2^ndims(mesh)) + differences = trixi_t8_get_difference(old_levels, new_levels, 2^ndims(mesh)) - mesh.forest = forest_cached + mesh.forest = forest_cached - return differences + return differences end diff --git a/src/callbacks_step/amr.jl b/src/callbacks_step/amr.jl index 8a85b279ba..5820beceb3 100644 --- a/src/callbacks_step/amr.jl +++ b/src/callbacks_step/amr.jl @@ -474,61 +474,60 @@ end function (amr_callback::AMRCallback)(u_ode::AbstractVector, mesh::T8codeMesh, equations, dg::DG, cache, semi, t, iter; - only_refine=false, only_coarsen=false, - passive_args=()) - - has_changed = false - - @unpack controller, adaptor = amr_callback + only_refine = false, only_coarsen = false, + passive_args = ()) + has_changed = false - u = wrap_array(u_ode, mesh, equations, dg, cache) - indicators = @trixi_timeit timer() "indicator" controller(u, mesh, equations, dg, cache, t=t, iter=iter) + @unpack controller, adaptor = amr_callback - if only_coarsen - indicators[indicators .> 0] .= 0 - end + u = wrap_array(u_ode, mesh, equations, dg, cache) + indicators = @trixi_timeit timer() "indicator" controller(u, mesh, equations, dg, + cache, t = t, iter = iter) - if only_refine - indicators[indicators .< 0] .= 0 - end + if only_coarsen + indicators[indicators .> 0] .= 0 + end - @boundscheck begin - @assert axes(indicators) == (Base.OneTo(ncells(mesh)),) ( - "Indicator array (axes = $(axes(indicators))) and mesh cells (axes = $(Base.OneTo(ncells(mesh)))) have different axes" - ) - end + if only_refine + indicators[indicators .< 0] .= 0 + end - @trixi_timeit timer() "adapt" begin - difference = @trixi_timeit timer() "mesh" trixi_t8_adapt!(mesh,indicators) + @boundscheck begin + @assert axes(indicators)==(Base.OneTo(ncells(mesh)),) ("Indicator array (axes = $(axes(indicators))) and mesh cells (axes = $(Base.OneTo(ncells(mesh)))) have different axes") + end - @trixi_timeit timer() "solver" adapt!(u_ode, adaptor, mesh, equations, dg, cache, difference) - end + @trixi_timeit timer() "adapt" begin + difference = @trixi_timeit timer() "mesh" trixi_t8_adapt!(mesh, indicators) - # Store whether there were any cells coarsened or refined and perform load balancing. - has_changed = any(difference .!= 0) + @trixi_timeit timer() "solver" adapt!(u_ode, adaptor, mesh, equations, dg, + cache, difference) + end - # TODO: T8codeMesh for MPI not implemented yet. - # Check if mesh changed on other processes - # if mpi_isparallel() - # has_changed = MPI.Allreduce!(Ref(has_changed), |, mpi_comm())[] - # end + # Store whether there were any cells coarsened or refined and perform load balancing. + has_changed = any(difference .!= 0) - if has_changed # TODO: T8codeMesh for MPI not implemented yet. - # if mpi_isparallel() && amr_callback.dynamic_load_balancing - # @trixi_timeit timer() "dynamic load balancing" begin - # global_first_quadrant = unsafe_wrap(Array, mesh.p4est.global_first_quadrant, mpi_nranks() + 1) - # old_global_first_quadrant = copy(global_first_quadrant) - # partition!(mesh) - # rebalance_solver!(u_ode, mesh, equations, dg, cache, old_global_first_quadrant) - # end + # Check if mesh changed on other processes + # if mpi_isparallel() + # has_changed = MPI.Allreduce!(Ref(has_changed), |, mpi_comm())[] # end - reinitialize_boundaries!(semi.boundary_conditions, cache) - end + if has_changed + # TODO: T8codeMesh for MPI not implemented yet. + # if mpi_isparallel() && amr_callback.dynamic_load_balancing + # @trixi_timeit timer() "dynamic load balancing" begin + # global_first_quadrant = unsafe_wrap(Array, mesh.p4est.global_first_quadrant, mpi_nranks() + 1) + # old_global_first_quadrant = copy(global_first_quadrant) + # partition!(mesh) + # rebalance_solver!(u_ode, mesh, equations, dg, cache, old_global_first_quadrant) + # end + # end - # Return true if there were any cells coarsened or refined, otherwise false. - return has_changed + reinitialize_boundaries!(semi.boundary_conditions, cache) + end + + # Return true if there were any cells coarsened or refined, otherwise false. + return has_changed end function reinitialize_boundaries!(boundary_conditions::UnstructuredSortedBoundaryTypes, @@ -700,7 +699,7 @@ function current_element_levels(mesh::P4estMesh, solver, cache) end function current_element_levels(mesh::T8codeMesh, solver, cache) - return trixi_t8_get_local_element_levels(mesh.forest) + return trixi_t8_get_local_element_levels(mesh.forest) end # TODO: Taal refactor, merge the two loops of ControllerThreeLevel and IndicatorLöhner etc.? diff --git a/src/callbacks_step/analysis.jl b/src/callbacks_step/analysis.jl index 79b24626d5..b6176945d1 100644 --- a/src/callbacks_step/analysis.jl +++ b/src/callbacks_step/analysis.jl @@ -537,29 +537,31 @@ end # Print level information only if AMR is enabled function print_amr_information(callbacks, mesh::T8codeMesh, solver, cache) - # Return early if there is nothing to print - uses_amr(callbacks) || return nothing + # Return early if there is nothing to print + uses_amr(callbacks) || return nothing - # TODO: Switch to global element levels array when MPI supported or find - # another solution. - levels = trixi_t8_get_local_element_levels(mesh.forest) + # TODO: Switch to global element levels array when MPI supported or find + # another solution. + levels = trixi_t8_get_local_element_levels(mesh.forest) - min_level = minimum(levels) - max_level = maximum(levels) + min_level = minimum(levels) + max_level = maximum(levels) - mpi_println(" minlevel = $min_level") - mpi_println(" maxlevel = $max_level") + mpi_println(" minlevel = $min_level") + mpi_println(" maxlevel = $max_level") - if min_level > 0 - elements_per_level = [count(==(l), levels) for l in 1:max_level] + if min_level > 0 + elements_per_level = [count(==(l), levels) for l in 1:max_level] - for level = max_level:-1:min_level+1 - mpi_println(" ├── level $level: " * @sprintf("% 14d", elements_per_level[level])) + for level in max_level:-1:(min_level + 1) + mpi_println(" ├── level $level: " * + @sprintf("% 14d", elements_per_level[level])) + end + mpi_println(" └── level $min_level: " * + @sprintf("% 14d", elements_per_level[min_level])) end - mpi_println(" └── level $min_level: " * @sprintf("% 14d", elements_per_level[min_level])) - end - return nothing + return nothing end # Iterate over tuples of analysis integrals in a type-stable way using "lispy tuple programming". diff --git a/src/callbacks_step/save_restart_dg.jl b/src/callbacks_step/save_restart_dg.jl index 718fdb6c68..8db6db2d2b 100644 --- a/src/callbacks_step/save_restart_dg.jl +++ b/src/callbacks_step/save_restart_dg.jl @@ -7,7 +7,8 @@ function save_restart_file(u, time, dt, timestep, mesh::Union{SerialTreeMesh, StructuredMesh, - UnstructuredMesh2D, SerialP4estMesh, SerialT8codeMesh}, + UnstructuredMesh2D, SerialP4estMesh, + SerialT8codeMesh}, equations, dg::DG, cache, restart_callback) @unpack output_directory = restart_callback diff --git a/src/callbacks_step/save_solution_dg.jl b/src/callbacks_step/save_solution_dg.jl index 5eabebdf07..6d5004ff65 100644 --- a/src/callbacks_step/save_solution_dg.jl +++ b/src/callbacks_step/save_solution_dg.jl @@ -7,7 +7,8 @@ function save_solution_file(u, time, dt, timestep, mesh::Union{SerialTreeMesh, StructuredMesh, - UnstructuredMesh2D, SerialP4estMesh, SerialT8codeMesh}, + UnstructuredMesh2D, SerialP4estMesh, + SerialT8codeMesh}, equations, dg::DG, cache, solution_callback, element_variables = Dict{Symbol, Any}(); system = "") diff --git a/src/callbacks_step/stepsize_dg2d.jl b/src/callbacks_step/stepsize_dg2d.jl index 56b6059495..673c3ba6aa 100644 --- a/src/callbacks_step/stepsize_dg2d.jl +++ b/src/callbacks_step/stepsize_dg2d.jl @@ -75,7 +75,9 @@ function max_dt(u, t, mesh::ParallelTreeMesh{2}, return dt end -function max_dt(u, t, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, +function max_dt(u, t, + mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, + T8codeMesh{2}}, constant_speed::False, equations, dg::DG, cache) # to avoid a division by zero if the speed vanishes everywhere, # e.g. for steady-state linear advection @@ -109,7 +111,9 @@ function max_dt(u, t, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMe return 2 / (nnodes(dg) * max_scaled_speed) end -function max_dt(u, t, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, +function max_dt(u, t, + mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, + T8codeMesh{2}}, constant_speed::True, equations, dg::DG, cache) @unpack contravariant_vectors, inverse_jacobian = cache.elements diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 5ba8d92876..9de790f96d 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -5,65 +5,69 @@ An unstructured curved mesh based on trees that uses the C library ['t8code'](https://github.com/DLR-AMR/t8code) to manage trees and mesh refinement. """ -mutable struct T8codeMesh{NDIMS, RealT<:Real, IsParallel, NDIMSP2, NNODES} <: AbstractMesh{NDIMS} - cmesh :: Ptr{t8_cmesh} # cpointer to coarse mesh - scheme :: Ptr{t8_eclass_scheme} # cpointer to element scheme - forest :: Ptr{t8_forest} # cpointer to forest - is_parallel :: IsParallel - - # This specifies the geometry interpolation for each tree. - tree_node_coordinates :: Array{RealT, NDIMSP2} # [dimension, i, j, k, tree] - - # Stores the quadrature nodes. - nodes :: SVector{NNODES, RealT} - - boundary_names :: Array{Symbol, 2} # [face direction, tree] - current_filename :: String - - ninterfaces :: Int - nmortars :: Int - nboundaries :: Int - - function T8codeMesh{NDIMS}(cmesh, scheme, forest, nodes) where NDIMS - # TODO: Implement MPI parallelization. - # if mpi_isparallel() - # if !T8code.uses_mpi() - # error("t8code library does not support MPI") - # end - # is_parallel = Val(true) - # else - # is_parallel = Val(false) - # end - is_parallel = False() - - mesh = new{NDIMS, Float64, typeof(is_parallel), NDIMS+2, length(nodes)}(cmesh, scheme, forest, is_parallel) - - # Destroy 't8code' structs when the mesh is garbage collected. - finalizer(function (mesh::T8codeMesh{NDIMS}) - trixi_t8_unref_forest(mesh.forest) - end, mesh) - - return mesh - end +mutable struct T8codeMesh{NDIMS, RealT <: Real, IsParallel, NDIMSP2, NNODES} <: + AbstractMesh{NDIMS} + cmesh :: Ptr{t8_cmesh} # cpointer to coarse mesh + scheme :: Ptr{t8_eclass_scheme} # cpointer to element scheme + forest :: Ptr{t8_forest} # cpointer to forest + is_parallel :: IsParallel + + # This specifies the geometry interpolation for each tree. + tree_node_coordinates::Array{RealT, NDIMSP2} # [dimension, i, j, k, tree] + + # Stores the quadrature nodes. + nodes::SVector{NNODES, RealT} + + boundary_names :: Array{Symbol, 2} # [face direction, tree] + current_filename :: String + + ninterfaces :: Int + nmortars :: Int + nboundaries :: Int + + function T8codeMesh{NDIMS}(cmesh, scheme, forest, nodes) where {NDIMS} + # TODO: Implement MPI parallelization. + # if mpi_isparallel() + # if !T8code.uses_mpi() + # error("t8code library does not support MPI") + # end + # is_parallel = Val(true) + # else + # is_parallel = Val(false) + # end + is_parallel = False() + + mesh = new{NDIMS, Float64, typeof(is_parallel), NDIMS + 2, length(nodes)}(cmesh, + scheme, + forest, + is_parallel) + + # Destroy 't8code' structs when the mesh is garbage collected. + finalizer(function (mesh::T8codeMesh{NDIMS}) + trixi_t8_unref_forest(mesh.forest) + end, mesh) + + return mesh + end end -function T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, boundary_names, - current_filename) where NDIMS - - mesh = T8codeMesh{NDIMS}(cmesh, scheme, forest, nodes) +function T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, + boundary_names, + current_filename) where {NDIMS} + mesh = T8codeMesh{NDIMS}(cmesh, scheme, forest, nodes) - mesh.nodes = nodes - mesh.boundary_names = boundary_names - mesh.current_filename = current_filename - mesh.tree_node_coordinates = tree_node_coordinates + mesh.nodes = nodes + mesh.boundary_names = boundary_names + mesh.current_filename = current_filename + mesh.tree_node_coordinates = tree_node_coordinates - return mesh + return mesh end const SerialT8codeMesh{NDIMS} = T8codeMesh{NDIMS, <:Real, <:False} @inline mpi_parallel(mesh::SerialT8codeMesh) = False() -@inline Base.ndims(::T8codeMesh{NDIMS}) where NDIMS = NDIMS +@inline Base.ndims(::T8codeMesh{NDIMS}) where {NDIMS} = NDIMS @inline Base.real(::T8codeMesh{NDIMS, RealT}) where {NDIMS, RealT} = RealT @inline ntrees(mesh::T8codeMesh) = Int(t8_forest_get_num_local_trees(mesh.forest)) @@ -73,20 +77,22 @@ const SerialT8codeMesh{NDIMS} = T8codeMesh{NDIMS, <:Real, <:False} @inline nboundaries(mesh::T8codeMesh) = mesh.nboundaries function Base.show(io::IO, mesh::T8codeMesh) - print(io, "T8codeMesh{", ndims(mesh), ", ", real(mesh), "}") + print(io, "T8codeMesh{", ndims(mesh), ", ", real(mesh), "}") end -function Base.show(io::IO, :: MIME"text/plain", mesh::T8codeMesh) - if get(io, :compact, false) - show(io, mesh) - else - setup = [ - "#trees" => ntrees(mesh), - "current #cells" => ncells(mesh), - "polydeg" => length(mesh.nodes) - 1, - ] - summary_box(io, "T8codeMesh{" * string(ndims(mesh)) * ", " * string(real(mesh)) * "}", setup) - end +function Base.show(io::IO, ::MIME"text/plain", mesh::T8codeMesh) + if get(io, :compact, false) + show(io, mesh) + else + setup = [ + "#trees" => ntrees(mesh), + "current #cells" => ncells(mesh), + "polydeg" => length(mesh.nodes) - 1, + ] + summary_box(io, + "T8codeMesh{" * string(ndims(mesh)) * ", " * string(real(mesh)) * "}", + setup) + end end """ @@ -110,73 +116,76 @@ Non-periodic boundaries will be called ':x_neg', ':x_pos', ':y_neg', ':y_pos', ' deciding for each dimension if the boundaries in this dimension are periodic. """ function T8codeMesh(trees_per_dimension; polydeg, - mapping=coordinates2mapping((-1.0,-1.0), (1.0,1.0)), RealT=Float64, initial_refinement_level=0, periodicity=true) - - NDIMS = length(trees_per_dimension) - - @assert NDIMS == 2 # Only support for NDIMS = 2 yet. - - # Convert periodicity to a Tuple of a Bool for every dimension - if all(periodicity) - # Also catches case where periodicity = true - periodicity = ntuple(_->true, NDIMS) - elseif !any(periodicity) - # Also catches case where periodicity = false - periodicity = ntuple(_->false, NDIMS) - else - # Default case if periodicity is an iterable - periodicity = Tuple(periodicity) - end + mapping = coordinates2mapping((-1.0, -1.0), (1.0, 1.0)), + RealT = Float64, initial_refinement_level = 0, periodicity = true) + NDIMS = length(trees_per_dimension) + + @assert NDIMS == 2 # Only support for NDIMS = 2 yet. + + # Convert periodicity to a Tuple of a Bool for every dimension + if all(periodicity) + # Also catches case where periodicity = true + periodicity = ntuple(_ -> true, NDIMS) + elseif !any(periodicity) + # Also catches case where periodicity = false + periodicity = ntuple(_ -> false, NDIMS) + else + # Default case if periodicity is an iterable + periodicity = Tuple(periodicity) + end - conn = T8code.Libt8.p4est_connectivity_new_brick(trees_per_dimension..., periodicity...) - do_partition = 0 - cmesh = t8_cmesh_new_from_p4est(conn,mpi_comm(),do_partition) - T8code.Libt8.p4est_connectivity_destroy(conn) + conn = T8code.Libt8.p4est_connectivity_new_brick(trees_per_dimension..., periodicity...) + do_partition = 0 + cmesh = t8_cmesh_new_from_p4est(conn, mpi_comm(), do_partition) + T8code.Libt8.p4est_connectivity_destroy(conn) - scheme = t8_scheme_new_default_cxx() - forest = t8_forest_new_uniform(cmesh,scheme,initial_refinement_level,0,mpi_comm()) + scheme = t8_scheme_new_default_cxx() + forest = t8_forest_new_uniform(cmesh, scheme, initial_refinement_level, 0, mpi_comm()) - basis = LobattoLegendreBasis(RealT, polydeg) - nodes = basis.nodes + basis = LobattoLegendreBasis(RealT, polydeg) + nodes = basis.nodes - tree_node_coordinates = Array{RealT, NDIMS+2}(undef, NDIMS, - ntuple(_ -> length(nodes), NDIMS)..., - prod(trees_per_dimension)) + tree_node_coordinates = Array{RealT, NDIMS + 2}(undef, NDIMS, + ntuple(_ -> length(nodes), NDIMS)..., + prod(trees_per_dimension)) - # Get cell length in reference mesh: Omega_ref = [-1,1]^2. - dx = 2 / trees_per_dimension[1] - dy = 2 / trees_per_dimension[2] + # Get cell length in reference mesh: Omega_ref = [-1,1]^2. + dx = 2 / trees_per_dimension[1] + dy = 2 / trees_per_dimension[2] - num_local_trees = t8_cmesh_get_num_local_trees(cmesh) + num_local_trees = t8_cmesh_get_num_local_trees(cmesh) - # Non-periodic boundaries. - boundary_names = fill(Symbol("---"), 2 * NDIMS, prod(trees_per_dimension)) + # Non-periodic boundaries. + boundary_names = fill(Symbol("---"), 2 * NDIMS, prod(trees_per_dimension)) - for itree = 1:num_local_trees - veptr = t8_cmesh_get_tree_vertices(cmesh, itree-1) - verts = unsafe_wrap(Array,veptr,(3,1 << NDIMS)) + for itree in 1:num_local_trees + veptr = t8_cmesh_get_tree_vertices(cmesh, itree - 1) + verts = unsafe_wrap(Array, veptr, (3, 1 << NDIMS)) - # Calculate node coordinates of reference mesh. - cell_x_offset = (verts[1,1] - 1/2*(trees_per_dimension[1]-1)) * dx - cell_y_offset = (verts[2,1] - 1/2*(trees_per_dimension[2]-1)) * dy + # Calculate node coordinates of reference mesh. + cell_x_offset = (verts[1, 1] - 1 / 2 * (trees_per_dimension[1] - 1)) * dx + cell_y_offset = (verts[2, 1] - 1 / 2 * (trees_per_dimension[2] - 1)) * dy - for j in eachindex(nodes), i in eachindex(nodes) - tree_node_coordinates[:, i, j, itree] .= mapping(cell_x_offset + dx * nodes[i]/2, - cell_y_offset + dy * nodes[j]/2) - end + for j in eachindex(nodes), i in eachindex(nodes) + tree_node_coordinates[:, i, j, itree] .= mapping(cell_x_offset + + dx * nodes[i] / 2, + cell_y_offset + + dy * nodes[j] / 2) + end - if !periodicity[1] - boundary_names[1, itree] = :x_neg - boundary_names[2, itree] = :x_pos - end + if !periodicity[1] + boundary_names[1, itree] = :x_neg + boundary_names[2, itree] = :x_pos + end - if !periodicity[2] - boundary_names[3, itree] = :y_neg - boundary_names[4, itree] = :y_pos + if !periodicity[2] + boundary_names[3, itree] = :y_neg + boundary_names[4, itree] = :y_pos + end end - end - return T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, boundary_names, "") + return T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, + boundary_names, "") end """ @@ -200,65 +209,61 @@ conforming mesh from a `t8_cmesh` data structure. - `initial_refinement_level::Integer`: refine the mesh uniformly to this level before the simulation starts. """ function T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh}; - mapping=nothing, polydeg=1, RealT=Float64, - initial_refinement_level=0) where NDIMS + mapping = nothing, polydeg = 1, RealT = Float64, + initial_refinement_level = 0) where {NDIMS} + @assert NDIMS == 2 # Only support for NDIMS = 2 yet. - @assert NDIMS == 2 # Only support for NDIMS = 2 yet. + scheme = t8_scheme_new_default_cxx() + forest = t8_forest_new_uniform(cmesh, scheme, initial_refinement_level, 0, mpi_comm()) - scheme = t8_scheme_new_default_cxx() - forest = t8_forest_new_uniform(cmesh,scheme,initial_refinement_level,0,mpi_comm()) + basis = LobattoLegendreBasis(RealT, polydeg) + nodes = basis.nodes - basis = LobattoLegendreBasis(RealT, polydeg) - nodes = basis.nodes + num_local_trees = t8_cmesh_get_num_local_trees(cmesh) - num_local_trees = t8_cmesh_get_num_local_trees(cmesh) + tree_node_coordinates = Array{RealT, NDIMS + 2}(undef, NDIMS, + ntuple(_ -> length(nodes), NDIMS)..., + num_local_trees) - tree_node_coordinates = Array{RealT, NDIMS+2}(undef, NDIMS, - ntuple(_ -> length(nodes), NDIMS)..., - num_local_trees) + nodes_in = [-1.0, 1.0] + matrix = polynomial_interpolation_matrix(nodes_in, nodes) + data_in = Array{RealT, 3}(undef, 2, 2, 2) + tmp1 = zeros(RealT, 2, length(nodes), length(nodes_in)) - nodes_in = [-1.0, 1.0] - matrix = polynomial_interpolation_matrix(nodes_in, nodes) - data_in = Array{RealT, 3}(undef, 2, 2, 2) - tmp1 = zeros(RealT, 2, length(nodes), length(nodes_in)) + for itree in 0:(num_local_trees - 1) + veptr = t8_cmesh_get_tree_vertices(cmesh, itree) + verts = unsafe_wrap(Array, veptr, (3, 1 << NDIMS)) - for itree in 0:num_local_trees-1 + u = verts[:, 2] - verts[:, 1] + v = verts[:, 3] - verts[:, 1] + w = [0.0, 0.0, 1.0] - veptr = t8_cmesh_get_tree_vertices(cmesh, itree) - verts = unsafe_wrap(Array,veptr,(3,1 << NDIMS)) + vol = dot(cross(u, v), w) - u = verts[:,2] - verts[:,1] - v = verts[:,3] - verts[:,1] - w = [0.0,0.0,1.0] + if vol < 0.0 + @warn "Discovered negative volumes in `cmesh`: vol = $vol" + end - vol = dot(cross(u,v),w) + # Tree vertices are stored in z-order. + @views data_in[:, 1, 1] .= verts[1:2, 1] + @views data_in[:, 2, 1] .= verts[1:2, 2] + @views data_in[:, 1, 2] .= verts[1:2, 3] + @views data_in[:, 2, 2] .= verts[1:2, 4] - if vol < 0.0 - @warn "Discovered negative volumes in `cmesh`: vol = $vol" + # Interpolate corner coordinates to specified nodes. + multiply_dimensionwise!(view(tree_node_coordinates, :, :, :, itree + 1), + matrix, matrix, + data_in, + tmp1) end - # Tree vertices are stored in z-order. - @views data_in[:, 1, 1] .= verts[1:2,1] - @views data_in[:, 2, 1] .= verts[1:2,2] - @views data_in[:, 1, 2] .= verts[1:2,3] - @views data_in[:, 2, 2] .= verts[1:2,4] + map_node_coordinates!(tree_node_coordinates, mapping) - # Interpolate corner coordinates to specified nodes. - multiply_dimensionwise!( - view(tree_node_coordinates, :, :, :, itree+1), - matrix, matrix, - data_in, - tmp1 - ) + # There's no simple and generic way to distinguish boundaries. Name all of them :all. + boundary_names = fill(:all, 2 * NDIMS, num_local_trees) - end - - map_node_coordinates!(tree_node_coordinates, mapping) - - # There's no simple and generic way to distinguish boundaries. Name all of them :all. - boundary_names = fill(:all, 2 * NDIMS, num_local_trees) - - return T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, boundary_names, "") + return T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, + boundary_names, "") end """ @@ -281,13 +286,12 @@ conforming mesh from a `p4est_connectivity` data structure. - `RealT::Type`: the type that should be used for coordinates. - `initial_refinement_level::Integer`: refine the mesh uniformly to this level before the simulation starts. """ -function T8codeMesh{NDIMS}(conn::Ptr{p4est_connectivity}; kwargs...) where NDIMS - - @assert NDIMS == 2 # Only support for NDIMS = 2 yet. +function T8codeMesh{NDIMS}(conn::Ptr{p4est_connectivity}; kwargs...) where {NDIMS} + @assert NDIMS == 2 # Only support for NDIMS = 2 yet. - cmesh = t8_cmesh_new_from_p4est(conn, mpi_comm(), 0) + cmesh = t8_cmesh_new_from_p4est(conn, mpi_comm(), 0) - return T8codeMesh{NDIMS}(cmesh; kwargs...) + return T8codeMesh{NDIMS}(cmesh; kwargs...) end """ @@ -310,26 +314,25 @@ mesh from a Gmsh mesh file (`.msh`). - `RealT::Type`: the type that should be used for coordinates. - `initial_refinement_level::Integer`: refine the mesh uniformly to this level before the simulation starts. """ -function T8codeMesh{NDIMS}(meshfile::String; kwargs...) where NDIMS +function T8codeMesh{NDIMS}(meshfile::String; kwargs...) where {NDIMS} + @assert NDIMS == 2 # Only support for NDIMS = 2 yet. - @assert NDIMS == 2 # Only support for NDIMS = 2 yet. + # Prevent `t8code` from crashing Julia if the file doesn't exist. + @assert isfile(meshfile) - # Prevent `t8code` from crashing Julia if the file doesn't exist. - @assert isfile(meshfile) - - meshfile_prefix, meshfile_suffix = splitext(meshfile) + meshfile_prefix, meshfile_suffix = splitext(meshfile) - cmesh = t8_cmesh_from_msh_file(meshfile_prefix, 0, mpi_comm(), NDIMS, 0, 0) + cmesh = t8_cmesh_from_msh_file(meshfile_prefix, 0, mpi_comm(), NDIMS, 0, 0) - return T8codeMesh{NDIMS}(cmesh; kwargs...) + return T8codeMesh{NDIMS}(cmesh; kwargs...) end # TODO: Just a placeholder. Will be implemented later when MPI is supported. -function balance!(mesh::T8codeMesh, init_fn=C_NULL) - return nothing +function balance!(mesh::T8codeMesh, init_fn = C_NULL) + return nothing end # TODO: Just a placeholder. Will be implemented later when MPI is supported. -function partition!(mesh::T8codeMesh; allow_coarsening=true, weight_fn=C_NULL) - return nothing +function partition!(mesh::T8codeMesh; allow_coarsening = true, weight_fn = C_NULL) + return nothing end diff --git a/src/solvers/dg.jl b/src/solvers/dg.jl index 81d2e70b31..495e0ffc4a 100644 --- a/src/solvers/dg.jl +++ b/src/solvers/dg.jl @@ -363,7 +363,8 @@ function get_element_variables!(element_variables, u, mesh, equations, dg::DG, c dg, cache) end -const MeshesDGSEM = Union{TreeMesh, StructuredMesh, UnstructuredMesh2D, P4estMesh, T8codeMesh} +const MeshesDGSEM = Union{TreeMesh, StructuredMesh, UnstructuredMesh2D, P4estMesh, + T8codeMesh} @inline function ndofs(mesh::MeshesDGSEM, dg::DG, cache) nelements(cache.elements) * nnodes(dg)^ndims(mesh) @@ -680,5 +681,4 @@ include("dgsem_structured/dg.jl") include("dgsem_unstructured/dg.jl") include("dgsem_p4est/dg.jl") include("dgsem_t8code/dg.jl") - end # @muladd diff --git a/src/solvers/dgsem_p4est/containers_2d.jl b/src/solvers/dgsem_p4est/containers_2d.jl index 62129dd346..236d7d24c0 100644 --- a/src/solvers/dgsem_p4est/containers_2d.jl +++ b/src/solvers/dgsem_p4est/containers_2d.jl @@ -6,9 +6,10 @@ #! format: noindent # Initialize data structures in element container -function init_elements!(elements, mesh::Union{P4estMesh{2}, T8codeMesh{2}}, basis::LobattoLegendreBasis) - @unpack node_coordinates, jacobian_matrix, - contravariant_vectors, inverse_jacobian = elements +function init_elements!(elements, mesh::Union{P4estMesh{2}, T8codeMesh{2}}, + basis::LobattoLegendreBasis) + @unpack node_coordinates, jacobian_matrix, + contravariant_vectors, inverse_jacobian = elements calc_node_coordinates!(node_coordinates, mesh, basis) diff --git a/src/solvers/dgsem_p4est/dg_2d.jl b/src/solvers/dgsem_p4est/dg_2d.jl index 21bcf28da0..97b931fa32 100644 --- a/src/solvers/dgsem_p4est/dg_2d.jl +++ b/src/solvers/dgsem_p4est/dg_2d.jl @@ -7,7 +7,8 @@ # The methods below are specialized on the mortar type # and called from the basic `create_cache` method at the top. -function create_cache(mesh::Union{P4estMesh{2}, T8codeMesh{2}}, equations, mortar_l2::LobattoLegendreMortarL2, uEltype) +function create_cache(mesh::Union{P4estMesh{2}, T8codeMesh{2}}, equations, + mortar_l2::LobattoLegendreMortarL2, uEltype) # TODO: Taal performance using different types MA2d = MArray{Tuple{nvariables(equations), nnodes(mortar_l2)}, uEltype, 2, @@ -558,7 +559,8 @@ end end @inline function mortar_fluxes_to_elements!(surface_flux_values, - mesh::Union{P4estMesh{2}, T8codeMesh{2}}, equations, + mesh::Union{P4estMesh{2}, T8codeMesh{2}}, + equations, mortar_l2::LobattoLegendreMortarL2, dg::DGSEM, cache, mortar, fstar, u_buffer) @unpack neighbor_ids, node_indices = cache.mortars diff --git a/src/solvers/dgsem_structured/dg_2d.jl b/src/solvers/dgsem_structured/dg_2d.jl index dc37690866..3e8ce759b3 100644 --- a/src/solvers/dgsem_structured/dg_2d.jl +++ b/src/solvers/dgsem_structured/dg_2d.jl @@ -91,7 +91,10 @@ end end @inline function flux_differencing_kernel!(du, u, - element, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, + element, + mesh::Union{StructuredMesh{2}, + UnstructuredMesh2D, P4estMesh{2}, + T8codeMesh{2}}, nonconservative_terms::False, equations, volume_flux, dg::DGSEM, cache, alpha = true) @unpack derivative_split = dg.basis @@ -145,7 +148,10 @@ end end @inline function flux_differencing_kernel!(du, u, - element, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, + element, + mesh::Union{StructuredMesh{2}, + UnstructuredMesh2D, P4estMesh{2}, + T8codeMesh{2}}, nonconservative_terms::True, equations, volume_flux, dg::DGSEM, cache, alpha = true) @unpack derivative_split = dg.basis @@ -212,7 +218,8 @@ end # "A provably entropy stable subcell shock capturing approach for high order split form DG for the compressible Euler equations" # [arXiv: 2008.12044v2](https://arxiv.org/pdf/2008.12044) @inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, u, - mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, + mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, + P4estMesh{2}, T8codeMesh{2}}, nonconservative_terms::False, equations, volume_flux_fv, dg::DGSEM, element, cache) @unpack contravariant_vectors = cache.elements @@ -279,8 +286,10 @@ end end # Calculate the finite volume fluxes inside curvilinear elements (**with non-conservative terms**). -@inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, u::AbstractArray{<:Any,4}, - mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, +@inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, + u::AbstractArray{<:Any, 4}, + mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, + P4estMesh{2}, T8codeMesh{2}}, nonconservative_terms::True, equations, volume_flux_fv, dg::DGSEM, element, cache) @unpack contravariant_vectors = cache.elements @@ -600,7 +609,8 @@ function calc_boundary_flux!(cache, u, t, boundary_conditions::NamedTuple, end function apply_jacobian!(du, - mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, + mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, + P4estMesh{2}, T8codeMesh{2}}, equations, dg::DG, cache) @unpack inverse_jacobian = cache.elements diff --git a/src/solvers/dgsem_t8code/containers.jl b/src/solvers/dgsem_t8code/containers.jl index e808a7336d..37bb581687 100644 --- a/src/solvers/dgsem_t8code/containers.jl +++ b/src/solvers/dgsem_t8code/containers.jl @@ -1,57 +1,58 @@ function reinitialize_containers!(mesh::T8codeMesh, equations, dg::DGSEM, cache) - # Re-initialize elements container. - @unpack elements = cache - resize!(elements, ncells(mesh)) - init_elements!(elements, mesh, dg.basis) + # Re-initialize elements container. + @unpack elements = cache + resize!(elements, ncells(mesh)) + init_elements!(elements, mesh, dg.basis) - count_required_surfaces!(mesh) + count_required_surfaces!(mesh) - # Resize interfaces container. - @unpack interfaces = cache - resize!(interfaces, mesh.ninterfaces) + # Resize interfaces container. + @unpack interfaces = cache + resize!(interfaces, mesh.ninterfaces) - # Resize mortars container. - @unpack mortars = cache - resize!(mortars, mesh.nmortars) + # Resize mortars container. + @unpack mortars = cache + resize!(mortars, mesh.nmortars) - # Resize boundaries container. - @unpack boundaries = cache - resize!(boundaries, mesh.nboundaries) + # Resize boundaries container. + @unpack boundaries = cache + resize!(boundaries, mesh.nboundaries) - trixi_t8_fill_mesh_info(mesh.forest, elements, interfaces, mortars, boundaries, mesh.boundary_names) + trixi_t8_fill_mesh_info(mesh.forest, elements, interfaces, mortars, boundaries, + mesh.boundary_names) - return nothing + return nothing end function count_required_surfaces!(mesh::T8codeMesh) - counts = trixi_t8_count_interfaces(mesh.forest) + counts = trixi_t8_count_interfaces(mesh.forest) - mesh.nmortars = counts.mortars - mesh.ninterfaces = counts.interfaces - mesh.nboundaries = counts.boundaries + mesh.nmortars = counts.mortars + mesh.ninterfaces = counts.interfaces + mesh.nboundaries = counts.boundaries end # Compatibility to `dgsem_p4est/containers.jl`. function count_required_surfaces(mesh::T8codeMesh) - return (interfaces = mesh.ninterfaces, - mortars = mesh.nmortars, - boundaries = mesh.nboundaries) + return (interfaces = mesh.ninterfaces, + mortars = mesh.nmortars, + boundaries = mesh.nboundaries) end # Compatibility to `dgsem_p4est/containers.jl`. function init_interfaces!(interfaces, mesh::T8codeMesh) - # Already computed. Do nothing. - return nothing + # Already computed. Do nothing. + return nothing end # Compatibility to `dgsem_p4est/containers.jl`. function init_mortars!(mortars, mesh::T8codeMesh) - # Already computed. Do nothing. - return nothing + # Already computed. Do nothing. + return nothing end # Compatibility to `dgsem_p4est/containers.jl`. function init_boundaries!(boundaries, mesh::T8codeMesh) - # Already computed. Do nothing. - return nothing + # Already computed. Do nothing. + return nothing end diff --git a/src/solvers/dgsem_t8code/containers_2d.jl b/src/solvers/dgsem_t8code/containers_2d.jl index aeb0d2fac4..86e2e7a9e4 100644 --- a/src/solvers/dgsem_t8code/containers_2d.jl +++ b/src/solvers/dgsem_t8code/containers_2d.jl @@ -1,53 +1,57 @@ @muladd begin -# Interpolate tree_node_coordinates to each quadrant at the specified nodes. -function calc_node_coordinates!(node_coordinates, - mesh::T8codeMesh{2}, - nodes::AbstractVector) - # We use `StrideArray`s here since these buffers are used in performance-critical - # places and the additional information passed to the compiler makes them faster - # than native `Array`s. - tmp1 = StrideArray(undef, real(mesh), - StaticInt(2), static_length(nodes), static_length(mesh.nodes)) - matrix1 = StrideArray(undef, real(mesh), - static_length(nodes), static_length(mesh.nodes)) - matrix2 = similar(matrix1) - baryweights_in = barycentric_weights(mesh.nodes) - - num_local_trees = t8_forest_get_num_local_trees(mesh.forest) - - current_index = 0 - for itree = 0:num_local_trees-1 - - tree_class = t8_forest_get_tree_class(mesh.forest, itree) - eclass_scheme = t8_forest_get_eclass_scheme(mesh.forest, tree_class) - num_elements_in_tree = t8_forest_get_tree_num_elements(mesh.forest, itree) - - for ielement = 0:num_elements_in_tree-1 - element = t8_forest_get_element_in_tree(mesh.forest, itree, ielement) - element_level = t8_element_level(eclass_scheme, element) - - element_length = t8_quad_len(element_level) / t8_quad_root_len - - element_coords = Array{Float64}(undef, 3) - t8_element_vertex_reference_coords(eclass_scheme, element, 0, pointer(element_coords)) - - nodes_out_x = 2 * (element_length * 1/2 * (nodes .+ 1) .+ element_coords[1]) .- 1 - nodes_out_y = 2 * (element_length * 1/2 * (nodes .+ 1) .+ element_coords[2]) .- 1 - - polynomial_interpolation_matrix!(matrix1, mesh.nodes, nodes_out_x, baryweights_in) - polynomial_interpolation_matrix!(matrix2, mesh.nodes, nodes_out_y, baryweights_in) - - multiply_dimensionwise!( - view(node_coordinates, :, :, :, current_index += 1), - matrix1, matrix2, - view(mesh.tree_node_coordinates, :, :, :, itree+1), - tmp1 - ) + # Interpolate tree_node_coordinates to each quadrant at the specified nodes. + function calc_node_coordinates!(node_coordinates, + mesh::T8codeMesh{2}, + nodes::AbstractVector) + # We use `StrideArray`s here since these buffers are used in performance-critical + # places and the additional information passed to the compiler makes them faster + # than native `Array`s. + tmp1 = StrideArray(undef, real(mesh), + StaticInt(2), static_length(nodes), static_length(mesh.nodes)) + matrix1 = StrideArray(undef, real(mesh), + static_length(nodes), static_length(mesh.nodes)) + matrix2 = similar(matrix1) + baryweights_in = barycentric_weights(mesh.nodes) + + num_local_trees = t8_forest_get_num_local_trees(mesh.forest) + + current_index = 0 + for itree in 0:(num_local_trees - 1) + tree_class = t8_forest_get_tree_class(mesh.forest, itree) + eclass_scheme = t8_forest_get_eclass_scheme(mesh.forest, tree_class) + num_elements_in_tree = t8_forest_get_tree_num_elements(mesh.forest, itree) + + for ielement in 0:(num_elements_in_tree - 1) + element = t8_forest_get_element_in_tree(mesh.forest, itree, ielement) + element_level = t8_element_level(eclass_scheme, element) + + element_length = t8_quad_len(element_level) / t8_quad_root_len + + element_coords = Array{Float64}(undef, 3) + t8_element_vertex_reference_coords(eclass_scheme, element, 0, + pointer(element_coords)) + + nodes_out_x = 2 * + (element_length * 1 / 2 * (nodes .+ 1) .+ element_coords[1]) .- + 1 + nodes_out_y = 2 * + (element_length * 1 / 2 * (nodes .+ 1) .+ element_coords[2]) .- + 1 + + polynomial_interpolation_matrix!(matrix1, mesh.nodes, nodes_out_x, + baryweights_in) + polynomial_interpolation_matrix!(matrix2, mesh.nodes, nodes_out_y, + baryweights_in) + + multiply_dimensionwise!(view(node_coordinates, :, :, :, current_index += 1), + matrix1, matrix2, + view(mesh.tree_node_coordinates, :, :, :, + itree + 1), + tmp1) + end + end + + return node_coordinates end - end - - return node_coordinates -end - end # @muladd diff --git a/src/solvers/dgsem_t8code/dg.jl b/src/solvers/dgsem_t8code/dg.jl index e92f9000c2..a126a1badb 100644 --- a/src/solvers/dgsem_t8code/dg.jl +++ b/src/solvers/dgsem_t8code/dg.jl @@ -1,28 +1,30 @@ @muladd begin -# This method is called when a SemidiscretizationHyperbolic is constructed. -# It constructs the basic `cache` used throughout the simulation to compute -# the RHS etc. -function create_cache(mesh::T8codeMesh, equations::AbstractEquations, dg::DG, ::Any, ::Type{uEltype}) where {uEltype<:Real} - count_required_surfaces!(mesh) + # This method is called when a SemidiscretizationHyperbolic is constructed. + # It constructs the basic `cache` used throughout the simulation to compute + # the RHS etc. + function create_cache(mesh::T8codeMesh, equations::AbstractEquations, dg::DG, ::Any, + ::Type{uEltype}) where {uEltype <: Real} + count_required_surfaces!(mesh) - elements = init_elements(mesh, equations, dg.basis, uEltype) - interfaces = init_interfaces(mesh, equations, dg.basis, elements) - boundaries = init_boundaries(mesh, equations, dg.basis, elements) - mortars = init_mortars(mesh, equations, dg.basis, elements) + elements = init_elements(mesh, equations, dg.basis, uEltype) + interfaces = init_interfaces(mesh, equations, dg.basis, elements) + boundaries = init_boundaries(mesh, equations, dg.basis, elements) + mortars = init_mortars(mesh, equations, dg.basis, elements) - trixi_t8_fill_mesh_info(mesh.forest, elements, interfaces, mortars, boundaries, mesh.boundary_names) + trixi_t8_fill_mesh_info(mesh.forest, elements, interfaces, mortars, boundaries, + mesh.boundary_names) - cache = (; elements, interfaces, boundaries, mortars) + cache = (; elements, interfaces, boundaries, mortars) - # Add specialized parts of the cache required to compute the volume integral etc. - cache = (; cache..., create_cache(mesh, equations, dg.volume_integral, dg, uEltype)...) - cache = (; cache..., create_cache(mesh, equations, dg.mortar, uEltype)...) + # Add specialized parts of the cache required to compute the volume integral etc. + cache = (; cache..., + create_cache(mesh, equations, dg.volume_integral, dg, uEltype)...) + cache = (; cache..., create_cache(mesh, equations, dg.mortar, uEltype)...) - return cache -end - -include("containers.jl") -include("containers_2d.jl") + return cache + end + include("containers.jl") + include("containers_2d.jl") end # @muladd diff --git a/src/solvers/dgsem_tree/dg_2d.jl b/src/solvers/dgsem_tree/dg_2d.jl index 2cc968b2ea..59af625781 100644 --- a/src/solvers/dgsem_tree/dg_2d.jl +++ b/src/solvers/dgsem_tree/dg_2d.jl @@ -36,12 +36,15 @@ end # The methods below are specialized on the volume integral type # and called from the basic `create_cache` method at the top. -function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, - equations, volume_integral::VolumeIntegralFluxDifferencing, dg::DG, uEltype) - NamedTuple() +function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, + P4estMesh{2}, T8codeMesh{2}}, + equations, volume_integral::VolumeIntegralFluxDifferencing, + dg::DG, uEltype) + NamedTuple() end -function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, equations, +function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, + P4estMesh{2}, T8codeMesh{2}}, equations, volume_integral::VolumeIntegralShockCapturingHG, dg::DG, uEltype) element_ids_dg = Int[] element_ids_dgfv = Int[] @@ -66,9 +69,10 @@ function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMe fstar1_L_threaded, fstar1_R_threaded, fstar2_L_threaded, fstar2_R_threaded) end -function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, equations, - volume_integral::VolumeIntegralPureLGLFiniteVolume, dg::DG, uEltype) - +function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, + P4estMesh{2}, T8codeMesh{2}}, equations, + volume_integral::VolumeIntegralPureLGLFiniteVolume, dg::DG, + uEltype) A3dp1_x = Array{uEltype, 3} A3dp1_y = Array{uEltype, 3} @@ -87,7 +91,8 @@ end # The methods below are specialized on the mortar type # and called from the basic `create_cache` method at the top. -function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, +function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, + P4estMesh{2}, T8codeMesh{2}}, equations, mortar_l2::LobattoLegendreMortarL2, uEltype) # TODO: Taal performance using different types MA2d = MArray{Tuple{nvariables(equations), nnodes(mortar_l2)}, uEltype, 2, @@ -174,7 +179,9 @@ function rhs!(du, u, t, end function calc_volume_integral!(du, u, - mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, + mesh::Union{TreeMesh{2}, StructuredMesh{2}, + UnstructuredMesh2D, P4estMesh{2}, + T8codeMesh{2}}, nonconservative_terms, equations, volume_integral::VolumeIntegralWeakForm, dg::DGSEM, cache) @@ -219,7 +226,9 @@ end # mapping terms, stored in `cache.elements.contravariant_vectors`, is peeled apart # from the evaluation of the physical fluxes in each Cartesian direction function calc_volume_integral!(du, u, - mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, + mesh::Union{TreeMesh{2}, StructuredMesh{2}, + UnstructuredMesh2D, P4estMesh{2}, + T8codeMesh{2}}, nonconservative_terms, equations, volume_integral::VolumeIntegralFluxDifferencing, dg::DGSEM, cache) @@ -314,7 +323,9 @@ end # TODO: Taal dimension agnostic function calc_volume_integral!(du, u, - mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, + mesh::Union{TreeMesh{2}, StructuredMesh{2}, + UnstructuredMesh2D, P4estMesh{2}, + T8codeMesh{2}}, nonconservative_terms, equations, volume_integral::VolumeIntegralShockCapturingHG, dg::DGSEM, cache) @@ -372,7 +383,9 @@ function calc_volume_integral!(du, u, end @inline function fv_kernel!(du, u, - mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, + mesh::Union{TreeMesh{2}, StructuredMesh{2}, + UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2} + }, nonconservative_terms, equations, volume_flux_fv, dg::DGSEM, cache, element, alpha = true) @unpack fstar1_L_threaded, fstar1_R_threaded, fstar2_L_threaded, fstar2_R_threaded = cache diff --git a/src/solvers/dgsem_tree/indicators_2d.jl b/src/solvers/dgsem_tree/indicators_2d.jl index c82dc57472..cc9382b81c 100644 --- a/src/solvers/dgsem_tree/indicators_2d.jl +++ b/src/solvers/dgsem_tree/indicators_2d.jl @@ -44,7 +44,7 @@ end modal = modal_threaded[Threads.threadid()] modal_tmp1 = modal_tmp1_threaded[Threads.threadid()] - # Calculate indicator variables at Gauss-Lobatto nodes + # Calculate indicator variables at Gauss-Lobatto nodes for j in eachnode(dg), i in eachnode(dg) u_local = get_node_vars(u, equations, dg, i, j, element) indicator[i, j] = indicator_hg.variable(u_local, equations) @@ -98,7 +98,8 @@ end end # Diffuse alpha values by setting each alpha to at least 50% of neighboring elements' alpha -function apply_smoothing!(mesh::Union{TreeMesh{2}, P4estMesh{2}, T8codeMesh{2}}, alpha, alpha_tmp, dg, +function apply_smoothing!(mesh::Union{TreeMesh{2}, P4estMesh{2}, T8codeMesh{2}}, alpha, + alpha_tmp, dg, cache) # Copy alpha values such that smoothing is indpedenent of the element access order alpha_tmp .= alpha diff --git a/src/solvers/dgsem_unstructured/dg_2d.jl b/src/solvers/dgsem_unstructured/dg_2d.jl index 1540e14da4..7b8dafdddd 100644 --- a/src/solvers/dgsem_unstructured/dg_2d.jl +++ b/src/solvers/dgsem_unstructured/dg_2d.jl @@ -325,9 +325,10 @@ end # Iterate over tuples of boundary condition types and associated indices # in a type-stable way using "lispy tuple programming". -function calc_boundary_flux_by_type!(cache, t, BCs::NTuple{N,Any}, - BC_indices::NTuple{N,Vector{Int}}, - mesh::Union{UnstructuredMesh2D, P4estMesh, T8codeMesh}, +function calc_boundary_flux_by_type!(cache, t, BCs::NTuple{N, Any}, + BC_indices::NTuple{N, Vector{Int}}, + mesh::Union{UnstructuredMesh2D, P4estMesh, + T8codeMesh}, equations, surface_integral, dg::DG) where {N} # Extract the boundary condition type and index vector boundary_condition = first(BCs) @@ -350,7 +351,8 @@ end # terminate the type-stable iteration over tuples function calc_boundary_flux_by_type!(cache, t, BCs::Tuple{}, BC_indices::Tuple{}, - mesh::Union{UnstructuredMesh2D, P4estMesh, T8codeMesh}, + mesh::Union{UnstructuredMesh2D, P4estMesh, + T8codeMesh}, equations, surface_integral, dg::DG) nothing end From 585303446a05b92f05354fab50132d09c2e3fed0 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 20 Jun 2023 10:22:10 +0200 Subject: [PATCH 053/128] Applied automatic formatting. --- src/Trixi.jl | 4 +- src/auxiliary/t8code.jl | 497 ++++++++++----------- src/callbacks_step/amr.jl | 85 ++-- src/callbacks_step/amr_dg2d.jl | 98 ++--- src/callbacks_step/amr_dg3d.jl | 102 ++--- src/callbacks_step/analysis.jl | 34 +- src/callbacks_step/analysis_dg2d.jl | 8 +- src/callbacks_step/analysis_dg3d.jl | 31 +- src/callbacks_step/save_restart_dg.jl | 3 +- src/callbacks_step/save_solution_dg.jl | 3 +- src/callbacks_step/stepsize_dg2d.jl | 8 +- src/meshes/mesh_io.jl | 9 +- src/meshes/t8code_mesh.jl | 510 +++++++++++----------- src/solvers/dg.jl | 4 +- src/solvers/dgsem_p4est/containers.jl | 14 +- src/solvers/dgsem_p4est/containers_2d.jl | 7 +- src/solvers/dgsem_p4est/containers_3d.jl | 8 +- src/solvers/dgsem_p4est/dg_2d.jl | 6 +- src/solvers/dgsem_p4est/dg_3d.jl | 29 +- src/solvers/dgsem_structured/dg_2d.jl | 22 +- src/solvers/dgsem_structured/dg_3d.jl | 25 +- src/solvers/dgsem_t8code/containers.jl | 59 +-- src/solvers/dgsem_t8code/containers_2d.jl | 120 ++--- src/solvers/dgsem_t8code/containers_3d.jl | 432 +++++++++--------- src/solvers/dgsem_t8code/dg.jl | 54 +-- src/solvers/dgsem_tree/dg_2d.jl | 37 +- src/solvers/dgsem_tree/dg_3d.jl | 28 +- src/solvers/dgsem_tree/indicators_2d.jl | 5 +- src/solvers/dgsem_tree/indicators_3d.jl | 3 +- src/solvers/dgsem_unstructured/dg_2d.jl | 10 +- 30 files changed, 1183 insertions(+), 1072 deletions(-) diff --git a/src/Trixi.jl b/src/Trixi.jl index 270aecb870..e38d27508e 100644 --- a/src/Trixi.jl +++ b/src/Trixi.jl @@ -271,8 +271,8 @@ export PlotData1D, PlotData2D, ScalarPlotData2D, getmesh, adapt_to_mesh_level!, function __init__() init_mpi() - init_p4est() - init_t8code() + init_p4est() + init_t8code() register_error_hints() diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index 67ac0ca32d..dda87f2041 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -7,266 +7,274 @@ is already initialized and if yes, do nothing, thus it is safe to call it multiple times. """ function init_t8code() - t8code_package_id = t8_get_package_id() - if t8code_package_id >= 0 - return nothing - end + t8code_package_id = t8_get_package_id() + if t8code_package_id >= 0 + return nothing + end - # Initialize the sc library, has to happen before we initialize t8code. - T8code.Libt8.sc_init(mpi_comm(), 1, 1, C_NULL, T8code.Libt8.SC_LP_ERROR) + # Initialize the sc library, has to happen before we initialize t8code. + T8code.Libt8.sc_init(mpi_comm(), 1, 1, C_NULL, T8code.Libt8.SC_LP_ERROR) - # Initialize `p4est` with log level ERROR to prevent a lot of output in AMR simulations - T8code.Libt8.p4est_init(C_NULL, SC_LP_ERROR) - # Initialize t8code with log level ERROR to prevent a lot of output in AMR simulations. - t8_init(T8code.Libt8.SC_LP_ERROR) + # Initialize `p4est` with log level ERROR to prevent a lot of output in AMR simulations + T8code.Libt8.p4est_init(C_NULL, SC_LP_ERROR) + # Initialize t8code with log level ERROR to prevent a lot of output in AMR simulations. + t8_init(T8code.Libt8.SC_LP_ERROR) - return nothing + return nothing end function trixi_t8_unref_forest(forest) - t8_forest_unref(Ref(forest)) + t8_forest_unref(Ref(forest)) end function t8_free(ptr) - T8code.Libt8.sc_free(t8_get_package_id(), ptr) + T8code.Libt8.sc_free(t8_get_package_id(), ptr) end function trixi_t8_count_interfaces(forest) - # Check that forest is a committed, that is valid and usable, forest. - @assert t8_forest_is_committed(forest) != 0 + # Check that forest is a committed, that is valid and usable, forest. + @assert t8_forest_is_committed(forest) != 0 - # Get the number of local elements of forest. - num_local_elements = t8_forest_get_local_num_elements(forest) - # Get the number of ghost elements of forest. - num_ghost_elements = t8_forest_get_num_ghosts(forest) - # Get the number of trees that have elements of this process. - num_local_trees = t8_forest_get_num_local_trees(forest) + # Get the number of local elements of forest. + num_local_elements = t8_forest_get_local_num_elements(forest) + # Get the number of ghost elements of forest. + num_ghost_elements = t8_forest_get_num_ghosts(forest) + # Get the number of trees that have elements of this process. + num_local_trees = t8_forest_get_num_local_trees(forest) - current_index = t8_locidx_t(0) + current_index = t8_locidx_t(0) - local_num_conform = 0 - local_num_mortars = 0 - local_num_boundary = 0 + local_num_conform = 0 + local_num_mortars = 0 + local_num_boundary = 0 - for itree = 0:num_local_trees-1 - tree_class = t8_forest_get_tree_class(forest, itree) - eclass_scheme = t8_forest_get_eclass_scheme(forest, tree_class) + for itree in 0:(num_local_trees - 1) + tree_class = t8_forest_get_tree_class(forest, itree) + eclass_scheme = t8_forest_get_eclass_scheme(forest, tree_class) - # Get the number of elements of this tree. - num_elements_in_tree = t8_forest_get_tree_num_elements(forest, itree) + # Get the number of elements of this tree. + num_elements_in_tree = t8_forest_get_tree_num_elements(forest, itree) - for ielement = 0:num_elements_in_tree-1 - element = t8_forest_get_element_in_tree(forest, itree, ielement) + for ielement in 0:(num_elements_in_tree - 1) + element = t8_forest_get_element_in_tree(forest, itree, ielement) - level = t8_element_level(eclass_scheme, element) + level = t8_element_level(eclass_scheme, element) - num_faces = t8_element_num_faces(eclass_scheme, element) + num_faces = t8_element_num_faces(eclass_scheme, element) - for iface = 0:num_faces-1 - pelement_indices_ref = Ref{Ptr{t8_locidx_t}}() - pneighbor_leafs_ref = Ref{Ptr{Ptr{t8_element}}}() - pneigh_scheme_ref = Ref{Ptr{t8_eclass_scheme}}() + for iface in 0:(num_faces - 1) + pelement_indices_ref = Ref{Ptr{t8_locidx_t}}() + pneighbor_leafs_ref = Ref{Ptr{Ptr{t8_element}}}() + pneigh_scheme_ref = Ref{Ptr{t8_eclass_scheme}}() - dual_faces_ref = Ref{Ptr{Cint}}() - num_neighbors_ref = Ref{Cint}() + dual_faces_ref = Ref{Ptr{Cint}}() + num_neighbors_ref = Ref{Cint}() - forest_is_balanced = Cint(1) + forest_is_balanced = Cint(1) - t8_forest_leaf_face_neighbors(forest, itree, element, - pneighbor_leafs_ref, iface, dual_faces_ref, num_neighbors_ref, - pelement_indices_ref, pneigh_scheme_ref, forest_is_balanced) + t8_forest_leaf_face_neighbors(forest, itree, element, + pneighbor_leafs_ref, iface, dual_faces_ref, + num_neighbors_ref, + pelement_indices_ref, pneigh_scheme_ref, + forest_is_balanced) - num_neighbors = num_neighbors_ref[] - neighbor_ielements = unsafe_wrap(Array, pelement_indices_ref[], num_neighbors) - neighbor_leafs = unsafe_wrap(Array, pneighbor_leafs_ref[], num_neighbors) - neighbor_scheme = pneigh_scheme_ref[] + num_neighbors = num_neighbors_ref[] + neighbor_ielements = unsafe_wrap(Array, pelement_indices_ref[], + num_neighbors) + neighbor_leafs = unsafe_wrap(Array, pneighbor_leafs_ref[], num_neighbors) + neighbor_scheme = pneigh_scheme_ref[] - if num_neighbors > 0 - neighbor_level = t8_element_level(neighbor_scheme, neighbor_leafs[1]) + if num_neighbors > 0 + neighbor_level = t8_element_level(neighbor_scheme, neighbor_leafs[1]) - # Conforming interface: The second condition ensures we only visit the interface once. - if level == neighbor_level && current_index <= neighbor_ielements[1] - local_num_conform += 1 - elseif level < neighbor_level - local_num_mortars += 1 - end + # Conforming interface: The second condition ensures we only visit the interface once. + if level == neighbor_level && current_index <= neighbor_ielements[1] + local_num_conform += 1 + elseif level < neighbor_level + local_num_mortars += 1 + end - else + else + local_num_boundary += 1 + end - local_num_boundary += 1 - end - - t8_free(dual_faces_ref[]) - t8_free(pneighbor_leafs_ref[]) - t8_free(pelement_indices_ref[]) + t8_free(dual_faces_ref[]) + t8_free(pneighbor_leafs_ref[]) + t8_free(pelement_indices_ref[]) + end # for - end # for - - current_index += 1 + current_index += 1 + end # for end # for - end # for - return (interfaces = local_num_conform, - mortars = local_num_mortars, - boundaries = local_num_boundary) + return (interfaces = local_num_conform, + mortars = local_num_mortars, + boundaries = local_num_boundary) end -function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundaries, boundary_names) - # Check that forest is a committed, that is valid and usable, forest. - @assert t8_forest_is_committed(forest) != 0 +function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundaries, + boundary_names) + # Check that forest is a committed, that is valid and usable, forest. + @assert t8_forest_is_committed(forest) != 0 - # Get the number of local elements of forest. - num_local_elements = t8_forest_get_local_num_elements(forest) - # Get the number of ghost elements of forest. - num_ghost_elements = t8_forest_get_num_ghosts(forest) - # Get the number of trees that have elements of this process. - num_local_trees = t8_forest_get_num_local_trees(forest) + # Get the number of local elements of forest. + num_local_elements = t8_forest_get_local_num_elements(forest) + # Get the number of ghost elements of forest. + num_ghost_elements = t8_forest_get_num_ghosts(forest) + # Get the number of trees that have elements of this process. + num_local_trees = t8_forest_get_num_local_trees(forest) - current_index = t8_locidx_t(0) + current_index = t8_locidx_t(0) - local_num_conform = 0 - local_num_mortars = 0 - local_num_boundary = 0 + local_num_conform = 0 + local_num_mortars = 0 + local_num_boundary = 0 - for itree = 0:num_local_trees-1 - tree_class = t8_forest_get_tree_class(forest, itree) - eclass_scheme = t8_forest_get_eclass_scheme(forest, tree_class) + for itree in 0:(num_local_trees - 1) + tree_class = t8_forest_get_tree_class(forest, itree) + eclass_scheme = t8_forest_get_eclass_scheme(forest, tree_class) - # Get the number of elements of this tree. - num_elements_in_tree = t8_forest_get_tree_num_elements(forest, itree) + # Get the number of elements of this tree. + num_elements_in_tree = t8_forest_get_tree_num_elements(forest, itree) - for ielement = 0:num_elements_in_tree-1 - element = t8_forest_get_element_in_tree(forest, itree, ielement) + for ielement in 0:(num_elements_in_tree - 1) + element = t8_forest_get_element_in_tree(forest, itree, ielement) - level = t8_element_level(eclass_scheme, element) + level = t8_element_level(eclass_scheme, element) - num_faces = t8_element_num_faces(eclass_scheme,element) + num_faces = t8_element_num_faces(eclass_scheme, element) - for iface = 0:num_faces-1 + for iface in 0:(num_faces - 1) - # Compute the `orientation` of the touching faces. - if t8_element_is_root_boundary(eclass_scheme, element, iface) == 1 - cmesh = t8_forest_get_cmesh(forest) - itree_in_cmesh = t8_forest_ltreeid_to_cmesh_ltreeid(forest, itree) - iface_in_tree = t8_element_tree_face(eclass_scheme, element, iface) - orientation_ref = Ref{Cint}() + # Compute the `orientation` of the touching faces. + if t8_element_is_root_boundary(eclass_scheme, element, iface) == 1 + cmesh = t8_forest_get_cmesh(forest) + itree_in_cmesh = t8_forest_ltreeid_to_cmesh_ltreeid(forest, itree) + iface_in_tree = t8_element_tree_face(eclass_scheme, element, iface) + orientation_ref = Ref{Cint}() - t8_cmesh_get_face_neighbor(cmesh, itree_in_cmesh, iface_in_tree, C_NULL, orientation_ref) - orientation = orientation_ref[] - else - orientation = zero(Cint) - end + t8_cmesh_get_face_neighbor(cmesh, itree_in_cmesh, iface_in_tree, C_NULL, + orientation_ref) + orientation = orientation_ref[] + else + orientation = zero(Cint) + end - pelement_indices_ref = Ref{Ptr{t8_locidx_t}}() - pneighbor_leafs_ref = Ref{Ptr{Ptr{t8_element}}}() - pneigh_scheme_ref = Ref{Ptr{t8_eclass_scheme}}() + pelement_indices_ref = Ref{Ptr{t8_locidx_t}}() + pneighbor_leafs_ref = Ref{Ptr{Ptr{t8_element}}}() + pneigh_scheme_ref = Ref{Ptr{t8_eclass_scheme}}() - dual_faces_ref = Ref{Ptr{Cint}}() - num_neighbors_ref = Ref{Cint}() + dual_faces_ref = Ref{Ptr{Cint}}() + num_neighbors_ref = Ref{Cint}() - forest_is_balanced = Cint(1) + forest_is_balanced = Cint(1) - t8_forest_leaf_face_neighbors(forest,itree,element, - pneighbor_leafs_ref, iface, dual_faces_ref, num_neighbors_ref, - pelement_indices_ref, pneigh_scheme_ref, forest_is_balanced) + t8_forest_leaf_face_neighbors(forest, itree, element, + pneighbor_leafs_ref, iface, dual_faces_ref, + num_neighbors_ref, + pelement_indices_ref, pneigh_scheme_ref, + forest_is_balanced) - num_neighbors = num_neighbors_ref[] - dual_faces = unsafe_wrap(Array, dual_faces_ref[], num_neighbors) - neighbor_ielements = unsafe_wrap(Array, pelement_indices_ref[], num_neighbors) - neighbor_leafs = unsafe_wrap(Array, pneighbor_leafs_ref[], num_neighbors) - neighbor_scheme = pneigh_scheme_ref[] + num_neighbors = num_neighbors_ref[] + dual_faces = unsafe_wrap(Array, dual_faces_ref[], num_neighbors) + neighbor_ielements = unsafe_wrap(Array, pelement_indices_ref[], + num_neighbors) + neighbor_leafs = unsafe_wrap(Array, pneighbor_leafs_ref[], num_neighbors) + neighbor_scheme = pneigh_scheme_ref[] - if num_neighbors > 0 - neighbor_level = t8_element_level(neighbor_scheme, neighbor_leafs[1]) + if num_neighbors > 0 + neighbor_level = t8_element_level(neighbor_scheme, neighbor_leafs[1]) - # Conforming interface: The second condition ensures we only visit the interface once. - if level == neighbor_level && current_index <= neighbor_ielements[1] - local_num_conform += 1 + # Conforming interface: The second condition ensures we only visit the interface once. + if level == neighbor_level && current_index <= neighbor_ielements[1] + local_num_conform += 1 - faces = (iface, dual_faces[1]) - interface_id = local_num_conform + faces = (iface, dual_faces[1]) + interface_id = local_num_conform - # Write data to interfaces container. - interfaces.neighbor_ids[1, interface_id] = current_index + 1 - interfaces.neighbor_ids[2, interface_id] = neighbor_ielements[1] + 1 + # Write data to interfaces container. + interfaces.neighbor_ids[1, interface_id] = current_index + 1 + interfaces.neighbor_ids[2, interface_id] = neighbor_ielements[1] + 1 - # Save interfaces.node_indices dimension specific in containers_3d.jl. - init_interface_node_indices!(interfaces, faces, orientation, interface_id) + # Save interfaces.node_indices dimension specific in containers_3d.jl. + init_interface_node_indices!(interfaces, faces, orientation, + interface_id) - # Non-conforming interface. - elseif level < neighbor_level - local_num_mortars += 1 + # Non-conforming interface. + elseif level < neighbor_level + local_num_mortars += 1 - faces = (dual_faces[1],iface) + faces = (dual_faces[1], iface) - mortar_id = local_num_mortars + mortar_id = local_num_mortars - # Last entry is the large element. - mortars.neighbor_ids[end, mortar_id] = current_index + 1 + # Last entry is the large element. + mortars.neighbor_ids[end, mortar_id] = current_index + 1 - # Fill in the `mortars.neighbor_ids` array and reorder if necessary. - trixi_t8_init_mortar_neighbor_ids!(mortars, faces[2], faces[1], orientation, neighbor_ielements, mortar_id) - - # Fill in the `mortars.node_indices` array. - init_mortar_node_indices!(mortars, faces, orientation, mortar_id) + # Fill in the `mortars.neighbor_ids` array and reorder if necessary. + trixi_t8_init_mortar_neighbor_ids!(mortars, faces[2], faces[1], + orientation, neighbor_ielements, + mortar_id) - # else: "level > neighbor_level" is skipped since we visit the mortar interface only once. - end + # Fill in the `mortars.node_indices` array. + init_mortar_node_indices!(mortars, faces, orientation, mortar_id) - # Domain boundary. - else - local_num_boundary += 1 - boundary_id = local_num_boundary + # else: "level > neighbor_level" is skipped since we visit the mortar interface only once. + end - boundaries.neighbor_ids[boundary_id] = current_index + 1 + # Domain boundary. + else + local_num_boundary += 1 + boundary_id = local_num_boundary - init_boundary_node_indices!(boundaries, iface, boundary_id) + boundaries.neighbor_ids[boundary_id] = current_index + 1 - # One-based indexing. - boundaries.name[boundary_id] = boundary_names[iface + 1, itree + 1] - end - - t8_free(dual_faces_ref[]) - t8_free(pneighbor_leafs_ref[]) - t8_free(pelement_indices_ref[]) + init_boundary_node_indices!(boundaries, iface, boundary_id) + + # One-based indexing. + boundaries.name[boundary_id] = boundary_names[iface + 1, itree + 1] + end - end # for iface = ... + t8_free(dual_faces_ref[]) + t8_free(pneighbor_leafs_ref[]) + t8_free(pelement_indices_ref[]) + end # for iface = ... - current_index += 1 + current_index += 1 + end # for end # for - end # for - return (interfaces = local_num_conform, - mortars = local_num_mortars, - boundaries = local_num_boundary) + return (interfaces = local_num_conform, + mortars = local_num_mortars, + boundaries = local_num_boundary) end function trixi_t8_get_local_element_levels(forest) - # Check that forest is a committed, that is valid and usable, forest. - @assert t8_forest_is_committed(forest) != 0 + # Check that forest is a committed, that is valid and usable, forest. + @assert t8_forest_is_committed(forest) != 0 - levels = Vector{Int}(undef, t8_forest_get_local_num_elements(forest)) + levels = Vector{Int}(undef, t8_forest_get_local_num_elements(forest)) - # Get the number of trees that have elements of this process. - num_local_trees = t8_forest_get_num_local_trees(forest) + # Get the number of trees that have elements of this process. + num_local_trees = t8_forest_get_num_local_trees(forest) - current_index = 0 + current_index = 0 - for itree = 0:num_local_trees-1 - tree_class = t8_forest_get_tree_class(forest, itree) - eclass_scheme = t8_forest_get_eclass_scheme(forest, tree_class) + for itree in 0:(num_local_trees - 1) + tree_class = t8_forest_get_tree_class(forest, itree) + eclass_scheme = t8_forest_get_eclass_scheme(forest, tree_class) - # Get the number of elements of this tree. - num_elements_in_tree = t8_forest_get_tree_num_elements(forest, itree) + # Get the number of elements of this tree. + num_elements_in_tree = t8_forest_get_tree_num_elements(forest, itree) - for ielement = 0:num_elements_in_tree-1 - element = t8_forest_get_element_in_tree(forest, itree, ielement) - current_index += 1 - levels[current_index] = t8_element_level(eclass_scheme, element) + for ielement in 0:(num_elements_in_tree - 1) + element = t8_forest_get_element_in_tree(forest, itree, ielement) + current_index += 1 + levels[current_index] = t8_element_level(eclass_scheme, element) + end # for end # for - end # for - return levels + return levels end # Callback function prototype to decide for refining and coarsening. @@ -293,103 +301,102 @@ function adapt_callback(forest, which_tree, lelement_id, ts, - is_family, + is_family, num_elements, - elements) :: Cint - - num_levels = t8_forest_get_local_num_elements(forest_from) + elements)::Cint + num_levels = t8_forest_get_local_num_elements(forest_from) - indicator_ptr = Ptr{Int}(t8_forest_get_user_data(forest)) - indicators = unsafe_wrap(Array,indicator_ptr,num_levels) + indicator_ptr = Ptr{Int}(t8_forest_get_user_data(forest)) + indicators = unsafe_wrap(Array, indicator_ptr, num_levels) - offset = t8_forest_get_tree_element_offset(forest_from, which_tree) + offset = t8_forest_get_tree_element_offset(forest_from, which_tree) - # Only allow coarsening for complete families. - if indicators[offset + lelement_id + 1] < 0 && is_family == 0 - return Cint(0) - end + # Only allow coarsening for complete families. + if indicators[offset + lelement_id + 1] < 0 && is_family == 0 + return Cint(0) + end - return Cint(indicators[offset + lelement_id + 1]) + return Cint(indicators[offset + lelement_id + 1]) end function trixi_t8_adapt_new(old_forest, indicators) - # Check that forest is a committed, that is valid and usable, forest. - @assert t8_forest_is_committed(old_forest) != 0 - - # Init new forest. - new_forest_ref = Ref{t8_forest_t}() - t8_forest_init(new_forest_ref) - new_forest = new_forest_ref[] - - let set_from = C_NULL, recursive = 0, set_for_coarsening = 0, no_repartition = 0, do_ghost = 1 - t8_forest_set_user_data(new_forest, pointer(indicators)) - t8_forest_set_adapt(new_forest, old_forest, @t8_adapt_callback(adapt_callback), recursive) - t8_forest_set_balance(new_forest, set_from, no_repartition) - t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - t8_forest_set_ghost(new_forest, do_ghost, T8_GHOST_FACES) # Note: MPI support not available yet so it is a dummy call. - t8_forest_commit(new_forest) - end - - return new_forest + # Check that forest is a committed, that is valid and usable, forest. + @assert t8_forest_is_committed(old_forest) != 0 + + # Init new forest. + new_forest_ref = Ref{t8_forest_t}() + t8_forest_init(new_forest_ref) + new_forest = new_forest_ref[] + + let set_from = C_NULL, recursive = 0, set_for_coarsening = 0, no_repartition = 0, + do_ghost = 1 + + t8_forest_set_user_data(new_forest, pointer(indicators)) + t8_forest_set_adapt(new_forest, old_forest, @t8_adapt_callback(adapt_callback), + recursive) + t8_forest_set_balance(new_forest, set_from, no_repartition) + t8_forest_set_partition(new_forest, set_from, set_for_coarsening) + t8_forest_set_ghost(new_forest, do_ghost, T8_GHOST_FACES) # Note: MPI support not available yet so it is a dummy call. + t8_forest_commit(new_forest) + end + + return new_forest end function trixi_t8_get_difference(old_levels, new_levels, num_children) + old_nelems = length(old_levels) + new_nelems = length(new_levels) - old_nelems = length(old_levels) - new_nelems = length(new_levels) + changes = Vector{Int}(undef, old_nelems) - changes = Vector{Int}(undef, old_nelems) + # Local element indices. + old_index = 1 + new_index = 1 - # Local element indices. - old_index = 1 - new_index = 1 + while old_index <= old_nelems && new_index <= new_nelems + if old_levels[old_index] < new_levels[new_index] + # Refined. - while old_index <= old_nelems && new_index <= new_nelems + changes[old_index] = 1 - if old_levels[old_index] < new_levels[new_index] - # Refined. - - changes[old_index] = 1 + old_index += 1 + new_index += num_children - old_index += 1 - new_index += num_children + elseif old_levels[old_index] > new_levels[new_index] + # Coarsend. - elseif old_levels[old_index] > new_levels[new_index] - # Coarsend. + for child_index in old_index:(old_index + num_children - 1) + changes[child_index] = -1 + end - for child_index = old_index:old_index+num_children-1 - changes[child_index] = -1 - end + old_index += num_children + new_index += 1 - old_index += num_children - new_index += 1 + else + # No changes. - else - # No changes. - - changes[old_index] = 0 + changes[old_index] = 0 - old_index += 1 - new_index += 1 + old_index += 1 + new_index += 1 + end end - end - return changes + return changes end # Coarsen or refine marked cells and rebalance forest. Return a difference between # old and new mesh. function trixi_t8_adapt!(mesh, indicators) + old_levels = trixi_t8_get_local_element_levels(mesh.forest) - old_levels = trixi_t8_get_local_element_levels(mesh.forest) - - forest_cached = trixi_t8_adapt_new(mesh.forest, indicators) + forest_cached = trixi_t8_adapt_new(mesh.forest, indicators) - new_levels = trixi_t8_get_local_element_levels(forest_cached) + new_levels = trixi_t8_get_local_element_levels(forest_cached) - differences = trixi_t8_get_difference(old_levels, new_levels, 2^ndims(mesh)) + differences = trixi_t8_get_difference(old_levels, new_levels, 2^ndims(mesh)) - mesh.forest = forest_cached + mesh.forest = forest_cached - return differences + return differences end diff --git a/src/callbacks_step/amr.jl b/src/callbacks_step/amr.jl index 8a85b279ba..5820beceb3 100644 --- a/src/callbacks_step/amr.jl +++ b/src/callbacks_step/amr.jl @@ -474,61 +474,60 @@ end function (amr_callback::AMRCallback)(u_ode::AbstractVector, mesh::T8codeMesh, equations, dg::DG, cache, semi, t, iter; - only_refine=false, only_coarsen=false, - passive_args=()) - - has_changed = false - - @unpack controller, adaptor = amr_callback + only_refine = false, only_coarsen = false, + passive_args = ()) + has_changed = false - u = wrap_array(u_ode, mesh, equations, dg, cache) - indicators = @trixi_timeit timer() "indicator" controller(u, mesh, equations, dg, cache, t=t, iter=iter) + @unpack controller, adaptor = amr_callback - if only_coarsen - indicators[indicators .> 0] .= 0 - end + u = wrap_array(u_ode, mesh, equations, dg, cache) + indicators = @trixi_timeit timer() "indicator" controller(u, mesh, equations, dg, + cache, t = t, iter = iter) - if only_refine - indicators[indicators .< 0] .= 0 - end + if only_coarsen + indicators[indicators .> 0] .= 0 + end - @boundscheck begin - @assert axes(indicators) == (Base.OneTo(ncells(mesh)),) ( - "Indicator array (axes = $(axes(indicators))) and mesh cells (axes = $(Base.OneTo(ncells(mesh)))) have different axes" - ) - end + if only_refine + indicators[indicators .< 0] .= 0 + end - @trixi_timeit timer() "adapt" begin - difference = @trixi_timeit timer() "mesh" trixi_t8_adapt!(mesh,indicators) + @boundscheck begin + @assert axes(indicators)==(Base.OneTo(ncells(mesh)),) ("Indicator array (axes = $(axes(indicators))) and mesh cells (axes = $(Base.OneTo(ncells(mesh)))) have different axes") + end - @trixi_timeit timer() "solver" adapt!(u_ode, adaptor, mesh, equations, dg, cache, difference) - end + @trixi_timeit timer() "adapt" begin + difference = @trixi_timeit timer() "mesh" trixi_t8_adapt!(mesh, indicators) - # Store whether there were any cells coarsened or refined and perform load balancing. - has_changed = any(difference .!= 0) + @trixi_timeit timer() "solver" adapt!(u_ode, adaptor, mesh, equations, dg, + cache, difference) + end - # TODO: T8codeMesh for MPI not implemented yet. - # Check if mesh changed on other processes - # if mpi_isparallel() - # has_changed = MPI.Allreduce!(Ref(has_changed), |, mpi_comm())[] - # end + # Store whether there were any cells coarsened or refined and perform load balancing. + has_changed = any(difference .!= 0) - if has_changed # TODO: T8codeMesh for MPI not implemented yet. - # if mpi_isparallel() && amr_callback.dynamic_load_balancing - # @trixi_timeit timer() "dynamic load balancing" begin - # global_first_quadrant = unsafe_wrap(Array, mesh.p4est.global_first_quadrant, mpi_nranks() + 1) - # old_global_first_quadrant = copy(global_first_quadrant) - # partition!(mesh) - # rebalance_solver!(u_ode, mesh, equations, dg, cache, old_global_first_quadrant) - # end + # Check if mesh changed on other processes + # if mpi_isparallel() + # has_changed = MPI.Allreduce!(Ref(has_changed), |, mpi_comm())[] # end - reinitialize_boundaries!(semi.boundary_conditions, cache) - end + if has_changed + # TODO: T8codeMesh for MPI not implemented yet. + # if mpi_isparallel() && amr_callback.dynamic_load_balancing + # @trixi_timeit timer() "dynamic load balancing" begin + # global_first_quadrant = unsafe_wrap(Array, mesh.p4est.global_first_quadrant, mpi_nranks() + 1) + # old_global_first_quadrant = copy(global_first_quadrant) + # partition!(mesh) + # rebalance_solver!(u_ode, mesh, equations, dg, cache, old_global_first_quadrant) + # end + # end - # Return true if there were any cells coarsened or refined, otherwise false. - return has_changed + reinitialize_boundaries!(semi.boundary_conditions, cache) + end + + # Return true if there were any cells coarsened or refined, otherwise false. + return has_changed end function reinitialize_boundaries!(boundary_conditions::UnstructuredSortedBoundaryTypes, @@ -700,7 +699,7 @@ function current_element_levels(mesh::P4estMesh, solver, cache) end function current_element_levels(mesh::T8codeMesh, solver, cache) - return trixi_t8_get_local_element_levels(mesh.forest) + return trixi_t8_get_local_element_levels(mesh.forest) end # TODO: Taal refactor, merge the two loops of ControllerThreeLevel and IndicatorLöhner etc.? diff --git a/src/callbacks_step/amr_dg2d.jl b/src/callbacks_step/amr_dg2d.jl index 0cecad069d..6c077116d1 100644 --- a/src/callbacks_step/amr_dg2d.jl +++ b/src/callbacks_step/amr_dg2d.jl @@ -335,78 +335,79 @@ end # this method is called when an `ControllerThreeLevel` is constructed function create_cache(::Type{ControllerThreeLevel}, - mesh::Union{TreeMesh{2}, P4estMesh{2}, T8codeMesh{2}}, equations, dg::DG, cache) + mesh::Union{TreeMesh{2}, P4estMesh{2}, T8codeMesh{2}}, equations, + dg::DG, cache) controller_value = Vector{Int}(undef, nelements(dg, cache)) return (; controller_value) end # Coarsen and refine elements in the DG solver based on a difference list. -function adapt!(u_ode::AbstractVector, adaptor, mesh::T8codeMesh{2}, equations, dg::DGSEM, cache, difference) +function adapt!(u_ode::AbstractVector, adaptor, mesh::T8codeMesh{2}, equations, + dg::DGSEM, cache, difference) - # Return early if there is nothing to do. - if !any(difference .!= 0) - return nothing - end - - # Number of (local) cells/elements. - old_nelems = nelements(dg, cache) - new_nelems = ncells(mesh) - - # Local element indices. - old_index = 1 - new_index = 1 - - # Note: This is true for `quads`. - T8_CHILDREN = 4 + # Return early if there is nothing to do. + if !any(difference .!= 0) + return nothing + end - # Retain current solution data. - old_u_ode = copy(u_ode) + # Number of (local) cells/elements. + old_nelems = nelements(dg, cache) + new_nelems = ncells(mesh) - GC.@preserve old_u_ode begin - old_u = wrap_array(old_u_ode, mesh, equations, dg, cache) + # Local element indices. + old_index = 1 + new_index = 1 - reinitialize_containers!(mesh, equations, dg, cache) + # Note: This is true for `quads`. + T8_CHILDREN = 4 - resize!(u_ode, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) - u = wrap_array(u_ode, mesh, equations, dg, cache) + # Retain current solution data. + old_u_ode = copy(u_ode) - while old_index <= old_nelems && new_index <= new_nelems + GC.@preserve old_u_ode begin + old_u = wrap_array(old_u_ode, mesh, equations, dg, cache) - if difference[old_index] > 0 # Refine. + reinitialize_containers!(mesh, equations, dg, cache) - # Refine element and store solution directly in new data structure. - refine_element!(u, new_index, old_u, old_index, adaptor, equations, dg) + resize!(u_ode, + nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) + u = wrap_array(u_ode, mesh, equations, dg, cache) - old_index += 1 - new_index += T8_CHILDREN + while old_index <= old_nelems && new_index <= new_nelems + if difference[old_index] > 0 # Refine. - elseif difference[old_index] < 0 # Coarsen. + # Refine element and store solution directly in new data structure. + refine_element!(u, new_index, old_u, old_index, adaptor, equations, dg) - # If an element is to be removed, sanity check if the following elements - # are also marked - otherwise there would be an error in the way the - # cells/elements are sorted. - @assert all(difference[old_index:(old_index+T8_CHILDREN-1)] .< 0) "bad cell/element order" + old_index += 1 + new_index += T8_CHILDREN - # Coarsen elements and store solution directly in new data structure. - coarsen_elements!(u, new_index, old_u, old_index, adaptor, equations, dg) + elseif difference[old_index] < 0 # Coarsen. - old_index += T8_CHILDREN - new_index += 1 + # If an element is to be removed, sanity check if the following elements + # are also marked - otherwise there would be an error in the way the + # cells/elements are sorted. + @assert all(difference[old_index:(old_index + T8_CHILDREN - 1)] .< 0) "bad cell/element order" - else # No changes. + # Coarsen elements and store solution directly in new data structure. + coarsen_elements!(u, new_index, old_u, old_index, adaptor, equations, + dg) - # Copy old element data to new element container. - @views u[:, .., new_index] .= old_u[:, .., old_index] + old_index += T8_CHILDREN + new_index += 1 - old_index += 1 - new_index += 1 - end + else # No changes. - end # while + # Copy old element data to new element container. + @views u[:, .., new_index] .= old_u[:, .., old_index] - end # GC.@preserve old_u_ode + old_index += 1 + new_index += 1 + end + end # while + end # GC.@preserve old_u_ode - return nothing + return nothing end function create_cache(::Type{ControllerThreeLevelCombined}, mesh::TreeMesh{2}, @@ -414,5 +415,4 @@ function create_cache(::Type{ControllerThreeLevelCombined}, mesh::TreeMesh{2}, controller_value = Vector{Int}(undef, nelements(dg, cache)) return (; controller_value) end - end # @muladd diff --git a/src/callbacks_step/amr_dg3d.jl b/src/callbacks_step/amr_dg3d.jl index a06fc0483d..52baa58c8b 100644 --- a/src/callbacks_step/amr_dg3d.jl +++ b/src/callbacks_step/amr_dg3d.jl @@ -311,75 +311,77 @@ function create_cache(::Type{ControllerThreeLevel}, end # Coarsen and refine elements in the DG solver based on a difference list. -function adapt!(u_ode::AbstractVector, adaptor, mesh::T8codeMesh{3}, equations, dg::DGSEM, cache, difference) +function adapt!(u_ode::AbstractVector, adaptor, mesh::T8codeMesh{3}, equations, + dg::DGSEM, cache, difference) - # Return early if there is nothing to do. - if !any(difference .!= 0) - return nothing - end - - # Number of (local) cells/elements. - old_nelems = nelements(dg, cache) - new_nelems = ncells(mesh) - - # Local element indices. - old_index = 1 - new_index = 1 - - # Note: This is true for `hexs`. - T8_CHILDREN = 8 + # Return early if there is nothing to do. + if !any(difference .!= 0) + return nothing + end - # Retain current solution data. - old_u_ode = copy(u_ode) + # Number of (local) cells/elements. + old_nelems = nelements(dg, cache) + new_nelems = ncells(mesh) - GC.@preserve old_u_ode begin - old_u = wrap_array(old_u_ode, mesh, equations, dg, cache) + # Local element indices. + old_index = 1 + new_index = 1 - reinitialize_containers!(mesh, equations, dg, cache) + # Note: This is true for `hexs`. + T8_CHILDREN = 8 - resize!(u_ode, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) - u = wrap_array(u_ode, mesh, equations, dg, cache) + # Retain current solution data. + old_u_ode = copy(u_ode) - u_tmp1 = Array{eltype(u), 4}(undef, nvariables(equations), nnodes(dg), nnodes(dg), nnodes(dg)) - u_tmp2 = Array{eltype(u), 4}(undef, nvariables(equations), nnodes(dg), nnodes(dg), nnodes(dg)) + GC.@preserve old_u_ode begin + old_u = wrap_array(old_u_ode, mesh, equations, dg, cache) - while old_index <= old_nelems && new_index <= new_nelems + reinitialize_containers!(mesh, equations, dg, cache) - if difference[old_index] > 0 # Refine. + resize!(u_ode, + nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) + u = wrap_array(u_ode, mesh, equations, dg, cache) - # Refine element and store solution directly in new data structure. - refine_element!(u, new_index, old_u, old_index, adaptor, equations, dg, u_tmp1, u_tmp2) + u_tmp1 = Array{eltype(u), 4}(undef, nvariables(equations), nnodes(dg), + nnodes(dg), nnodes(dg)) + u_tmp2 = Array{eltype(u), 4}(undef, nvariables(equations), nnodes(dg), + nnodes(dg), nnodes(dg)) - old_index += 1 - new_index += T8_CHILDREN + while old_index <= old_nelems && new_index <= new_nelems + if difference[old_index] > 0 # Refine. - elseif difference[old_index] < 0 # Coarsen. + # Refine element and store solution directly in new data structure. + refine_element!(u, new_index, old_u, old_index, adaptor, equations, dg, + u_tmp1, u_tmp2) - # If an element is to be removed, sanity check if the following elements - # are also marked - otherwise there would be an error in the way the - # cells/elements are sorted. - @assert all(difference[old_index:(old_index+T8_CHILDREN-1)] .< 0) "bad cell/element order" + old_index += 1 + new_index += T8_CHILDREN - # Coarsen elements and store solution directly in new data structure. - coarsen_elements!(u, new_index, old_u, old_index, adaptor, equations, dg, u_tmp1, u_tmp2) + elseif difference[old_index] < 0 # Coarsen. - old_index += T8_CHILDREN - new_index += 1 + # If an element is to be removed, sanity check if the following elements + # are also marked - otherwise there would be an error in the way the + # cells/elements are sorted. + @assert all(difference[old_index:(old_index + T8_CHILDREN - 1)] .< 0) "bad cell/element order" - else # No changes. + # Coarsen elements and store solution directly in new data structure. + coarsen_elements!(u, new_index, old_u, old_index, adaptor, equations, + dg, u_tmp1, u_tmp2) - # Copy old element data to new element container. - @views u[:, .., new_index] .= old_u[:, .., old_index] + old_index += T8_CHILDREN + new_index += 1 - old_index += 1 - new_index += 1 - end + else # No changes. - end # while + # Copy old element data to new element container. + @views u[:, .., new_index] .= old_u[:, .., old_index] - end # GC.@preserve old_u_ode + old_index += 1 + new_index += 1 + end + end # while + end # GC.@preserve old_u_ode - return nothing + return nothing end - end # @muladd diff --git a/src/callbacks_step/analysis.jl b/src/callbacks_step/analysis.jl index 79b24626d5..b6176945d1 100644 --- a/src/callbacks_step/analysis.jl +++ b/src/callbacks_step/analysis.jl @@ -537,29 +537,31 @@ end # Print level information only if AMR is enabled function print_amr_information(callbacks, mesh::T8codeMesh, solver, cache) - # Return early if there is nothing to print - uses_amr(callbacks) || return nothing + # Return early if there is nothing to print + uses_amr(callbacks) || return nothing - # TODO: Switch to global element levels array when MPI supported or find - # another solution. - levels = trixi_t8_get_local_element_levels(mesh.forest) + # TODO: Switch to global element levels array when MPI supported or find + # another solution. + levels = trixi_t8_get_local_element_levels(mesh.forest) - min_level = minimum(levels) - max_level = maximum(levels) + min_level = minimum(levels) + max_level = maximum(levels) - mpi_println(" minlevel = $min_level") - mpi_println(" maxlevel = $max_level") + mpi_println(" minlevel = $min_level") + mpi_println(" maxlevel = $max_level") - if min_level > 0 - elements_per_level = [count(==(l), levels) for l in 1:max_level] + if min_level > 0 + elements_per_level = [count(==(l), levels) for l in 1:max_level] - for level = max_level:-1:min_level+1 - mpi_println(" ├── level $level: " * @sprintf("% 14d", elements_per_level[level])) + for level in max_level:-1:(min_level + 1) + mpi_println(" ├── level $level: " * + @sprintf("% 14d", elements_per_level[level])) + end + mpi_println(" └── level $min_level: " * + @sprintf("% 14d", elements_per_level[min_level])) end - mpi_println(" └── level $min_level: " * @sprintf("% 14d", elements_per_level[min_level])) - end - return nothing + return nothing end # Iterate over tuples of analysis integrals in a type-stable way using "lispy tuple programming". diff --git a/src/callbacks_step/analysis_dg2d.jl b/src/callbacks_step/analysis_dg2d.jl index 6b4931474c..4e456f7987 100644 --- a/src/callbacks_step/analysis_dg2d.jl +++ b/src/callbacks_step/analysis_dg2d.jl @@ -175,7 +175,7 @@ function integrate_via_indices(func::Func, u, end function integrate_via_indices(func::Func, u, - mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, + mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, equations, dg::DGSEM, cache, args...; normalize = true) where {Func} @unpack weights = dg.basis @@ -259,7 +259,8 @@ function analyze(::Val{:l2_divb}, du, u, t, end function analyze(::Val{:l2_divb}, du, u, t, - mesh::Union{StructuredMesh{2},UnstructuredMesh2D,P4estMesh{2}, T8codeMesh{2}}, + mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, + T8codeMesh{2}}, equations::IdealGlmMhdEquations2D, dg::DGSEM, cache) @unpack contravariant_vectors = cache.elements integrate_via_indices(u, mesh, equations, dg, cache, cache, @@ -326,7 +327,8 @@ function analyze(::Val{:linf_divb}, du, u, t, end function analyze(::Val{:linf_divb}, du, u, t, - mesh::Union{StructuredMesh{2},UnstructuredMesh2D,P4estMesh{2}, T8codeMesh{2}}, + mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, + T8codeMesh{2}}, equations::IdealGlmMhdEquations2D, dg::DGSEM, cache) @unpack derivative_matrix, weights = dg.basis @unpack contravariant_vectors = cache.elements diff --git a/src/callbacks_step/analysis_dg3d.jl b/src/callbacks_step/analysis_dg3d.jl index 534136ca75..d51eedf825 100644 --- a/src/callbacks_step/analysis_dg3d.jl +++ b/src/callbacks_step/analysis_dg3d.jl @@ -35,7 +35,9 @@ function create_cache_analysis(analyzer, mesh::TreeMesh{3}, return (; u_local, u_tmp1, u_tmp2, x_local, x_tmp1, x_tmp2) end -function create_cache_analysis(analyzer, mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, +function create_cache_analysis(analyzer, + mesh::Union{StructuredMesh{3}, P4estMesh{3}, + T8codeMesh{3}}, equations, dg::DG, cache, RealT, uEltype) @@ -190,7 +192,8 @@ function integrate_via_indices(func::Func, u, end function integrate_via_indices(func::Func, u, - mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, + mesh::Union{StructuredMesh{3}, P4estMesh{3}, + T8codeMesh{3}}, equations, dg::DGSEM, cache, args...; normalize = true) where {Func} @unpack weights = dg.basis @@ -218,12 +221,14 @@ function integrate_via_indices(func::Func, u, end function integrate(func::Func, u, - mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, - equations, dg::DG, cache; normalize=true) where {Func} - integrate_via_indices(u, mesh, equations, dg, cache; normalize=normalize) do u, i, j, k, element, equations, dg - u_local = get_node_vars(u, equations, dg, i, j, k, element) - return func(u_local, equations) - end + mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, + T8codeMesh{3}}, + equations, dg::DG, cache; normalize = true) where {Func} + integrate_via_indices(u, mesh, equations, dg, cache; + normalize = normalize) do u, i, j, k, element, equations, dg + u_local = get_node_vars(u, equations, dg, i, j, k, element) + return func(u_local, equations) + end end function integrate(func::Func, u, @@ -247,7 +252,9 @@ function integrate(func::Func, u, end function analyze(::typeof(entropy_timederivative), du, u, t, - mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, + mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3 + } + }, equations, dg::DG, cache) # Calculate ∫(∂S/∂u ⋅ ∂u/∂t)dΩ integrate_via_indices(u, mesh, equations, dg, cache, @@ -276,7 +283,8 @@ function analyze(::Val{:l2_divb}, du, u, t, end function analyze(::Val{:l2_divb}, du, u, t, - mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, equations::IdealGlmMhdEquations3D, + mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, + equations::IdealGlmMhdEquations3D, dg::DGSEM, cache) @unpack contravariant_vectors = cache.elements integrate_via_indices(u, mesh, equations, dg, cache, cache, @@ -331,7 +339,8 @@ function analyze(::Val{:linf_divb}, du, u, t, end function analyze(::Val{:linf_divb}, du, u, t, - mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, equations::IdealGlmMhdEquations3D, + mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, + equations::IdealGlmMhdEquations3D, dg::DGSEM, cache) @unpack derivative_matrix, weights = dg.basis @unpack contravariant_vectors = cache.elements diff --git a/src/callbacks_step/save_restart_dg.jl b/src/callbacks_step/save_restart_dg.jl index 718fdb6c68..8db6db2d2b 100644 --- a/src/callbacks_step/save_restart_dg.jl +++ b/src/callbacks_step/save_restart_dg.jl @@ -7,7 +7,8 @@ function save_restart_file(u, time, dt, timestep, mesh::Union{SerialTreeMesh, StructuredMesh, - UnstructuredMesh2D, SerialP4estMesh, SerialT8codeMesh}, + UnstructuredMesh2D, SerialP4estMesh, + SerialT8codeMesh}, equations, dg::DG, cache, restart_callback) @unpack output_directory = restart_callback diff --git a/src/callbacks_step/save_solution_dg.jl b/src/callbacks_step/save_solution_dg.jl index 5eabebdf07..6d5004ff65 100644 --- a/src/callbacks_step/save_solution_dg.jl +++ b/src/callbacks_step/save_solution_dg.jl @@ -7,7 +7,8 @@ function save_solution_file(u, time, dt, timestep, mesh::Union{SerialTreeMesh, StructuredMesh, - UnstructuredMesh2D, SerialP4estMesh, SerialT8codeMesh}, + UnstructuredMesh2D, SerialP4estMesh, + SerialT8codeMesh}, equations, dg::DG, cache, solution_callback, element_variables = Dict{Symbol, Any}(); system = "") diff --git a/src/callbacks_step/stepsize_dg2d.jl b/src/callbacks_step/stepsize_dg2d.jl index 56b6059495..673c3ba6aa 100644 --- a/src/callbacks_step/stepsize_dg2d.jl +++ b/src/callbacks_step/stepsize_dg2d.jl @@ -75,7 +75,9 @@ function max_dt(u, t, mesh::ParallelTreeMesh{2}, return dt end -function max_dt(u, t, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, +function max_dt(u, t, + mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, + T8codeMesh{2}}, constant_speed::False, equations, dg::DG, cache) # to avoid a division by zero if the speed vanishes everywhere, # e.g. for steady-state linear advection @@ -109,7 +111,9 @@ function max_dt(u, t, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMe return 2 / (nnodes(dg) * max_scaled_speed) end -function max_dt(u, t, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, +function max_dt(u, t, + mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, + T8codeMesh{2}}, constant_speed::True, equations, dg::DG, cache) @unpack contravariant_vectors, inverse_jacobian = cache.elements diff --git a/src/meshes/mesh_io.jl b/src/meshes/mesh_io.jl index 042d35e704..b479bda0c4 100644 --- a/src/meshes/mesh_io.jl +++ b/src/meshes/mesh_io.jl @@ -6,8 +6,9 @@ #! format: noindent # Save current mesh with some context information as an HDF5 file. -function save_mesh_file(mesh::Union{TreeMesh, P4estMesh, T8codeMesh}, output_directory, timestep=0) - save_mesh_file(mesh, output_directory, timestep, mpi_parallel(mesh)) +function save_mesh_file(mesh::Union{TreeMesh, P4estMesh, T8codeMesh}, output_directory, + timestep = 0) + save_mesh_file(mesh, output_directory, timestep, mpi_parallel(mesh)) end function save_mesh_file(mesh::TreeMesh, output_directory, timestep, @@ -221,9 +222,9 @@ end # TODO: Implement this function as soon as there is support for this in `t8code`. function save_mesh_file(mesh::T8codeMesh, output_directory, timestep, mpi_parallel) - @warn "Mesh file output not supported yet for `T8codeMesh`." + @warn "Mesh file output not supported yet for `T8codeMesh`." - return joinpath(output_directory, "dummy_mesh.h5") + return joinpath(output_directory, "dummy_mesh.h5") end """ diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 855515c348..c117d7bb15 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -5,67 +5,71 @@ An unstructured curved mesh based on trees that uses the C library ['t8code'](https://github.com/DLR-AMR/t8code) to manage trees and mesh refinement. """ -mutable struct T8codeMesh{NDIMS, RealT<:Real, IsParallel, NDIMSP2, NNODES} <: AbstractMesh{NDIMS} - cmesh :: Ptr{t8_cmesh} # cpointer to coarse mesh - scheme :: Ptr{t8_eclass_scheme} # cpointer to element scheme - forest :: Ptr{t8_forest} # cpointer to forest - is_parallel :: IsParallel - - unsaved_changes ::Bool - - # This specifies the geometry interpolation for each tree. - tree_node_coordinates :: Array{RealT, NDIMSP2} # [dimension, i, j, k, tree] - - # Stores the quadrature nodes. - nodes :: SVector{NNODES, RealT} - - boundary_names :: Array{Symbol, 2} # [face direction, tree] - current_filename :: String - - ninterfaces :: Int - nmortars :: Int - nboundaries :: Int - - function T8codeMesh{NDIMS}(cmesh, scheme, forest, nodes) where NDIMS - # TODO: Implement MPI parallelization. - # if mpi_isparallel() - # if !T8code.uses_mpi() - # error("t8code library does not support MPI") - # end - # is_parallel = Val(true) - # else - # is_parallel = Val(false) - # end - is_parallel = False() - - mesh = new{NDIMS, Float64, typeof(is_parallel), NDIMS+2, length(nodes)}(cmesh, scheme, forest, is_parallel) - - # Destroy 't8code' structs when the mesh is garbage collected. - finalizer(function (mesh::T8codeMesh{NDIMS}) - trixi_t8_unref_forest(mesh.forest) - end, mesh) - - return mesh - end +mutable struct T8codeMesh{NDIMS, RealT <: Real, IsParallel, NDIMSP2, NNODES} <: + AbstractMesh{NDIMS} + cmesh :: Ptr{t8_cmesh} # cpointer to coarse mesh + scheme :: Ptr{t8_eclass_scheme} # cpointer to element scheme + forest :: Ptr{t8_forest} # cpointer to forest + is_parallel :: IsParallel + + unsaved_changes::Bool + + # This specifies the geometry interpolation for each tree. + tree_node_coordinates::Array{RealT, NDIMSP2} # [dimension, i, j, k, tree] + + # Stores the quadrature nodes. + nodes::SVector{NNODES, RealT} + + boundary_names :: Array{Symbol, 2} # [face direction, tree] + current_filename :: String + + ninterfaces :: Int + nmortars :: Int + nboundaries :: Int + + function T8codeMesh{NDIMS}(cmesh, scheme, forest, nodes) where {NDIMS} + # TODO: Implement MPI parallelization. + # if mpi_isparallel() + # if !T8code.uses_mpi() + # error("t8code library does not support MPI") + # end + # is_parallel = Val(true) + # else + # is_parallel = Val(false) + # end + is_parallel = False() + + mesh = new{NDIMS, Float64, typeof(is_parallel), NDIMS + 2, length(nodes)}(cmesh, + scheme, + forest, + is_parallel) + + # Destroy 't8code' structs when the mesh is garbage collected. + finalizer(function (mesh::T8codeMesh{NDIMS}) + trixi_t8_unref_forest(mesh.forest) + end, mesh) + + return mesh + end end -function T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, boundary_names, - current_filename) where NDIMS - - mesh = T8codeMesh{NDIMS}(cmesh, scheme, forest, nodes) +function T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, + boundary_names, + current_filename) where {NDIMS} + mesh = T8codeMesh{NDIMS}(cmesh, scheme, forest, nodes) - mesh.nodes = nodes - mesh.boundary_names = boundary_names - mesh.current_filename = current_filename - mesh.tree_node_coordinates = tree_node_coordinates + mesh.nodes = nodes + mesh.boundary_names = boundary_names + mesh.current_filename = current_filename + mesh.tree_node_coordinates = tree_node_coordinates - return mesh + return mesh end const SerialT8codeMesh{NDIMS} = T8codeMesh{NDIMS, <:Real, <:False} @inline mpi_parallel(mesh::SerialT8codeMesh) = False() -@inline Base.ndims(::T8codeMesh{NDIMS}) where NDIMS = NDIMS +@inline Base.ndims(::T8codeMesh{NDIMS}) where {NDIMS} = NDIMS @inline Base.real(::T8codeMesh{NDIMS, RealT}) where {NDIMS, RealT} = RealT @inline ntrees(mesh::T8codeMesh) = Int(t8_forest_get_num_local_trees(mesh.forest)) @@ -75,20 +79,22 @@ const SerialT8codeMesh{NDIMS} = T8codeMesh{NDIMS, <:Real, <:False} @inline nboundaries(mesh::T8codeMesh) = mesh.nboundaries function Base.show(io::IO, mesh::T8codeMesh) - print(io, "T8codeMesh{", ndims(mesh), ", ", real(mesh), "}") + print(io, "T8codeMesh{", ndims(mesh), ", ", real(mesh), "}") end -function Base.show(io::IO, :: MIME"text/plain", mesh::T8codeMesh) - if get(io, :compact, false) - show(io, mesh) - else - setup = [ - "#trees" => ntrees(mesh), - "current #cells" => ncells(mesh), - "polydeg" => length(mesh.nodes) - 1, - ] - summary_box(io, "T8codeMesh{" * string(ndims(mesh)) * ", " * string(real(mesh)) * "}", setup) - end +function Base.show(io::IO, ::MIME"text/plain", mesh::T8codeMesh) + if get(io, :compact, false) + show(io, mesh) + else + setup = [ + "#trees" => ntrees(mesh), + "current #cells" => ncells(mesh), + "polydeg" => length(mesh.nodes) - 1, + ] + summary_box(io, + "T8codeMesh{" * string(ndims(mesh)) * ", " * string(real(mesh)) * "}", + setup) + end end """ @@ -112,104 +118,112 @@ Non-periodic boundaries will be called ':x_neg', ':x_pos', ':y_neg', ':y_pos', ' deciding for each dimension if the boundaries in this dimension are periodic. """ function T8codeMesh(trees_per_dimension; polydeg, - mapping=nothing, RealT=Float64, initial_refinement_level=0, periodicity=true) - - NDIMS = length(trees_per_dimension) - - # Convert periodicity to a Tuple of a Bool for every dimension - if all(periodicity) - # Also catches case where periodicity = true - periodicity = ntuple(_->true, NDIMS) - elseif !any(periodicity) - # Also catches case where periodicity = false - periodicity = ntuple(_->false, NDIMS) - else - # Default case if periodicity is an iterable - periodicity = Tuple(periodicity) - end - - if NDIMS == 2 - conn = T8code.Libt8.p4est_connectivity_new_brick(trees_per_dimension..., periodicity...) - do_partition = 0 - cmesh = t8_cmesh_new_from_p4est(conn,mpi_comm(),do_partition) - T8code.Libt8.p4est_connectivity_destroy(conn) - elseif NDIMS == 3 - conn = T8code.Libt8.p8est_connectivity_new_brick(trees_per_dimension..., periodicity...) - do_partition = 0 - cmesh = t8_cmesh_new_from_p8est(conn,mpi_comm(),do_partition) - T8code.Libt8.p8est_connectivity_destroy(conn) - end - - scheme = t8_scheme_new_default_cxx() - forest = t8_forest_new_uniform(cmesh,scheme,initial_refinement_level,0,mpi_comm()) - - basis = LobattoLegendreBasis(RealT, polydeg) - nodes = basis.nodes - - tree_node_coordinates = Array{RealT, NDIMS+2}(undef, NDIMS, - ntuple(_ -> length(nodes), NDIMS)..., - prod(trees_per_dimension)) - - # Get cell length in reference mesh: Omega_ref = [-1,1]^2. - dx = [2 / n for n in trees_per_dimension] - - num_local_trees = t8_cmesh_get_num_local_trees(cmesh) - - # Non-periodic boundaries. - boundary_names = fill(Symbol("---"), 2 * NDIMS, prod(trees_per_dimension)) - - if mapping == nothing - mapping_ = coordinates2mapping(ntuple(_->-1.0,NDIMS), ntuple(_->1.0,NDIMS)) - else - mapping_ = mapping - end - - for itree = 1:num_local_trees - veptr = t8_cmesh_get_tree_vertices(cmesh, itree-1) - verts = unsafe_wrap(Array,veptr,(3,1 << NDIMS)) + mapping = nothing, RealT = Float64, initial_refinement_level = 0, + periodicity = true) + NDIMS = length(trees_per_dimension) + + # Convert periodicity to a Tuple of a Bool for every dimension + if all(periodicity) + # Also catches case where periodicity = true + periodicity = ntuple(_ -> true, NDIMS) + elseif !any(periodicity) + # Also catches case where periodicity = false + periodicity = ntuple(_ -> false, NDIMS) + else + # Default case if periodicity is an iterable + periodicity = Tuple(periodicity) + end if NDIMS == 2 - # Calculate node coordinates of reference mesh. - cell_x_offset = (verts[1,1] - 0.5*(trees_per_dimension[1]-1)) * dx[1] - cell_y_offset = (verts[2,1] - 0.5*(trees_per_dimension[2]-1)) * dx[2] - - for j in eachindex(nodes), i in eachindex(nodes) - tree_node_coordinates[:, i, j, itree] .= mapping_(cell_x_offset + dx[1] * nodes[i]/2, - cell_y_offset + dx[2] * nodes[j]/2) - end + conn = T8code.Libt8.p4est_connectivity_new_brick(trees_per_dimension..., + periodicity...) + do_partition = 0 + cmesh = t8_cmesh_new_from_p4est(conn, mpi_comm(), do_partition) + T8code.Libt8.p4est_connectivity_destroy(conn) elseif NDIMS == 3 - cell_x_offset = (verts[1,1] - 0.5*(trees_per_dimension[1]-1)) * dx[1] - cell_y_offset = (verts[2,1] - 0.5*(trees_per_dimension[2]-1)) * dx[2] - cell_z_offset = (verts[3,1] - 0.5*(trees_per_dimension[3]-1)) * dx[3] - - for k in eachindex(nodes), j in eachindex(nodes), i in eachindex(nodes) - tree_node_coordinates[:, i, j, k, itree] .= mapping_(cell_x_offset + dx[1] * nodes[i]/2, - cell_y_offset + dx[2] * nodes[j]/2, - cell_z_offset + dx[3] * nodes[k]/2) - end - else - throw(ArgumentError("NDIMS should be 2 or 3.")) + conn = T8code.Libt8.p8est_connectivity_new_brick(trees_per_dimension..., + periodicity...) + do_partition = 0 + cmesh = t8_cmesh_new_from_p8est(conn, mpi_comm(), do_partition) + T8code.Libt8.p8est_connectivity_destroy(conn) end - if !periodicity[1] - boundary_names[1, itree] = :x_neg - boundary_names[2, itree] = :x_pos - end + scheme = t8_scheme_new_default_cxx() + forest = t8_forest_new_uniform(cmesh, scheme, initial_refinement_level, 0, mpi_comm()) + + basis = LobattoLegendreBasis(RealT, polydeg) + nodes = basis.nodes + + tree_node_coordinates = Array{RealT, NDIMS + 2}(undef, NDIMS, + ntuple(_ -> length(nodes), NDIMS)..., + prod(trees_per_dimension)) + + # Get cell length in reference mesh: Omega_ref = [-1,1]^2. + dx = [2 / n for n in trees_per_dimension] + + num_local_trees = t8_cmesh_get_num_local_trees(cmesh) + + # Non-periodic boundaries. + boundary_names = fill(Symbol("---"), 2 * NDIMS, prod(trees_per_dimension)) - if !periodicity[2] - boundary_names[3, itree] = :y_neg - boundary_names[4, itree] = :y_pos + if mapping == nothing + mapping_ = coordinates2mapping(ntuple(_ -> -1.0, NDIMS), ntuple(_ -> 1.0, NDIMS)) + else + mapping_ = mapping end - if NDIMS > 2 - if !periodicity[3] - boundary_names[5, itree] = :z_neg - boundary_names[6, itree] = :z_pos - end + for itree in 1:num_local_trees + veptr = t8_cmesh_get_tree_vertices(cmesh, itree - 1) + verts = unsafe_wrap(Array, veptr, (3, 1 << NDIMS)) + + if NDIMS == 2 + # Calculate node coordinates of reference mesh. + cell_x_offset = (verts[1, 1] - 0.5 * (trees_per_dimension[1] - 1)) * dx[1] + cell_y_offset = (verts[2, 1] - 0.5 * (trees_per_dimension[2] - 1)) * dx[2] + + for j in eachindex(nodes), i in eachindex(nodes) + tree_node_coordinates[:, i, j, itree] .= mapping_(cell_x_offset + + dx[1] * nodes[i] / 2, + cell_y_offset + + dx[2] * nodes[j] / 2) + end + elseif NDIMS == 3 + cell_x_offset = (verts[1, 1] - 0.5 * (trees_per_dimension[1] - 1)) * dx[1] + cell_y_offset = (verts[2, 1] - 0.5 * (trees_per_dimension[2] - 1)) * dx[2] + cell_z_offset = (verts[3, 1] - 0.5 * (trees_per_dimension[3] - 1)) * dx[3] + + for k in eachindex(nodes), j in eachindex(nodes), i in eachindex(nodes) + tree_node_coordinates[:, i, j, k, itree] .= mapping_(cell_x_offset + + dx[1] * nodes[i] / 2, + cell_y_offset + + dx[2] * nodes[j] / 2, + cell_z_offset + + dx[3] * nodes[k] / 2) + end + else + throw(ArgumentError("NDIMS should be 2 or 3.")) + end + + if !periodicity[1] + boundary_names[1, itree] = :x_neg + boundary_names[2, itree] = :x_pos + end + + if !periodicity[2] + boundary_names[3, itree] = :y_neg + boundary_names[4, itree] = :y_pos + end + + if NDIMS > 2 + if !periodicity[3] + boundary_names[5, itree] = :z_neg + boundary_names[6, itree] = :z_pos + end + end end - end - return T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, boundary_names, "") + return T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, + boundary_names, "") end """ @@ -233,93 +247,89 @@ conforming mesh from a `t8_cmesh` data structure. - `initial_refinement_level::Integer`: refine the mesh uniformly to this level before the simulation starts. """ function T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh}; - mapping=nothing, polydeg=1, RealT=Float64, - initial_refinement_level=0) where NDIMS - - scheme = t8_scheme_new_default_cxx() - forest = t8_forest_new_uniform(cmesh,scheme,initial_refinement_level,0,mpi_comm()) - - basis = LobattoLegendreBasis(RealT, polydeg) - nodes = basis.nodes - - num_local_trees = t8_cmesh_get_num_local_trees(cmesh) - - tree_node_coordinates = Array{RealT, NDIMS+2}(undef, NDIMS, - ntuple(_ -> length(nodes), NDIMS)..., - num_local_trees) - - nodes_in = [-1.0, 1.0] - matrix = polynomial_interpolation_matrix(nodes_in, nodes) - - if NDIMS == 2 - data_in = Array{RealT, 3}(undef, 2, 2, 2) - tmp1 = zeros(RealT, 2, length(nodes), length(nodes_in)) - - for itree in 0:num_local_trees-1 - veptr = t8_cmesh_get_tree_vertices(cmesh, itree) - verts = unsafe_wrap(Array,veptr,(3,1 << NDIMS)) + mapping = nothing, polydeg = 1, RealT = Float64, + initial_refinement_level = 0) where {NDIMS} + scheme = t8_scheme_new_default_cxx() + forest = t8_forest_new_uniform(cmesh, scheme, initial_refinement_level, 0, mpi_comm()) - u = verts[:,2] - verts[:,1] - v = verts[:,3] - verts[:,1] - w = [0.0,0.0,1.0] + basis = LobattoLegendreBasis(RealT, polydeg) + nodes = basis.nodes - vol = dot(cross(u,v),w) + num_local_trees = t8_cmesh_get_num_local_trees(cmesh) - if vol < 0.0 - @warn "Discovered negative volumes in `cmesh`: vol = $vol" - end + tree_node_coordinates = Array{RealT, NDIMS + 2}(undef, NDIMS, + ntuple(_ -> length(nodes), NDIMS)..., + num_local_trees) - # Tree vertices are stored in z-order. - @views data_in[:, 1, 1] .= verts[1:2,1] - @views data_in[:, 2, 1] .= verts[1:2,2] - @views data_in[:, 1, 2] .= verts[1:2,3] - @views data_in[:, 2, 2] .= verts[1:2,4] + nodes_in = [-1.0, 1.0] + matrix = polynomial_interpolation_matrix(nodes_in, nodes) - # Interpolate corner coordinates to specified nodes. - multiply_dimensionwise!( - view(tree_node_coordinates, :, :, :, itree+1), - matrix, matrix, - data_in, - tmp1 - ) - end - elseif NDIMS == 3 - data_in = Array{RealT, 4}(undef, 3, 2, 2, 2) - tmp1 = zeros(RealT, 3, length(nodes), length(nodes_in), length(nodes_in)) - - for itree in 0:num_local_trees-1 - veptr = t8_cmesh_get_tree_vertices(cmesh, itree) - verts = unsafe_wrap(Array,veptr,(3,1 << NDIMS)) - - # Tree vertices are stored in z-order. - @views data_in[:, 1, 1, 1] .= verts[1:3,1] - @views data_in[:, 2, 1, 1] .= verts[1:3,2] - @views data_in[:, 1, 2, 1] .= verts[1:3,3] - @views data_in[:, 2, 2, 1] .= verts[1:3,4] - - @views data_in[:, 1, 1, 2] .= verts[1:3,5] - @views data_in[:, 2, 1, 2] .= verts[1:3,6] - @views data_in[:, 1, 2, 2] .= verts[1:3,7] - @views data_in[:, 2, 2, 2] .= verts[1:3,8] - - # Interpolate corner coordinates to specified nodes. - multiply_dimensionwise!( - view(tree_node_coordinates, :, :, :, :, itree+1), - matrix, matrix, matrix, - data_in, - tmp1 - ) + if NDIMS == 2 + data_in = Array{RealT, 3}(undef, 2, 2, 2) + tmp1 = zeros(RealT, 2, length(nodes), length(nodes_in)) + + for itree in 0:(num_local_trees - 1) + veptr = t8_cmesh_get_tree_vertices(cmesh, itree) + verts = unsafe_wrap(Array, veptr, (3, 1 << NDIMS)) + + u = verts[:, 2] - verts[:, 1] + v = verts[:, 3] - verts[:, 1] + w = [0.0, 0.0, 1.0] + + vol = dot(cross(u, v), w) + + if vol < 0.0 + @warn "Discovered negative volumes in `cmesh`: vol = $vol" + end + + # Tree vertices are stored in z-order. + @views data_in[:, 1, 1] .= verts[1:2, 1] + @views data_in[:, 2, 1] .= verts[1:2, 2] + @views data_in[:, 1, 2] .= verts[1:2, 3] + @views data_in[:, 2, 2] .= verts[1:2, 4] + + # Interpolate corner coordinates to specified nodes. + multiply_dimensionwise!(view(tree_node_coordinates, :, :, :, itree + 1), + matrix, matrix, + data_in, + tmp1) + end + elseif NDIMS == 3 + data_in = Array{RealT, 4}(undef, 3, 2, 2, 2) + tmp1 = zeros(RealT, 3, length(nodes), length(nodes_in), length(nodes_in)) + + for itree in 0:(num_local_trees - 1) + veptr = t8_cmesh_get_tree_vertices(cmesh, itree) + verts = unsafe_wrap(Array, veptr, (3, 1 << NDIMS)) + + # Tree vertices are stored in z-order. + @views data_in[:, 1, 1, 1] .= verts[1:3, 1] + @views data_in[:, 2, 1, 1] .= verts[1:3, 2] + @views data_in[:, 1, 2, 1] .= verts[1:3, 3] + @views data_in[:, 2, 2, 1] .= verts[1:3, 4] + + @views data_in[:, 1, 1, 2] .= verts[1:3, 5] + @views data_in[:, 2, 1, 2] .= verts[1:3, 6] + @views data_in[:, 1, 2, 2] .= verts[1:3, 7] + @views data_in[:, 2, 2, 2] .= verts[1:3, 8] + + # Interpolate corner coordinates to specified nodes. + multiply_dimensionwise!(view(tree_node_coordinates, :, :, :, :, itree + 1), + matrix, matrix, matrix, + data_in, + tmp1) + end + else + throw(ArgumentError("NDIMS should be 2 or 3.")) end - else - throw(ArgumentError("NDIMS should be 2 or 3.")) - end - map_node_coordinates!(tree_node_coordinates, mapping) + map_node_coordinates!(tree_node_coordinates, mapping) - # There's no simple and generic way to distinguish boundaries. Name all of them :all. - boundary_names = fill(:all, 2 * NDIMS, num_local_trees) + # There's no simple and generic way to distinguish boundaries. Name all of them :all. + boundary_names = fill(:all, 2 * NDIMS, num_local_trees) - return T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, boundary_names, "") + return T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, + boundary_names, "") end """ @@ -342,13 +352,12 @@ conforming mesh from a `p4est_connectivity` data structure. - `RealT::Type`: the type that should be used for coordinates. - `initial_refinement_level::Integer`: refine the mesh uniformly to this level before the simulation starts. """ -function T8codeMesh{NDIMS}(conn::Ptr{p4est_connectivity}; kwargs...) where NDIMS +function T8codeMesh{NDIMS}(conn::Ptr{p4est_connectivity}; kwargs...) where {NDIMS} + @assert NDIMS == 2 # Only support for NDIMS = 2. - @assert NDIMS == 2 # Only support for NDIMS = 2. + cmesh = t8_cmesh_new_from_p4est(conn, mpi_comm(), 0) - cmesh = t8_cmesh_new_from_p4est(conn, mpi_comm(), 0) - - return T8codeMesh{NDIMS}(cmesh; kwargs...) + return T8codeMesh{NDIMS}(cmesh; kwargs...) end """ @@ -371,13 +380,12 @@ conforming mesh from a `p4est_connectivity` data structure. - `RealT::Type`: the type that should be used for coordinates. - `initial_refinement_level::Integer`: refine the mesh uniformly to this level before the simulation starts. """ -function T8codeMesh{NDIMS}(conn::Ptr{p8est_connectivity}; kwargs...) where NDIMS - - @assert NDIMS == 3 # Only support for NDIMS = 3. +function T8codeMesh{NDIMS}(conn::Ptr{p8est_connectivity}; kwargs...) where {NDIMS} + @assert NDIMS == 3 # Only support for NDIMS = 3. - cmesh = t8_cmesh_new_from_p8est(conn, mpi_comm(), 0) + cmesh = t8_cmesh_new_from_p8est(conn, mpi_comm(), 0) - return T8codeMesh{NDIMS}(cmesh; kwargs...) + return T8codeMesh{NDIMS}(cmesh; kwargs...) end """ @@ -400,24 +408,24 @@ mesh from a Gmsh mesh file (`.msh`). - `RealT::Type`: the type that should be used for coordinates. - `initial_refinement_level::Integer`: refine the mesh uniformly to this level before the simulation starts. """ -function T8codeMesh{NDIMS}(meshfile::String; kwargs...) where NDIMS +function T8codeMesh{NDIMS}(meshfile::String; kwargs...) where {NDIMS} + + # Prevent `t8code` from crashing Julia if the file doesn't exist. + @assert isfile(meshfile) - # Prevent `t8code` from crashing Julia if the file doesn't exist. - @assert isfile(meshfile) - - meshfile_prefix, meshfile_suffix = splitext(meshfile) + meshfile_prefix, meshfile_suffix = splitext(meshfile) - cmesh = t8_cmesh_from_msh_file(meshfile_prefix, 0, mpi_comm(), NDIMS, 0, 0) + cmesh = t8_cmesh_from_msh_file(meshfile_prefix, 0, mpi_comm(), NDIMS, 0, 0) - return T8codeMesh{NDIMS}(cmesh; kwargs...) + return T8codeMesh{NDIMS}(cmesh; kwargs...) end # TODO: Just a placeholder. Will be implemented later when MPI is supported. -function balance!(mesh::T8codeMesh, init_fn=C_NULL) - return nothing +function balance!(mesh::T8codeMesh, init_fn = C_NULL) + return nothing end # TODO: Just a placeholder. Will be implemented later when MPI is supported. -function partition!(mesh::T8codeMesh; allow_coarsening=true, weight_fn=C_NULL) - return nothing +function partition!(mesh::T8codeMesh; allow_coarsening = true, weight_fn = C_NULL) + return nothing end diff --git a/src/solvers/dg.jl b/src/solvers/dg.jl index 81d2e70b31..495e0ffc4a 100644 --- a/src/solvers/dg.jl +++ b/src/solvers/dg.jl @@ -363,7 +363,8 @@ function get_element_variables!(element_variables, u, mesh, equations, dg::DG, c dg, cache) end -const MeshesDGSEM = Union{TreeMesh, StructuredMesh, UnstructuredMesh2D, P4estMesh, T8codeMesh} +const MeshesDGSEM = Union{TreeMesh, StructuredMesh, UnstructuredMesh2D, P4estMesh, + T8codeMesh} @inline function ndofs(mesh::MeshesDGSEM, dg::DG, cache) nelements(cache.elements) * nnodes(dg)^ndims(mesh) @@ -680,5 +681,4 @@ include("dgsem_structured/dg.jl") include("dgsem_unstructured/dg.jl") include("dgsem_p4est/dg.jl") include("dgsem_t8code/dg.jl") - end # @muladd diff --git a/src/solvers/dgsem_p4est/containers.jl b/src/solvers/dgsem_p4est/containers.jl index cdebb75c3f..0176f5c634 100644 --- a/src/solvers/dgsem_p4est/containers.jl +++ b/src/solvers/dgsem_p4est/containers.jl @@ -81,8 +81,10 @@ function Base.resize!(elements::P4estElementContainer, capacity) end # Create element container and initialize element data -function init_elements(mesh::Union{P4estMesh{NDIMS, RealT}, T8codeMesh{NDIMS, RealT}}, equations, - basis, ::Type{uEltype}) where {NDIMS, RealT<:Real, uEltype<:Real} +function init_elements(mesh::Union{P4estMesh{NDIMS, RealT}, T8codeMesh{NDIMS, RealT}}, + equations, + basis, + ::Type{uEltype}) where {NDIMS, RealT <: Real, uEltype <: Real} nelements = ncells(mesh) _node_coordinates = Vector{RealT}(undef, NDIMS * nnodes(basis)^NDIMS * nelements) @@ -165,8 +167,8 @@ end # Create interface container and initialize interface data. function init_interfaces(mesh::Union{P4estMesh, T8codeMesh}, equations, basis, elements) - NDIMS = ndims(elements) - uEltype = eltype(elements) + NDIMS = ndims(elements) + uEltype = eltype(elements) # Initialize container n_interfaces = count_required_surfaces(mesh).interfaces @@ -240,8 +242,8 @@ end # Create interface container and initialize interface data in `elements`. function init_boundaries(mesh::Union{P4estMesh, T8codeMesh}, equations, basis, elements) - NDIMS = ndims(elements) - uEltype = eltype(elements) + NDIMS = ndims(elements) + uEltype = eltype(elements) # Initialize container n_boundaries = count_required_surfaces(mesh).boundaries diff --git a/src/solvers/dgsem_p4est/containers_2d.jl b/src/solvers/dgsem_p4est/containers_2d.jl index 62129dd346..236d7d24c0 100644 --- a/src/solvers/dgsem_p4est/containers_2d.jl +++ b/src/solvers/dgsem_p4est/containers_2d.jl @@ -6,9 +6,10 @@ #! format: noindent # Initialize data structures in element container -function init_elements!(elements, mesh::Union{P4estMesh{2}, T8codeMesh{2}}, basis::LobattoLegendreBasis) - @unpack node_coordinates, jacobian_matrix, - contravariant_vectors, inverse_jacobian = elements +function init_elements!(elements, mesh::Union{P4estMesh{2}, T8codeMesh{2}}, + basis::LobattoLegendreBasis) + @unpack node_coordinates, jacobian_matrix, + contravariant_vectors, inverse_jacobian = elements calc_node_coordinates!(node_coordinates, mesh, basis) diff --git a/src/solvers/dgsem_p4est/containers_3d.jl b/src/solvers/dgsem_p4est/containers_3d.jl index d9f7f3398f..7e383924ba 100644 --- a/src/solvers/dgsem_p4est/containers_3d.jl +++ b/src/solvers/dgsem_p4est/containers_3d.jl @@ -6,9 +6,10 @@ #! format: noindent # Initialize data structures in element container -function init_elements!(elements, mesh::Union{P4estMesh{3}, T8codeMesh{3}}, basis::LobattoLegendreBasis) - @unpack node_coordinates, jacobian_matrix, - contravariant_vectors, inverse_jacobian = elements +function init_elements!(elements, mesh::Union{P4estMesh{3}, T8codeMesh{3}}, + basis::LobattoLegendreBasis) + @unpack node_coordinates, jacobian_matrix, + contravariant_vectors, inverse_jacobian = elements calc_node_coordinates!(node_coordinates, mesh, basis) @@ -326,5 +327,4 @@ function orientation_to_indices_p4est(my_face, other_face, orientation_code) return surface_index1, surface_index2 end - end # @muladd diff --git a/src/solvers/dgsem_p4est/dg_2d.jl b/src/solvers/dgsem_p4est/dg_2d.jl index 21bcf28da0..97b931fa32 100644 --- a/src/solvers/dgsem_p4est/dg_2d.jl +++ b/src/solvers/dgsem_p4est/dg_2d.jl @@ -7,7 +7,8 @@ # The methods below are specialized on the mortar type # and called from the basic `create_cache` method at the top. -function create_cache(mesh::Union{P4estMesh{2}, T8codeMesh{2}}, equations, mortar_l2::LobattoLegendreMortarL2, uEltype) +function create_cache(mesh::Union{P4estMesh{2}, T8codeMesh{2}}, equations, + mortar_l2::LobattoLegendreMortarL2, uEltype) # TODO: Taal performance using different types MA2d = MArray{Tuple{nvariables(equations), nnodes(mortar_l2)}, uEltype, 2, @@ -558,7 +559,8 @@ end end @inline function mortar_fluxes_to_elements!(surface_flux_values, - mesh::Union{P4estMesh{2}, T8codeMesh{2}}, equations, + mesh::Union{P4estMesh{2}, T8codeMesh{2}}, + equations, mortar_l2::LobattoLegendreMortarL2, dg::DGSEM, cache, mortar, fstar, u_buffer) @unpack neighbor_ids, node_indices = cache.mortars diff --git a/src/solvers/dgsem_p4est/dg_3d.jl b/src/solvers/dgsem_p4est/dg_3d.jl index a6d54b94b3..9bde04d081 100644 --- a/src/solvers/dgsem_p4est/dg_3d.jl +++ b/src/solvers/dgsem_p4est/dg_3d.jl @@ -7,17 +7,21 @@ # The methods below are specialized on the mortar type # and called from the basic `create_cache` method at the top. -function create_cache(mesh::Union{P4estMesh{3}, T8codeMesh{3}}, equations, mortar_l2::LobattoLegendreMortarL2, uEltype) - # TODO: Taal compare performance of different types - fstar_threaded = [Array{uEltype, 4}(undef, nvariables(equations), nnodes(mortar_l2), nnodes(mortar_l2), 4) - for _ in 1:Threads.nthreads()] - - fstar_tmp_threaded = [Array{uEltype, 3}(undef, nvariables(equations), nnodes(mortar_l2), nnodes(mortar_l2)) - for _ in 1:Threads.nthreads()] - u_threaded = [Array{uEltype, 3}(undef, nvariables(equations), nnodes(mortar_l2), nnodes(mortar_l2)) - for _ in 1:Threads.nthreads()] - - (; fstar_threaded, fstar_tmp_threaded, u_threaded) +function create_cache(mesh::Union{P4estMesh{3}, T8codeMesh{3}}, equations, + mortar_l2::LobattoLegendreMortarL2, uEltype) + # TODO: Taal compare performance of different types + fstar_threaded = [Array{uEltype, 4}(undef, nvariables(equations), nnodes(mortar_l2), + nnodes(mortar_l2), 4) + for _ in 1:Threads.nthreads()] + + fstar_tmp_threaded = [Array{uEltype, 3}(undef, nvariables(equations), + nnodes(mortar_l2), nnodes(mortar_l2)) + for _ in 1:Threads.nthreads()] + u_threaded = [Array{uEltype, 3}(undef, nvariables(equations), nnodes(mortar_l2), + nnodes(mortar_l2)) + for _ in 1:Threads.nthreads()] + + (; fstar_threaded, fstar_tmp_threaded, u_threaded) end # index_to_start_step_3d(index::Symbol, index_range) @@ -634,7 +638,8 @@ end end @inline function mortar_fluxes_to_elements!(surface_flux_values, - mesh::Union{P4estMesh{3}, T8codeMesh{3}}, equations, + mesh::Union{P4estMesh{3}, T8codeMesh{3}}, + equations, mortar_l2::LobattoLegendreMortarL2, dg::DGSEM, cache, mortar, fstar, u_buffer, fstar_tmp) diff --git a/src/solvers/dgsem_structured/dg_2d.jl b/src/solvers/dgsem_structured/dg_2d.jl index dc37690866..3e8ce759b3 100644 --- a/src/solvers/dgsem_structured/dg_2d.jl +++ b/src/solvers/dgsem_structured/dg_2d.jl @@ -91,7 +91,10 @@ end end @inline function flux_differencing_kernel!(du, u, - element, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, + element, + mesh::Union{StructuredMesh{2}, + UnstructuredMesh2D, P4estMesh{2}, + T8codeMesh{2}}, nonconservative_terms::False, equations, volume_flux, dg::DGSEM, cache, alpha = true) @unpack derivative_split = dg.basis @@ -145,7 +148,10 @@ end end @inline function flux_differencing_kernel!(du, u, - element, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, + element, + mesh::Union{StructuredMesh{2}, + UnstructuredMesh2D, P4estMesh{2}, + T8codeMesh{2}}, nonconservative_terms::True, equations, volume_flux, dg::DGSEM, cache, alpha = true) @unpack derivative_split = dg.basis @@ -212,7 +218,8 @@ end # "A provably entropy stable subcell shock capturing approach for high order split form DG for the compressible Euler equations" # [arXiv: 2008.12044v2](https://arxiv.org/pdf/2008.12044) @inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, u, - mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, + mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, + P4estMesh{2}, T8codeMesh{2}}, nonconservative_terms::False, equations, volume_flux_fv, dg::DGSEM, element, cache) @unpack contravariant_vectors = cache.elements @@ -279,8 +286,10 @@ end end # Calculate the finite volume fluxes inside curvilinear elements (**with non-conservative terms**). -@inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, u::AbstractArray{<:Any,4}, - mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, +@inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, + u::AbstractArray{<:Any, 4}, + mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, + P4estMesh{2}, T8codeMesh{2}}, nonconservative_terms::True, equations, volume_flux_fv, dg::DGSEM, element, cache) @unpack contravariant_vectors = cache.elements @@ -600,7 +609,8 @@ function calc_boundary_flux!(cache, u, t, boundary_conditions::NamedTuple, end function apply_jacobian!(du, - mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, + mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, + P4estMesh{2}, T8codeMesh{2}}, equations, dg::DG, cache) @unpack inverse_jacobian = cache.elements diff --git a/src/solvers/dgsem_structured/dg_3d.jl b/src/solvers/dgsem_structured/dg_3d.jl index 8c4714f1fb..5a898e9b15 100644 --- a/src/solvers/dgsem_structured/dg_3d.jl +++ b/src/solvers/dgsem_structured/dg_3d.jl @@ -51,7 +51,8 @@ end @inline function weak_form_kernel!(du, u, element, - mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, + mesh::Union{StructuredMesh{3}, P4estMesh{3}, + T8codeMesh{3}}, nonconservative_terms::False, equations, dg::DGSEM, cache, alpha = true) # true * [some floating point value] == [exactly the same floating point value] @@ -107,7 +108,9 @@ end # mapping terms, stored in `contravariant_vectors`, is peeled apart from the evaluation of # the physical fluxes in each Cartesian direction @inline function flux_differencing_kernel!(du, u, - element, mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, + element, + mesh::Union{StructuredMesh{3}, P4estMesh{3}, + T8codeMesh{3}}, nonconservative_terms::False, equations, volume_flux, dg::DGSEM, cache, alpha = true) # true * [some floating point value] == [exactly the same floating point value] @@ -180,7 +183,9 @@ end end @inline function flux_differencing_kernel!(du, u, - element, mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, + element, + mesh::Union{StructuredMesh{3}, P4estMesh{3}, + T8codeMesh{3}}, nonconservative_terms::True, equations, volume_flux, dg::DGSEM, cache, alpha = true) @unpack derivative_split = dg.basis @@ -263,8 +268,11 @@ end # by Hennemann, Rueda-Ramirez, Hindenlang, Gassner (2020) # "A provably entropy stable subcell shock capturing approach for high order split form DG for the compressible Euler equations" # [arXiv: 2008.12044v2](https://arxiv.org/pdf/2008.12044) -@inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, fstar3_L, fstar3_R, u, - mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, nonconservative_terms::False, +@inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, fstar3_L, + fstar3_R, u, + mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3 + } + }, nonconservative_terms::False, equations, volume_flux_fv, dg::DGSEM, element, cache) @unpack contravariant_vectors = cache.elements @unpack weights, derivative_matrix = dg.basis @@ -356,8 +364,11 @@ end end # # Calculate the finite volume fluxes inside curvilinear elements (**with non-conservative terms**). -@inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, fstar3_L, fstar3_R, u, - mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, nonconservative_terms::True, +@inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, fstar3_L, + fstar3_R, u, + mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3 + } + }, nonconservative_terms::True, equations, volume_flux_fv, dg::DGSEM, element, cache) @unpack contravariant_vectors = cache.elements @unpack weights, derivative_matrix = dg.basis diff --git a/src/solvers/dgsem_t8code/containers.jl b/src/solvers/dgsem_t8code/containers.jl index e808a7336d..37bb581687 100644 --- a/src/solvers/dgsem_t8code/containers.jl +++ b/src/solvers/dgsem_t8code/containers.jl @@ -1,57 +1,58 @@ function reinitialize_containers!(mesh::T8codeMesh, equations, dg::DGSEM, cache) - # Re-initialize elements container. - @unpack elements = cache - resize!(elements, ncells(mesh)) - init_elements!(elements, mesh, dg.basis) + # Re-initialize elements container. + @unpack elements = cache + resize!(elements, ncells(mesh)) + init_elements!(elements, mesh, dg.basis) - count_required_surfaces!(mesh) + count_required_surfaces!(mesh) - # Resize interfaces container. - @unpack interfaces = cache - resize!(interfaces, mesh.ninterfaces) + # Resize interfaces container. + @unpack interfaces = cache + resize!(interfaces, mesh.ninterfaces) - # Resize mortars container. - @unpack mortars = cache - resize!(mortars, mesh.nmortars) + # Resize mortars container. + @unpack mortars = cache + resize!(mortars, mesh.nmortars) - # Resize boundaries container. - @unpack boundaries = cache - resize!(boundaries, mesh.nboundaries) + # Resize boundaries container. + @unpack boundaries = cache + resize!(boundaries, mesh.nboundaries) - trixi_t8_fill_mesh_info(mesh.forest, elements, interfaces, mortars, boundaries, mesh.boundary_names) + trixi_t8_fill_mesh_info(mesh.forest, elements, interfaces, mortars, boundaries, + mesh.boundary_names) - return nothing + return nothing end function count_required_surfaces!(mesh::T8codeMesh) - counts = trixi_t8_count_interfaces(mesh.forest) + counts = trixi_t8_count_interfaces(mesh.forest) - mesh.nmortars = counts.mortars - mesh.ninterfaces = counts.interfaces - mesh.nboundaries = counts.boundaries + mesh.nmortars = counts.mortars + mesh.ninterfaces = counts.interfaces + mesh.nboundaries = counts.boundaries end # Compatibility to `dgsem_p4est/containers.jl`. function count_required_surfaces(mesh::T8codeMesh) - return (interfaces = mesh.ninterfaces, - mortars = mesh.nmortars, - boundaries = mesh.nboundaries) + return (interfaces = mesh.ninterfaces, + mortars = mesh.nmortars, + boundaries = mesh.nboundaries) end # Compatibility to `dgsem_p4est/containers.jl`. function init_interfaces!(interfaces, mesh::T8codeMesh) - # Already computed. Do nothing. - return nothing + # Already computed. Do nothing. + return nothing end # Compatibility to `dgsem_p4est/containers.jl`. function init_mortars!(mortars, mesh::T8codeMesh) - # Already computed. Do nothing. - return nothing + # Already computed. Do nothing. + return nothing end # Compatibility to `dgsem_p4est/containers.jl`. function init_boundaries!(boundaries, mesh::T8codeMesh) - # Already computed. Do nothing. - return nothing + # Already computed. Do nothing. + return nothing end diff --git a/src/solvers/dgsem_t8code/containers_2d.jl b/src/solvers/dgsem_t8code/containers_2d.jl index 305c8c4f27..4e69d0cd20 100644 --- a/src/solvers/dgsem_t8code/containers_2d.jl +++ b/src/solvers/dgsem_t8code/containers_2d.jl @@ -1,63 +1,69 @@ @muladd begin -# Interpolate tree_node_coordinates to each quadrant at the specified nodes. -function calc_node_coordinates!(node_coordinates, - mesh::T8codeMesh{2}, - nodes::AbstractVector) - # We use `StrideArray`s here since these buffers are used in performance-critical - # places and the additional information passed to the compiler makes them faster - # than native `Array`s. - tmp1 = StrideArray(undef, real(mesh), - StaticInt(2), static_length(nodes), static_length(mesh.nodes)) - matrix1 = StrideArray(undef, real(mesh), - static_length(nodes), static_length(mesh.nodes)) - matrix2 = similar(matrix1) - baryweights_in = barycentric_weights(mesh.nodes) - - num_local_trees = t8_forest_get_num_local_trees(mesh.forest) - - current_index = 0 - for itree = 0:num_local_trees-1 - - tree_class = t8_forest_get_tree_class(mesh.forest, itree) - eclass_scheme = t8_forest_get_eclass_scheme(mesh.forest, tree_class) - num_elements_in_tree = t8_forest_get_tree_num_elements(mesh.forest, itree) - - for ielement = 0:num_elements_in_tree-1 - element = t8_forest_get_element_in_tree(mesh.forest, itree, ielement) - element_level = t8_element_level(eclass_scheme, element) - - element_length = t8_quad_len(element_level) / t8_quad_root_len - - element_coords = Array{Float64}(undef, 3) - t8_element_vertex_reference_coords(eclass_scheme, element, 0, pointer(element_coords)) - - nodes_out_x = 2 * (element_length * 1/2 * (nodes .+ 1) .+ element_coords[1]) .- 1 - nodes_out_y = 2 * (element_length * 1/2 * (nodes .+ 1) .+ element_coords[2]) .- 1 - - polynomial_interpolation_matrix!(matrix1, mesh.nodes, nodes_out_x, baryweights_in) - polynomial_interpolation_matrix!(matrix2, mesh.nodes, nodes_out_y, baryweights_in) - - multiply_dimensionwise!( - view(node_coordinates, :, :, :, current_index += 1), - matrix1, matrix2, - view(mesh.tree_node_coordinates, :, :, :, itree+1), - tmp1 - ) - end - end + # Interpolate tree_node_coordinates to each quadrant at the specified nodes. + function calc_node_coordinates!(node_coordinates, + mesh::T8codeMesh{2}, + nodes::AbstractVector) + # We use `StrideArray`s here since these buffers are used in performance-critical + # places and the additional information passed to the compiler makes them faster + # than native `Array`s. + tmp1 = StrideArray(undef, real(mesh), + StaticInt(2), static_length(nodes), static_length(mesh.nodes)) + matrix1 = StrideArray(undef, real(mesh), + static_length(nodes), static_length(mesh.nodes)) + matrix2 = similar(matrix1) + baryweights_in = barycentric_weights(mesh.nodes) + + num_local_trees = t8_forest_get_num_local_trees(mesh.forest) + + current_index = 0 + for itree in 0:(num_local_trees - 1) + tree_class = t8_forest_get_tree_class(mesh.forest, itree) + eclass_scheme = t8_forest_get_eclass_scheme(mesh.forest, tree_class) + num_elements_in_tree = t8_forest_get_tree_num_elements(mesh.forest, itree) + + for ielement in 0:(num_elements_in_tree - 1) + element = t8_forest_get_element_in_tree(mesh.forest, itree, ielement) + element_level = t8_element_level(eclass_scheme, element) + + element_length = t8_quad_len(element_level) / t8_quad_root_len - return node_coordinates -end + element_coords = Array{Float64}(undef, 3) + t8_element_vertex_reference_coords(eclass_scheme, element, 0, + pointer(element_coords)) -function trixi_t8_init_mortar_neighbor_ids!(mortars::P4estMortarContainer{2}, my_face, other_face, orientation, neighbor_ielements, mortar_id) - if orientation == 0 - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[1] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[2] + 1 - else - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 - end -end + nodes_out_x = 2 * + (element_length * 1 / 2 * (nodes .+ 1) .+ element_coords[1]) .- + 1 + nodes_out_y = 2 * + (element_length * 1 / 2 * (nodes .+ 1) .+ element_coords[2]) .- + 1 + polynomial_interpolation_matrix!(matrix1, mesh.nodes, nodes_out_x, + baryweights_in) + polynomial_interpolation_matrix!(matrix2, mesh.nodes, nodes_out_y, + baryweights_in) + + multiply_dimensionwise!(view(node_coordinates, :, :, :, current_index += 1), + matrix1, matrix2, + view(mesh.tree_node_coordinates, :, :, :, + itree + 1), + tmp1) + end + end + + return node_coordinates + end + + function trixi_t8_init_mortar_neighbor_ids!(mortars::P4estMortarContainer{2}, my_face, + other_face, orientation, neighbor_ielements, + mortar_id) + if orientation == 0 + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[2] + 1 + else + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 + end + end end # @muladd diff --git a/src/solvers/dgsem_t8code/containers_3d.jl b/src/solvers/dgsem_t8code/containers_3d.jl index 25368972f1..65fb049f39 100644 --- a/src/solvers/dgsem_t8code/containers_3d.jl +++ b/src/solvers/dgsem_t8code/containers_3d.jl @@ -4,218 +4,228 @@ # See https://ranocha.de/blog/Optimizing_EC_Trixi for further details. @muladd begin -# Interpolate tree_node_coordinates to each quadrant at the specified nodes -function calc_node_coordinates!(node_coordinates, - mesh::T8codeMesh{3}, - nodes::AbstractVector) - # We use `StrideArray`s here since these buffers are used in performance-critical - # places and the additional information passed to the compiler makes them faster - # than native `Array`s. - tmp1 = StrideArray(undef, real(mesh), - StaticInt(3), static_length(nodes), static_length(mesh.nodes), static_length(mesh.nodes)) - matrix1 = StrideArray(undef, real(mesh), - static_length(nodes), static_length(mesh.nodes)) - matrix2 = similar(matrix1) - matrix3 = similar(matrix1) - baryweights_in = barycentric_weights(mesh.nodes) - - num_local_trees = t8_forest_get_num_local_trees(mesh.forest) - - current_index = 0 - for itree = 0:num_local_trees-1 - - tree_class = t8_forest_get_tree_class(mesh.forest, itree) - eclass_scheme = t8_forest_get_eclass_scheme(mesh.forest, tree_class) - num_elements_in_tree = t8_forest_get_tree_num_elements(mesh.forest, itree) - - for ielement = 0:num_elements_in_tree-1 - element = t8_forest_get_element_in_tree(mesh.forest, itree, ielement) - element_level = t8_element_level(eclass_scheme, element) - - element_length = t8_hex_len(element_level) / t8_hex_root_len - - element_coords = Vector{Cdouble}(undef, 3) - t8_element_vertex_reference_coords(eclass_scheme, element, 0, pointer(element_coords)) - - nodes_out_x = 2 * (element_length * 1/2 * (nodes .+ 1) .+ element_coords[1]) .- 1 - nodes_out_y = 2 * (element_length * 1/2 * (nodes .+ 1) .+ element_coords[2]) .- 1 - nodes_out_z = 2 * (element_length * 1/2 * (nodes .+ 1) .+ element_coords[3]) .- 1 - - polynomial_interpolation_matrix!(matrix1, mesh.nodes, nodes_out_x, baryweights_in) - polynomial_interpolation_matrix!(matrix2, mesh.nodes, nodes_out_y, baryweights_in) - polynomial_interpolation_matrix!(matrix3, mesh.nodes, nodes_out_z, baryweights_in) - - multiply_dimensionwise!( - view(node_coordinates, :, :, :, :, current_index += 1), - matrix1, matrix2, matrix3, - view(mesh.tree_node_coordinates, :, :, :, :, itree+1), - tmp1 - ) + # Interpolate tree_node_coordinates to each quadrant at the specified nodes + function calc_node_coordinates!(node_coordinates, + mesh::T8codeMesh{3}, + nodes::AbstractVector) + # We use `StrideArray`s here since these buffers are used in performance-critical + # places and the additional information passed to the compiler makes them faster + # than native `Array`s. + tmp1 = StrideArray(undef, real(mesh), + StaticInt(3), static_length(nodes), static_length(mesh.nodes), + static_length(mesh.nodes)) + matrix1 = StrideArray(undef, real(mesh), + static_length(nodes), static_length(mesh.nodes)) + matrix2 = similar(matrix1) + matrix3 = similar(matrix1) + baryweights_in = barycentric_weights(mesh.nodes) + + num_local_trees = t8_forest_get_num_local_trees(mesh.forest) + + current_index = 0 + for itree in 0:(num_local_trees - 1) + tree_class = t8_forest_get_tree_class(mesh.forest, itree) + eclass_scheme = t8_forest_get_eclass_scheme(mesh.forest, tree_class) + num_elements_in_tree = t8_forest_get_tree_num_elements(mesh.forest, itree) + + for ielement in 0:(num_elements_in_tree - 1) + element = t8_forest_get_element_in_tree(mesh.forest, itree, ielement) + element_level = t8_element_level(eclass_scheme, element) + + element_length = t8_hex_len(element_level) / t8_hex_root_len + + element_coords = Vector{Cdouble}(undef, 3) + t8_element_vertex_reference_coords(eclass_scheme, element, 0, + pointer(element_coords)) + + nodes_out_x = 2 * + (element_length * 1 / 2 * (nodes .+ 1) .+ element_coords[1]) .- + 1 + nodes_out_y = 2 * + (element_length * 1 / 2 * (nodes .+ 1) .+ element_coords[2]) .- + 1 + nodes_out_z = 2 * + (element_length * 1 / 2 * (nodes .+ 1) .+ element_coords[3]) .- + 1 + + polynomial_interpolation_matrix!(matrix1, mesh.nodes, nodes_out_x, + baryweights_in) + polynomial_interpolation_matrix!(matrix2, mesh.nodes, nodes_out_y, + baryweights_in) + polynomial_interpolation_matrix!(matrix3, mesh.nodes, nodes_out_z, + baryweights_in) + + multiply_dimensionwise!(view(node_coordinates, :, :, :, :, + current_index += 1), + matrix1, matrix2, matrix3, + view(mesh.tree_node_coordinates, :, :, :, :, + itree + 1), + tmp1) + end + end + + return node_coordinates end - end - - return node_coordinates -end - -# This routine was copied and adapated from `src/dgsem_p4est/containers_3d.jl`: `orientation_to_indices_p4est`. -function trixi_t8_init_mortar_neighbor_ids!(mortars::P4estMortarContainer{3}, my_face, other_face, orientation, neighbor_ielements, mortar_id) - # my_face and other_face are the face directions (zero-based) - # of "my side" and "other side" respectively. - # Face corner 0 of the face with the lower face direction connects to a corner of the other face. - # The number of this corner is the orientation code in `p4est`. - lower = my_face <= other_face - - # x_pos, y_neg, and z_pos are the directions in which the face has right-handed coordinates - # when looked at from the outside. - my_right_handed = my_face in (1, 2, 5) - other_right_handed = other_face in (1, 2, 5) - - # If both or none are right-handed when looked at from the outside, they will have different - # orientations when looked at from the same side of the interface. - flipped = my_right_handed == other_right_handed - - # In the following illustrations, the face corner numbering of `p4est` is shown. - # ξ and η are the local coordinates of the respective face. - # We're looking at both faces from the same side of the interface, so that "other side" - # (in the illustrations on the left) has right-handed coordinates. - if !flipped - if orientation == 0 - # Corner 0 of other side matches corner 0 of my side - # 2┌──────┐3 2┌──────┐3 - # │ │ │ │ - # │ │ │ │ - # 0└──────┘1 0└──────┘1 - # η η - # ↑ ↑ - # │ │ - # └───> ξ └───> ξ - - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[1] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[2] + 1 - mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[3] + 1 - mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[4] + 1 - - elseif ((lower && orientation == 2) # Corner 0 of my side matches corner 2 of other side - || (!lower && orientation == 1)) # Corner 0 of other side matches corner 1 of my side - # 2┌──────┐3 0┌──────┐2 - # │ │ │ │ - # │ │ │ │ - # 0└──────┘1 1└──────┘3 - # η ┌───> η - # ↑ │ - # │ ↓ - # └───> ξ ξ - - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[4] + 1 - mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[1] + 1 - mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[3] + 1 - - elseif ((lower && orientation == 1) # Corner 0 of my side matches corner 1 of other side - || (!lower && orientation == 2)) # Corner 0 of other side matches corner 2 of my side - # 2┌──────┐3 3┌──────┐1 - # │ │ │ │ - # │ │ │ │ - # 0└──────┘1 2└──────┘0 - # η ξ - # ↑ ↑ - # │ │ - # └───> ξ η <───┘ - - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[3] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 - mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[4] + 1 - mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[2] + 1 - - else # orientation == 3 - # Corner 0 of my side matches corner 3 of other side and - # corner 0 of other side matches corner 3 of my side. - # 2┌──────┐3 1┌──────┐0 - # │ │ │ │ - # │ │ │ │ - # 0└──────┘1 3└──────┘2 - # η ξ <───┐ - # ↑ │ - # │ ↓ - # └───> ξ η - - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[4] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[3] + 1 - mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[2] + 1 - mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[1] + 1 + # This routine was copied and adapated from `src/dgsem_p4est/containers_3d.jl`: `orientation_to_indices_p4est`. + function trixi_t8_init_mortar_neighbor_ids!(mortars::P4estMortarContainer{3}, my_face, + other_face, orientation, neighbor_ielements, + mortar_id) + # my_face and other_face are the face directions (zero-based) + # of "my side" and "other side" respectively. + # Face corner 0 of the face with the lower face direction connects to a corner of the other face. + # The number of this corner is the orientation code in `p4est`. + lower = my_face <= other_face + + # x_pos, y_neg, and z_pos are the directions in which the face has right-handed coordinates + # when looked at from the outside. + my_right_handed = my_face in (1, 2, 5) + other_right_handed = other_face in (1, 2, 5) + + # If both or none are right-handed when looked at from the outside, they will have different + # orientations when looked at from the same side of the interface. + flipped = my_right_handed == other_right_handed + + # In the following illustrations, the face corner numbering of `p4est` is shown. + # ξ and η are the local coordinates of the respective face. + # We're looking at both faces from the same side of the interface, so that "other side" + # (in the illustrations on the left) has right-handed coordinates. + if !flipped + if orientation == 0 + # Corner 0 of other side matches corner 0 of my side + # 2┌──────┐3 2┌──────┐3 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 0└──────┘1 + # η η + # ↑ ↑ + # │ │ + # └───> ξ └───> ξ + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[3] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[4] + 1 + + elseif ((lower && orientation == 2) # Corner 0 of my side matches corner 2 of other side + || + (!lower && orientation == 1)) # Corner 0 of other side matches corner 1 of my side + # 2┌──────┐3 0┌──────┐2 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 1└──────┘3 + # η ┌───> η + # ↑ │ + # │ ↓ + # └───> ξ ξ + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[4] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[3] + 1 + + elseif ((lower && orientation == 1) # Corner 0 of my side matches corner 1 of other side + || + (!lower && orientation == 2)) # Corner 0 of other side matches corner 2 of my side + # 2┌──────┐3 3┌──────┐1 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 2└──────┘0 + # η ξ + # ↑ ↑ + # │ │ + # └───> ξ η <───┘ + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[3] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[4] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[2] + 1 + + else # orientation == 3 + # Corner 0 of my side matches corner 3 of other side and + # corner 0 of other side matches corner 3 of my side. + # 2┌──────┐3 1┌──────┐0 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 3└──────┘2 + # η ξ <───┐ + # ↑ │ + # │ ↓ + # └───> ξ η + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[4] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[3] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[1] + 1 + end + else # flipped + if orientation == 0 + # Corner 0 of other side matches corner 0 of my side + # 2┌──────┐3 1┌──────┐3 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 0└──────┘2 + # η ξ + # ↑ ↑ + # │ │ + # └───> ξ └───> η + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[3] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[4] + 1 + + elseif orientation == 2 + # Corner 0 of my side matches corner 2 of other side and + # corner 0 of other side matches corner 2 of my side. + # 2┌──────┐3 0┌──────┐1 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 2└──────┘3 + # η ┌───> ξ + # ↑ │ + # │ ↓ + # └───> ξ η + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[3] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[4] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[2] + 1 + + elseif orientation == 1 + # Corner 0 of my side matches corner 1 of other side and + # corner 0 of other side matches corner 1 of my side. + # 2┌──────┐3 3┌──────┐2 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 1└──────┘0 + # η η + # ↑ ↑ + # │ │ + # └───> ξ ξ <───┘ + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[4] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[3] + 1 + + else # orientation == 3 + # Corner 0 of my side matches corner 3 of other side and + # corner 0 of other side matches corner 3 of my side. + # 2┌──────┐3 2┌──────┐0 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 3└──────┘1 + # η η <───┐ + # ↑ │ + # │ ↓ + # └───> ξ ξ + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[4] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[3] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[1] + 1 + end + end end - else # flipped - if orientation == 0 - # Corner 0 of other side matches corner 0 of my side - # 2┌──────┐3 1┌──────┐3 - # │ │ │ │ - # │ │ │ │ - # 0└──────┘1 0└──────┘2 - # η ξ - # ↑ ↑ - # │ │ - # └───> ξ └───> η - - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[1] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[3] + 1 - mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[2] + 1 - mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[4] + 1 - - elseif orientation == 2 - # Corner 0 of my side matches corner 2 of other side and - # corner 0 of other side matches corner 2 of my side. - # 2┌──────┐3 0┌──────┐1 - # │ │ │ │ - # │ │ │ │ - # 0└──────┘1 2└──────┘3 - # η ┌───> ξ - # ↑ │ - # │ ↓ - # └───> ξ η - - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[3] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[4] + 1 - mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[1] + 1 - mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[2] + 1 - - elseif orientation == 1 - # Corner 0 of my side matches corner 1 of other side and - # corner 0 of other side matches corner 1 of my side. - # 2┌──────┐3 3┌──────┐2 - # │ │ │ │ - # │ │ │ │ - # 0└──────┘1 1└──────┘0 - # η η - # ↑ ↑ - # │ │ - # └───> ξ ξ <───┘ - - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 - mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[4] + 1 - mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[3] + 1 - - else # orientation == 3 - # Corner 0 of my side matches corner 3 of other side and - # corner 0 of other side matches corner 3 of my side. - # 2┌──────┐3 2┌──────┐0 - # │ │ │ │ - # │ │ │ │ - # 0└──────┘1 3└──────┘1 - # η η <───┐ - # ↑ │ - # │ ↓ - # └───> ξ ξ - - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[4] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[2] + 1 - mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[3] + 1 - mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[1] + 1 - - end - end - -end - end # @muladd diff --git a/src/solvers/dgsem_t8code/dg.jl b/src/solvers/dgsem_t8code/dg.jl index 3a76042f41..551c518394 100644 --- a/src/solvers/dgsem_t8code/dg.jl +++ b/src/solvers/dgsem_t8code/dg.jl @@ -1,29 +1,31 @@ @muladd begin -# This method is called when a SemidiscretizationHyperbolic is constructed. -# It constructs the basic `cache` used throughout the simulation to compute -# the RHS etc. -function create_cache(mesh::T8codeMesh, equations::AbstractEquations, dg::DG, ::Any, ::Type{uEltype}) where {uEltype<:Real} - count_required_surfaces!(mesh) - - elements = init_elements(mesh, equations, dg.basis, uEltype) - interfaces = init_interfaces(mesh, equations, dg.basis, elements) - boundaries = init_boundaries(mesh, equations, dg.basis, elements) - mortars = init_mortars(mesh, equations, dg.basis, elements) - - trixi_t8_fill_mesh_info(mesh.forest, elements, interfaces, mortars, boundaries, mesh.boundary_names) - - cache = (; elements, interfaces, boundaries, mortars) - - # Add specialized parts of the cache required to compute the volume integral etc. - cache = (; cache..., create_cache(mesh, equations, dg.volume_integral, dg, uEltype)...) - cache = (; cache..., create_cache(mesh, equations, dg.mortar, uEltype)...) - - return cache -end - -include("containers.jl") -include("containers_2d.jl") -include("containers_3d.jl") - + # This method is called when a SemidiscretizationHyperbolic is constructed. + # It constructs the basic `cache` used throughout the simulation to compute + # the RHS etc. + function create_cache(mesh::T8codeMesh, equations::AbstractEquations, dg::DG, ::Any, + ::Type{uEltype}) where {uEltype <: Real} + count_required_surfaces!(mesh) + + elements = init_elements(mesh, equations, dg.basis, uEltype) + interfaces = init_interfaces(mesh, equations, dg.basis, elements) + boundaries = init_boundaries(mesh, equations, dg.basis, elements) + mortars = init_mortars(mesh, equations, dg.basis, elements) + + trixi_t8_fill_mesh_info(mesh.forest, elements, interfaces, mortars, boundaries, + mesh.boundary_names) + + cache = (; elements, interfaces, boundaries, mortars) + + # Add specialized parts of the cache required to compute the volume integral etc. + cache = (; cache..., + create_cache(mesh, equations, dg.volume_integral, dg, uEltype)...) + cache = (; cache..., create_cache(mesh, equations, dg.mortar, uEltype)...) + + return cache + end + + include("containers.jl") + include("containers_2d.jl") + include("containers_3d.jl") end # @muladd diff --git a/src/solvers/dgsem_tree/dg_2d.jl b/src/solvers/dgsem_tree/dg_2d.jl index 2cc968b2ea..59af625781 100644 --- a/src/solvers/dgsem_tree/dg_2d.jl +++ b/src/solvers/dgsem_tree/dg_2d.jl @@ -36,12 +36,15 @@ end # The methods below are specialized on the volume integral type # and called from the basic `create_cache` method at the top. -function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, - equations, volume_integral::VolumeIntegralFluxDifferencing, dg::DG, uEltype) - NamedTuple() +function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, + P4estMesh{2}, T8codeMesh{2}}, + equations, volume_integral::VolumeIntegralFluxDifferencing, + dg::DG, uEltype) + NamedTuple() end -function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, equations, +function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, + P4estMesh{2}, T8codeMesh{2}}, equations, volume_integral::VolumeIntegralShockCapturingHG, dg::DG, uEltype) element_ids_dg = Int[] element_ids_dgfv = Int[] @@ -66,9 +69,10 @@ function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMe fstar1_L_threaded, fstar1_R_threaded, fstar2_L_threaded, fstar2_R_threaded) end -function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, equations, - volume_integral::VolumeIntegralPureLGLFiniteVolume, dg::DG, uEltype) - +function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, + P4estMesh{2}, T8codeMesh{2}}, equations, + volume_integral::VolumeIntegralPureLGLFiniteVolume, dg::DG, + uEltype) A3dp1_x = Array{uEltype, 3} A3dp1_y = Array{uEltype, 3} @@ -87,7 +91,8 @@ end # The methods below are specialized on the mortar type # and called from the basic `create_cache` method at the top. -function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, +function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, + P4estMesh{2}, T8codeMesh{2}}, equations, mortar_l2::LobattoLegendreMortarL2, uEltype) # TODO: Taal performance using different types MA2d = MArray{Tuple{nvariables(equations), nnodes(mortar_l2)}, uEltype, 2, @@ -174,7 +179,9 @@ function rhs!(du, u, t, end function calc_volume_integral!(du, u, - mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, + mesh::Union{TreeMesh{2}, StructuredMesh{2}, + UnstructuredMesh2D, P4estMesh{2}, + T8codeMesh{2}}, nonconservative_terms, equations, volume_integral::VolumeIntegralWeakForm, dg::DGSEM, cache) @@ -219,7 +226,9 @@ end # mapping terms, stored in `cache.elements.contravariant_vectors`, is peeled apart # from the evaluation of the physical fluxes in each Cartesian direction function calc_volume_integral!(du, u, - mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, + mesh::Union{TreeMesh{2}, StructuredMesh{2}, + UnstructuredMesh2D, P4estMesh{2}, + T8codeMesh{2}}, nonconservative_terms, equations, volume_integral::VolumeIntegralFluxDifferencing, dg::DGSEM, cache) @@ -314,7 +323,9 @@ end # TODO: Taal dimension agnostic function calc_volume_integral!(du, u, - mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, + mesh::Union{TreeMesh{2}, StructuredMesh{2}, + UnstructuredMesh2D, P4estMesh{2}, + T8codeMesh{2}}, nonconservative_terms, equations, volume_integral::VolumeIntegralShockCapturingHG, dg::DGSEM, cache) @@ -372,7 +383,9 @@ function calc_volume_integral!(du, u, end @inline function fv_kernel!(du, u, - mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}}, + mesh::Union{TreeMesh{2}, StructuredMesh{2}, + UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2} + }, nonconservative_terms, equations, volume_flux_fv, dg::DGSEM, cache, element, alpha = true) @unpack fstar1_L_threaded, fstar1_R_threaded, fstar2_L_threaded, fstar2_R_threaded = cache diff --git a/src/solvers/dgsem_tree/dg_3d.jl b/src/solvers/dgsem_tree/dg_3d.jl index 20c20d6427..8568ad7469 100644 --- a/src/solvers/dgsem_tree/dg_3d.jl +++ b/src/solvers/dgsem_tree/dg_3d.jl @@ -36,13 +36,15 @@ end # The methods below are specialized on the volume integral type # and called from the basic `create_cache` method at the top. -function create_cache(mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, +function create_cache(mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, + T8codeMesh{3}}, equations, volume_integral::VolumeIntegralFluxDifferencing, dg::DG, uEltype) NamedTuple() end -function create_cache(mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, +function create_cache(mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, + T8codeMesh{3}}, equations, volume_integral::VolumeIntegralShockCapturingHG, dg::DG, uEltype) element_ids_dg = Int[] @@ -79,9 +81,10 @@ function create_cache(mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, fstar2_L_threaded, fstar2_R_threaded, fstar3_L_threaded, fstar3_R_threaded) end -function create_cache(mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, equations, - volume_integral::VolumeIntegralPureLGLFiniteVolume, dg::DG, uEltype) - +function create_cache(mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, + T8codeMesh{3}}, equations, + volume_integral::VolumeIntegralPureLGLFiniteVolume, dg::DG, + uEltype) A4dp1_x = Array{uEltype, 4} A4dp1_y = Array{uEltype, 4} A4dp1_z = Array{uEltype, 4} @@ -111,7 +114,8 @@ end # The methods below are specialized on the mortar type # and called from the basic `create_cache` method at the top. -function create_cache(mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, +function create_cache(mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, + T8codeMesh{3}}, equations, mortar_l2::LobattoLegendreMortarL2, uEltype) # TODO: Taal compare performance of different types A3d = Array{uEltype, 3} @@ -208,7 +212,8 @@ function rhs!(du, u, t, end function calc_volume_integral!(du, u, - mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, + mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, + T8codeMesh{3}}, nonconservative_terms, equations, volume_integral::VolumeIntegralWeakForm, dg::DGSEM, cache) @@ -255,7 +260,8 @@ end end function calc_volume_integral!(du, u, - mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, + mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, + T8codeMesh{3}}, nonconservative_terms, equations, volume_integral::VolumeIntegralFluxDifferencing, dg::DGSEM, cache) @@ -368,7 +374,8 @@ end # TODO: Taal dimension agnostic function calc_volume_integral!(du, u, - mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, + mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, + T8codeMesh{3}}, nonconservative_terms, equations, volume_integral::VolumeIntegralShockCapturingHG, dg::DGSEM, cache) @@ -426,7 +433,8 @@ function calc_volume_integral!(du, u, end @inline function fv_kernel!(du, u, - mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, + mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, + T8codeMesh{3}}, nonconservative_terms, equations, volume_flux_fv, dg::DGSEM, cache, element, alpha = true) @unpack fstar1_L_threaded, fstar1_R_threaded, fstar2_L_threaded, fstar2_R_threaded, fstar3_L_threaded, fstar3_R_threaded = cache diff --git a/src/solvers/dgsem_tree/indicators_2d.jl b/src/solvers/dgsem_tree/indicators_2d.jl index c82dc57472..cc9382b81c 100644 --- a/src/solvers/dgsem_tree/indicators_2d.jl +++ b/src/solvers/dgsem_tree/indicators_2d.jl @@ -44,7 +44,7 @@ end modal = modal_threaded[Threads.threadid()] modal_tmp1 = modal_tmp1_threaded[Threads.threadid()] - # Calculate indicator variables at Gauss-Lobatto nodes + # Calculate indicator variables at Gauss-Lobatto nodes for j in eachnode(dg), i in eachnode(dg) u_local = get_node_vars(u, equations, dg, i, j, element) indicator[i, j] = indicator_hg.variable(u_local, equations) @@ -98,7 +98,8 @@ end end # Diffuse alpha values by setting each alpha to at least 50% of neighboring elements' alpha -function apply_smoothing!(mesh::Union{TreeMesh{2}, P4estMesh{2}, T8codeMesh{2}}, alpha, alpha_tmp, dg, +function apply_smoothing!(mesh::Union{TreeMesh{2}, P4estMesh{2}, T8codeMesh{2}}, alpha, + alpha_tmp, dg, cache) # Copy alpha values such that smoothing is indpedenent of the element access order alpha_tmp .= alpha diff --git a/src/solvers/dgsem_tree/indicators_3d.jl b/src/solvers/dgsem_tree/indicators_3d.jl index 49af3f3e26..61d7f770dc 100644 --- a/src/solvers/dgsem_tree/indicators_3d.jl +++ b/src/solvers/dgsem_tree/indicators_3d.jl @@ -101,7 +101,8 @@ end alpha[element] = min(alpha_max, alpha_element) end -function apply_smoothing!(mesh::Union{TreeMesh{3}, P4estMesh{3}, T8codeMesh{3}}, alpha, alpha_tmp, dg, +function apply_smoothing!(mesh::Union{TreeMesh{3}, P4estMesh{3}, T8codeMesh{3}}, alpha, + alpha_tmp, dg, cache) # Diffuse alpha values by setting each alpha to at least 50% of neighboring elements' alpha diff --git a/src/solvers/dgsem_unstructured/dg_2d.jl b/src/solvers/dgsem_unstructured/dg_2d.jl index 1540e14da4..7b8dafdddd 100644 --- a/src/solvers/dgsem_unstructured/dg_2d.jl +++ b/src/solvers/dgsem_unstructured/dg_2d.jl @@ -325,9 +325,10 @@ end # Iterate over tuples of boundary condition types and associated indices # in a type-stable way using "lispy tuple programming". -function calc_boundary_flux_by_type!(cache, t, BCs::NTuple{N,Any}, - BC_indices::NTuple{N,Vector{Int}}, - mesh::Union{UnstructuredMesh2D, P4estMesh, T8codeMesh}, +function calc_boundary_flux_by_type!(cache, t, BCs::NTuple{N, Any}, + BC_indices::NTuple{N, Vector{Int}}, + mesh::Union{UnstructuredMesh2D, P4estMesh, + T8codeMesh}, equations, surface_integral, dg::DG) where {N} # Extract the boundary condition type and index vector boundary_condition = first(BCs) @@ -350,7 +351,8 @@ end # terminate the type-stable iteration over tuples function calc_boundary_flux_by_type!(cache, t, BCs::Tuple{}, BC_indices::Tuple{}, - mesh::Union{UnstructuredMesh2D, P4estMesh, T8codeMesh}, + mesh::Union{UnstructuredMesh2D, P4estMesh, + T8codeMesh}, equations, surface_integral, dg::DG) nothing end From d238db721b05f46b42dd0c9725047c743ba17ac6 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 11 Jul 2023 16:09:20 +0200 Subject: [PATCH 054/128] Backup. --- .github/workflows/ci.yml | 1 + Project.toml | 2 +- ...ixir_advection_amr_solution_independent.jl | 145 ++++++------ .../elixir_advection_amr_unstructured_flag.jl | 65 +++--- .../t8code_2d_dgsem/elixir_advection_basic.jl | 33 ++- .../elixir_advection_nonconforming_flag.jl | 79 +++---- .../elixir_advection_unstructured_flag.jl | 45 ++-- .../elixir_euler_free_stream.jl | 99 ++++---- .../t8code_2d_dgsem/elixir_euler_sedov.jl | 79 +++---- .../elixir_euler_shockcapturing_ec.jl | 43 ++-- ...e_terms_nonconforming_unstructured_flag.jl | 11 - examples/t8code_2d_dgsem/elixir_mhd_rotor.jl | 127 +++++----- src/callbacks_step/amr.jl | 2 +- src/callbacks_step/amr_dg2d.jl | 16 +- src/meshes/mesh_io.jl | 2 +- src/meshes/t8code_mesh.jl | 1 + test/runtests.jl | 220 +++++++++--------- 17 files changed, 450 insertions(+), 520 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b0a2c93db3..4790f93d91 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,6 +69,7 @@ jobs: - structured - p4est_part1 - p4est_part2 + - t8code_part1 - unstructured_dgmulti - parabolic - paper_self_gravitating_gas_dynamics diff --git a/Project.toml b/Project.toml index 5e5dba436f..4ad8fe7abd 100644 --- a/Project.toml +++ b/Project.toml @@ -81,7 +81,7 @@ StaticArrays = "1" StrideArrays = "0.1.18" StructArrays = "0.6" SummationByPartsOperators = "0.5.25" -T8code = "0.3.0" +T8code = "0.4.0" TimerOutputs = "0.5" Triangulate = "2.0" TriplotBase = "0.1" diff --git a/examples/t8code_2d_dgsem/elixir_advection_amr_solution_independent.jl b/examples/t8code_2d_dgsem/elixir_advection_amr_solution_independent.jl index 12fc263cc0..653bab41e2 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_amr_solution_independent.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_amr_solution_independent.jl @@ -6,75 +6,74 @@ module TrixiExtension using Trixi -struct IndicatorSolutionIndependent{Cache<:NamedTuple} <: Trixi.AbstractIndicator - cache::Cache +struct IndicatorSolutionIndependent{Cache <: NamedTuple} <: Trixi.AbstractIndicator + cache::Cache end function IndicatorSolutionIndependent(semi) - basis = semi.solver.basis - alpha = Vector{real(basis)}() - cache = (; semi.mesh, alpha) - return IndicatorSolutionIndependent{typeof(cache)}(cache) + basis = semi.solver.basis + alpha = Vector{real(basis)}() + cache = (; semi.mesh, alpha) + return IndicatorSolutionIndependent{typeof(cache)}(cache) end -function (indicator::IndicatorSolutionIndependent)(u::AbstractArray{<:Any,4}, +function (indicator::IndicatorSolutionIndependent)(u::AbstractArray{<:Any, 4}, mesh, equations, dg, cache; t, kwargs...) - - mesh = indicator.cache.mesh - alpha = indicator.cache.alpha - resize!(alpha, nelements(dg, cache)) - - # Predict the theoretical center. - advection_velocity = (0.2, -0.7) - center = t.*advection_velocity - - inner_distance = 1 - outer_distance = 1.85 - - # Iterate over all elements. - for element in 1:length(alpha) - # Calculate periodic distance between cell and center. - # This requires an uncurved mesh! - coordinates = SVector(0.5 * (cache.elements.node_coordinates[1, 1, 1, element] + - cache.elements.node_coordinates[1, end, 1, element]), - 0.5 * (cache.elements.node_coordinates[2, 1, 1, element] + - cache.elements.node_coordinates[2, 1, end, element])) - - # The geometric shape of the amr should be preserved when the base_level is increased. - # This is done by looking at the original coordinates of each cell. - cell_coordinates = original_coordinates(coordinates, 5/8) - cell_distance = periodic_distance_2d(cell_coordinates, center, 10) - if cell_distance < (inner_distance+outer_distance)/2 - cell_coordinates = original_coordinates(coordinates, 5/16) - cell_distance = periodic_distance_2d(cell_coordinates, center, 10) + mesh = indicator.cache.mesh + alpha = indicator.cache.alpha + resize!(alpha, nelements(dg, cache)) + + # Predict the theoretical center. + advection_velocity = (0.2, -0.7) + center = t .* advection_velocity + + inner_distance = 1 + outer_distance = 1.85 + + # Iterate over all elements. + for element in 1:length(alpha) + # Calculate periodic distance between cell and center. + # This requires an uncurved mesh! + coordinates = SVector(0.5 * (cache.elements.node_coordinates[1, 1, 1, element] + + cache.elements.node_coordinates[1, end, 1, element]), + 0.5 * (cache.elements.node_coordinates[2, 1, 1, element] + + cache.elements.node_coordinates[2, 1, end, element])) + + # The geometric shape of the amr should be preserved when the base_level is increased. + # This is done by looking at the original coordinates of each cell. + cell_coordinates = original_coordinates(coordinates, 5 / 8) + cell_distance = periodic_distance_2d(cell_coordinates, center, 10) + if cell_distance < (inner_distance + outer_distance) / 2 + cell_coordinates = original_coordinates(coordinates, 5 / 16) + cell_distance = periodic_distance_2d(cell_coordinates, center, 10) + end + + # Set alpha according to cells position inside the circles. + target_level = (cell_distance < inner_distance) + (cell_distance < outer_distance) + alpha[element] = target_level / 2 end - - # Set alpha according to cells position inside the circles. - target_level = (cell_distance < inner_distance) + (cell_distance < outer_distance) - alpha[element] = target_level/2 - end - return alpha + return alpha end # For periodic domains, distance between two points must take into account # periodic extensions of the domain. function periodic_distance_2d(coordinates, center, domain_length) - dx = coordinates .- center - dx_shifted = abs.(dx .% domain_length) - dx_periodic = min.(dx_shifted, domain_length .- dx_shifted) - return sqrt(sum(dx_periodic.^2)) + dx = coordinates .- center + dx_shifted = abs.(dx .% domain_length) + dx_periodic = min.(dx_shifted, domain_length .- dx_shifted) + return sqrt(sum(dx_periodic .^ 2)) end # This takes a cells coordinates and transforms them into the coordinates of a # parent-cell it originally refined from. It does it so that the parent-cell # has given cell_length. function original_coordinates(coordinates, cell_length) - offset = coordinates .% cell_length - offset_sign = sign.(offset) - border = coordinates - offset - center = border + (offset_sign .* cell_length/2) - return center + offset = coordinates .% cell_length + offset_sign = sign.(offset) + border = coordinates - offset + center = border + (offset_sign .* cell_length / 2) + return center end end # module TrixiExtension @@ -89,18 +88,18 @@ equations = LinearScalarAdvectionEquation2D(advection_velocity) initial_condition = initial_condition_gauss -solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) +solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs) coordinates_min = (-5.0, -5.0) -coordinates_max = ( 5.0, 5.0) +coordinates_max = (5.0, 5.0) mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) trees_per_dimension = (1, 1) -mesh = T8codeMesh(trees_per_dimension, polydeg=3, - mapping=mapping, - initial_refinement_level=1) +mesh = T8codeMesh(trees_per_dimension, polydeg = 3, + mapping = mapping, + initial_refinement_level = 1) semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) @@ -113,38 +112,32 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() analysis_interval = 100 -analysis_callback = AnalysisCallback(semi, interval=analysis_interval, - extra_analysis_integrals=(entropy,)) - -alive_callback = AliveCallback(analysis_interval=analysis_interval) +analysis_callback = AnalysisCallback(semi, interval = analysis_interval, + extra_analysis_integrals = (entropy,)) -# Not implemented yet. -# save_solution = SaveSolutionCallback(interval=100, -# save_initial_solution=true, -# save_final_solution=true, -# solution_variables=cons2prim) +alive_callback = AliveCallback(analysis_interval = analysis_interval) -amr_controller = ControllerThreeLevel(semi, TrixiExtension.IndicatorSolutionIndependent(semi), - base_level=4, - med_level=5, med_threshold=0.1, - max_level=6, max_threshold=0.6) +amr_controller = ControllerThreeLevel(semi, + TrixiExtension.IndicatorSolutionIndependent(semi), + base_level = 4, + med_level = 5, med_threshold = 0.1, + max_level = 6, max_threshold = 0.6) amr_callback = AMRCallback(semi, amr_controller, - interval=5, - adapt_initial_condition=true, - adapt_initial_condition_only_refine=true) + interval = 5, + adapt_initial_condition = true, + adapt_initial_condition_only_refine = true) -stepsize_callback = StepsizeCallback(cfl=1.6) +stepsize_callback = StepsizeCallback(cfl = 1.6) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, - # save_solution, amr_callback, stepsize_callback); ############################################################################### # Run the simulation. -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep = false, callback = callbacks); summary_callback() # print the timer summary diff --git a/examples/t8code_2d_dgsem/elixir_advection_amr_unstructured_flag.jl b/examples/t8code_2d_dgsem/elixir_advection_amr_unstructured_flag.jl index 49406b44c5..adf1d009a5 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_amr_unstructured_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_amr_unstructured_flag.jl @@ -11,18 +11,16 @@ equations = LinearScalarAdvectionEquation2D(advection_velocity) initial_condition = initial_condition_gauss boundary_condition = BoundaryConditionDirichlet(initial_condition) -boundary_conditions = Dict( - :all => boundary_condition -) +boundary_conditions = Dict(:all => boundary_condition) -solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) +solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs) # Deformed rectangle that looks like a waving flag, lower and upper faces are # sinus curves, left and right are vertical lines. f1(s) = SVector(-5.0, 5 * s - 5.0) -f2(s) = SVector( 5.0, 5 * s + 5.0) +f2(s) = SVector(5.0, 5 * s + 5.0) f3(s) = SVector(5 * s, -5.0 + 5 * sin(0.5 * pi * s)) -f4(s) = SVector(5 * s, 5.0 + 5 * sin(0.5 * pi * s)) +f4(s) = SVector(5 * s, 5.0 + 5 * sin(0.5 * pi * s)) faces = (f1, f2, f3, f4) # This creates a mapping that transforms [-1, 1]^2 to the domain with the faces @@ -34,20 +32,21 @@ mapping_flag = Trixi.transfinite_mapping(faces) # Unstructured mesh with 24 cells of the square domain [-1, 1]^n mesh_file = joinpath(@__DIR__, "square_unstructured_2.inp") -isfile(mesh_file) || download("https://gist.githubusercontent.com/efaulhaber/63ff2ea224409e55ee8423b3a33e316a/raw/7db58af7446d1479753ae718930741c47a3b79b7/square_unstructured_2.inp", - mesh_file) +isfile(mesh_file) || + download("https://gist.githubusercontent.com/efaulhaber/63ff2ea224409e55ee8423b3a33e316a/raw/7db58af7446d1479753ae718930741c47a3b79b7/square_unstructured_2.inp", + mesh_file) # INP mesh files are only support by p4est. Hence, we # create a p4est connecvity object first from which # we can create a t8code mesh. -conn = Trixi.read_inp_p4est(mesh_file,Val(2)) +conn = Trixi.read_inp_p4est(mesh_file, Val(2)) -mesh = T8codeMesh{2}(conn, polydeg=3, - mapping=mapping_flag, - initial_refinement_level=1) +mesh = T8codeMesh{2}(conn, polydeg = 3, + mapping = mapping_flag, + initial_refinement_level = 1) semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, - boundary_conditions=boundary_conditions) + boundary_conditions = boundary_conditions) ############################################################################### # ODE solvers, callbacks etc. @@ -58,43 +57,31 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() analysis_interval = 100 -analysis_callback = AnalysisCallback(semi, interval=analysis_interval, - extra_analysis_integrals=(entropy,)) +analysis_callback = AnalysisCallback(semi, interval = analysis_interval, + extra_analysis_integrals = (entropy,)) -alive_callback = AliveCallback(analysis_interval=analysis_interval) +alive_callback = AliveCallback(analysis_interval = analysis_interval) -# Not implemented yet. -# save_restart = SaveRestartCallback(interval=100, -# save_final_restart=true) - -# Not implemented yet. -# save_solution = SaveSolutionCallback(interval=100, -# save_initial_solution=true, -# save_final_solution=true, -# solution_variables=cons2prim) - -amr_controller = ControllerThreeLevel(semi, IndicatorMax(semi, variable=first), - base_level=1, - med_level=2, med_threshold=0.1, - max_level=3, max_threshold=0.6) +amr_controller = ControllerThreeLevel(semi, IndicatorMax(semi, variable = first), + base_level = 1, + med_level = 2, med_threshold = 0.1, + max_level = 3, max_threshold = 0.6) amr_callback = AMRCallback(semi, amr_controller, - interval=5, - adapt_initial_condition=true, - adapt_initial_condition_only_refine=true) + interval = 5, + adapt_initial_condition = true, + adapt_initial_condition_only_refine = true) -stepsize_callback = StepsizeCallback(cfl=0.7) +stepsize_callback = StepsizeCallback(cfl = 0.7) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, - # save_restart, save_solution, amr_callback, stepsize_callback) - ############################################################################### # Run the simulation. -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - dt=1, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), + dt = 1, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep = false, callback = callbacks); summary_callback() # print the timer summary diff --git a/examples/t8code_2d_dgsem/elixir_advection_basic.jl b/examples/t8code_2d_dgsem/elixir_advection_basic.jl index e9827db8c9..efc5122658 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_basic.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_basic.jl @@ -11,22 +11,22 @@ advection_velocity = (0.2, -0.7) equations = LinearScalarAdvectionEquation2D(advection_velocity) # Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux -solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) +solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs) coordinates_min = (-1.0, -1.0) # minimum coordinates (min(x), min(y)) -coordinates_max = ( 1.0, 1.0) # maximum coordinates (max(x), max(y)) +coordinates_max = (1.0, 1.0) # maximum coordinates (max(x), max(y)) mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) trees_per_dimension = (8, 8) -mesh = T8codeMesh(trees_per_dimension, polydeg=3, - mapping=mapping, - initial_refinement_level=1) +mesh = T8codeMesh(trees_per_dimension, polydeg = 3, + mapping = mapping, + initial_refinement_level = 1) # A semidiscretization collects data structures and functions for the spatial discretization -semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_convergence_test, solver) - +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_convergence_test, + solver) ############################################################################### # ODE solvers, callbacks etc. @@ -39,28 +39,21 @@ ode = semidiscretize(semi, (0.0, 1.0)); summary_callback = SummaryCallback() # The AnalysisCallback allows to analyse the solution in regular intervals and prints the results -analysis_callback = AnalysisCallback(semi, interval=100) - -# Not implemented yet. -# # The SaveSolutionCallback allows to save the solution to a file in regular intervals -# save_solution = SaveSolutionCallback(interval=100, -# solution_variables=cons2prim) +analysis_callback = AnalysisCallback(semi, interval = 100) # The StepsizeCallback handles the re-calculation of the maximum Δt after each time step -stepsize_callback = StepsizeCallback(cfl=1.6) +stepsize_callback = StepsizeCallback(cfl = 1.6) # Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver -callbacks = CallbackSet(summary_callback, analysis_callback, # save_solution, - stepsize_callback) - +callbacks = CallbackSet(summary_callback, analysis_callback, stepsize_callback) ############################################################################### # run the simulation # OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep = false, callback = callbacks); # Print the timer summary summary_callback() diff --git a/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl b/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl index ef42eb8d77..ce9e612943 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl @@ -8,14 +8,14 @@ advection_velocity = (0.2, -0.7) equations = LinearScalarAdvectionEquation2D(advection_velocity) # Create DG solver with polynomial degree = 4 and (local) Lax-Friedrichs/Rusanov flux as surface flux -solver = DGSEM(polydeg=4, surface_flux=flux_lax_friedrichs) +solver = DGSEM(polydeg = 4, surface_flux = flux_lax_friedrichs) # Deformed rectangle that looks like a waving flag, # lower and upper faces are sinus curves, left and right are vertical lines. f1(s) = SVector(-1.0, s - 1.0) -f2(s) = SVector( 1.0, s + 1.0) +f2(s) = SVector(1.0, s + 1.0) f3(s) = SVector(s, -1.0 + sin(0.5 * pi * s)) -f4(s) = SVector(s, 1.0 + sin(0.5 * pi * s)) +f4(s) = SVector(s, 1.0 + sin(0.5 * pi * s)) faces = (f1, f2, f3, f4) mapping = Trixi.transfinite_mapping(faces) @@ -23,38 +23,37 @@ mapping = Trixi.transfinite_mapping(faces) # Create P4estMesh with 3 x 2 trees and 6 x 4 elements, # approximate the geometry with a smaller polydeg for testing. trees_per_dimension = (3, 2) -mesh = T8codeMesh(trees_per_dimension, polydeg=3, - mapping=mapping, - initial_refinement_level=1) +mesh = T8codeMesh(trees_per_dimension, polydeg = 3, + mapping = mapping, + initial_refinement_level = 1) function adapt_callback(forest, forest_from, which_tree, lelement_id, ts, - is_family, + is_family, num_elements, - elements_ptr) :: Cint + elements_ptr)::Cint + vertex = Vector{Cdouble}(undef, 3) - vertex = Vector{Cdouble}(undef,3) + elements = unsafe_wrap(Array, elements_ptr, num_elements) - elements = unsafe_wrap(Array, elements_ptr, num_elements) + Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) - Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) + level = Trixi.t8_element_level(ts, elements[1]) - level = Trixi.t8_element_level(ts,elements[1]) - - # TODO: Make this condition more general. - if vertex[1] < 1e-8 && vertex[2] < 1e-8 && level < 4 - # return true (refine) - return 1 - else - # return false (don't refine) - return 0 - end + # TODO: Make this condition more general. + if vertex[1] < 1e-8 && vertex[2] < 1e-8 && level < 4 + # return true (refine) + return 1 + else + # return false (don't refine) + return 0 + end end -@Trixi.T8_ASSERT(Trixi.t8_forest_is_committed(mesh.forest) != 0); +Trixi.@T8_ASSERT(Trixi.t8_forest_is_committed(mesh.forest)!=0); # Init new forest. new_forest_ref = Ref{Trixi.t8_forest_t}() @@ -62,19 +61,20 @@ Trixi.t8_forest_init(new_forest_ref); new_forest = new_forest_ref[] let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0 - Trixi.t8_forest_set_user_data(new_forest, C_NULL) - Trixi.t8_forest_set_adapt(new_forest, mesh.forest, @Trixi.t8_adapt_callback(adapt_callback), recursive) - Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) - Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - Trixi.t8_forest_set_ghost(new_forest, 1, Trixi.T8_GHOST_FACES); - Trixi.t8_forest_commit(new_forest) + Trixi.t8_forest_set_user_data(new_forest, C_NULL) + Trixi.t8_forest_set_adapt(new_forest, mesh.forest, + Trixi.@t8_adapt_callback(adapt_callback), recursive) + Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) + Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) + Trixi.t8_forest_set_ghost(new_forest, 1, Trixi.T8_GHOST_FACES) + Trixi.t8_forest_commit(new_forest) end mesh.forest = new_forest # A semidiscretization collects data structures and functions for the spatial discretization -semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_convergence_test, solver) - +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_convergence_test, + solver) ############################################################################### # ODE solvers, callbacks etc. @@ -87,28 +87,21 @@ ode = semidiscretize(semi, (0.0, 0.2)); summary_callback = SummaryCallback() # The AnalysisCallback allows to analyse the solution in regular intervals and prints the results -analysis_callback = AnalysisCallback(semi, interval=100) - -# Not implemented yet. -# # The SaveSolutionCallback allows to save the solution to a file in regular intervals -# save_solution = SaveSolutionCallback(interval=100, -# solution_variables=cons2prim) +analysis_callback = AnalysisCallback(semi, interval = 100) # The StepsizeCallback handles the re-calculation of the maximum Δt after each time step -stepsize_callback = StepsizeCallback(cfl=1.6) +stepsize_callback = StepsizeCallback(cfl = 1.6) # Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver -callbacks = CallbackSet(summary_callback, analysis_callback, # save_solution, - stepsize_callback) - +callbacks = CallbackSet(summary_callback, analysis_callback, stepsize_callback) ############################################################################### # run the simulation # OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep = false, callback = callbacks); # Print the timer summary summary_callback() diff --git a/examples/t8code_2d_dgsem/elixir_advection_unstructured_flag.jl b/examples/t8code_2d_dgsem/elixir_advection_unstructured_flag.jl index e0e0dd3e5c..df9cbc26f6 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_unstructured_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_unstructured_flag.jl @@ -11,19 +11,17 @@ equations = LinearScalarAdvectionEquation2D(advection_velocity) initial_condition = initial_condition_convergence_test boundary_condition = BoundaryConditionDirichlet(initial_condition) -boundary_conditions = Dict( - :all => boundary_condition -) +boundary_conditions = Dict(:all => boundary_condition) # Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux. -solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) +solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs) # Deformed rectangle that looks like a waving flag, # lower and upper faces are sinus curves, left and right are vertical lines. f1(s) = SVector(-1.0, s - 1.0) -f2(s) = SVector( 1.0, s + 1.0) +f2(s) = SVector(1.0, s + 1.0) f3(s) = SVector(s, -1.0 + sin(0.5 * pi * s)) -f4(s) = SVector(s, 1.0 + sin(0.5 * pi * s)) +f4(s) = SVector(s, 1.0 + sin(0.5 * pi * s)) faces = (f1, f2, f3, f4) Trixi.validate_faces(faces) @@ -31,20 +29,22 @@ mapping_flag = Trixi.transfinite_mapping(faces) # Unstructured mesh with 24 cells of the square domain [-1, 1]^n. mesh_file = joinpath(@__DIR__, "square_unstructured_2.inp") -isfile(mesh_file) || download("https://gist.githubusercontent.com/efaulhaber/63ff2ea224409e55ee8423b3a33e316a/raw/7db58af7446d1479753ae718930741c47a3b79b7/square_unstructured_2.inp", - mesh_file) +isfile(mesh_file) || + download("https://gist.githubusercontent.com/efaulhaber/63ff2ea224409e55ee8423b3a33e316a/raw/7db58af7446d1479753ae718930741c47a3b79b7/square_unstructured_2.inp", + mesh_file) # INP mesh files are only support by p4est. Hence, we # create a p4est connecvity object first from which # we can create a t8code mesh. -conn = Trixi.read_inp_p4est(mesh_file,Val(2)) +conn = Trixi.read_inp_p4est(mesh_file, Val(2)) -mesh = T8codeMesh{2}(conn, polydeg=3, - mapping=mapping_flag, - initial_refinement_level=2) +mesh = T8codeMesh{2}(conn, polydeg = 3, + mapping = mapping_flag, + initial_refinement_level = 2) # A semidiscretization collects data structures and functions for the spatial discretization. -semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, boundary_conditions=boundary_conditions) +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, + boundary_conditions = boundary_conditions) ############################################################################### # ODE solvers, callbacks etc. @@ -59,30 +59,23 @@ summary_callback = SummaryCallback() # The AnalysisCallback allows to analyse the solution in regular intervals and # prints the results. -analysis_callback = AnalysisCallback(semi, interval=100) - -# Not implemented yet. -# # The SaveSolutionCallback allows to save the solution to a file in regular -# # intervals. -# save_solution = SaveSolutionCallback(interval=100, -# solution_variables=cons2prim) +analysis_callback = AnalysisCallback(semi, interval = 100) # The StepsizeCallback handles the re-calculation of the maximum Δt after each # time step. -stepsize_callback = StepsizeCallback(cfl=1.4) +stepsize_callback = StepsizeCallback(cfl = 1.4) # Create a CallbackSet to collect all callbacks such that they can be passed to # the ODE solver. -callbacks = CallbackSet(summary_callback, analysis_callback, # save_solution, - stepsize_callback) +callbacks = CallbackSet(summary_callback, analysis_callback, stepsize_callback) ############################################################################### # Run the simulation. # OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks. -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - dt=1.0, # Solve needs some value here but it will be overwritten by the stepsize_callback. - save_everystep=false, callback=callbacks); +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), + dt = 1.0, # Solve needs some value here but it will be overwritten by the stepsize_callback. + save_everystep = false, callback = callbacks); # Print the timer summary. summary_callback() diff --git a/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl b/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl index 38addfdcde..114a112221 100644 --- a/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl +++ b/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl @@ -9,21 +9,21 @@ equations = CompressibleEulerEquations2D(1.4) initial_condition = initial_condition_constant -solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) +solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs) # Mapping as described in https://arxiv.org/abs/2012.12040 but reduced to 2D function mapping(xi_, eta_) - # Transform input variables between -1 and 1 onto [0,3] - xi = 1.5 * xi_ + 1.5 - eta = 1.5 * eta_ + 1.5 + # Transform input variables between -1 and 1 onto [0,3] + xi = 1.5 * xi_ + 1.5 + eta = 1.5 * eta_ + 1.5 - y = eta + 3/8 * (cos(1.5 * pi * (2 * xi - 3)/3) * - cos(0.5 * pi * (2 * eta - 3)/3)) + y = eta + 3 / 8 * (cos(1.5 * pi * (2 * xi - 3) / 3) * + cos(0.5 * pi * (2 * eta - 3) / 3)) - x = xi + 3/8 * (cos(0.5 * pi * (2 * xi - 3)/3) * - cos(2 * pi * (2 * y - 3)/3)) + x = xi + 3 / 8 * (cos(0.5 * pi * (2 * xi - 3) / 3) * + cos(2 * pi * (2 * y - 3) / 3)) - return SVector(x, y) + return SVector(x, y) end ############################################################################### @@ -31,46 +31,46 @@ end # Unstructured mesh with 48 cells of the square domain [-1, 1]^n mesh_file = joinpath(@__DIR__, "square_unstructured_1.inp") -isfile(mesh_file) || download("https://gist.githubusercontent.com/efaulhaber/a075f8ec39a67fa9fad8f6f84342cbca/raw/a7206a02ed3a5d3cadacd8d9694ac154f9151db7/square_unstructured_1.inp", - mesh_file) +isfile(mesh_file) || + download("https://gist.githubusercontent.com/efaulhaber/a075f8ec39a67fa9fad8f6f84342cbca/raw/a7206a02ed3a5d3cadacd8d9694ac154f9151db7/square_unstructured_1.inp", + mesh_file) # INP mesh files are only support by p4est. Hence, we # create a p4est connecvity object first from which # we can create a t8code mesh. -conn = Trixi.read_inp_p4est(mesh_file,Val(2)) +conn = Trixi.read_inp_p4est(mesh_file, Val(2)) -mesh = T8codeMesh{2}(conn, polydeg=3, - mapping=mapping, - initial_refinement_level=1) +mesh = T8codeMesh{2}(conn, polydeg = 3, + mapping = mapping, + initial_refinement_level = 1) function adapt_callback(forest, forest_from, which_tree, lelement_id, ts, - is_family, + is_family, num_elements, - elements_ptr) :: Cint + elements_ptr)::Cint + vertex = Vector{Cdouble}(undef, 3) - vertex = Vector{Cdouble}(undef,3) + elements = unsafe_wrap(Array, elements_ptr, num_elements) - elements = unsafe_wrap(Array, elements_ptr, num_elements) + Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) - Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) + level = Trixi.t8_element_level(ts, elements[1]) - level = Trixi.t8_element_level(ts,elements[1]) - - # TODO: Make this condition more general. - if vertex[1] < 1e-8 && vertex[2] < 1e-8 && level < 3 - # return true (refine) - return 1 - else - # return false (don't refine) - return 0 - end + # TODO: Make this condition more general. + if vertex[1] < 1e-8 && vertex[2] < 1e-8 && level < 3 + # return true (refine) + return 1 + else + # return false (don't refine) + return 0 + end end -@Trixi.T8_ASSERT(Trixi.t8_forest_is_committed(mesh.forest) != 0); +Trixi.@T8_ASSERT(Trixi.t8_forest_is_committed(mesh.forest)!=0); # Init new forest. new_forest_ref = Ref{Trixi.t8_forest_t}() @@ -78,21 +78,19 @@ Trixi.t8_forest_init(new_forest_ref); new_forest = new_forest_ref[] let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0 - Trixi.t8_forest_set_user_data(new_forest, C_NULL) - Trixi.t8_forest_set_adapt(new_forest, mesh.forest, @Trixi.t8_adapt_callback(adapt_callback), recursive) - Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) - Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - Trixi.t8_forest_set_ghost(new_forest, 1, Trixi.T8_GHOST_FACES); - Trixi.t8_forest_commit(new_forest) + Trixi.t8_forest_set_user_data(new_forest, C_NULL) + Trixi.t8_forest_set_adapt(new_forest, mesh.forest, + Trixi.@t8_adapt_callback(adapt_callback), recursive) + Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) + Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) + Trixi.t8_forest_set_ghost(new_forest, 1, Trixi.T8_GHOST_FACES) + Trixi.t8_forest_commit(new_forest) end mesh.forest = new_forest semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, - boundary_conditions=Dict( - :all => BoundaryConditionDirichlet(initial_condition) - )) - + boundary_conditions = Dict(:all => BoundaryConditionDirichlet(initial_condition))) ############################################################################### # ODE solvers, callbacks etc. @@ -103,27 +101,20 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() analysis_interval = 100 -analysis_callback = AnalysisCallback(semi, interval=analysis_interval) - -alive_callback = AliveCallback(analysis_interval=analysis_interval) +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) -# Not implemented yet. -# save_solution = SaveSolutionCallback(interval=100, -# save_initial_solution=true, -# save_final_solution=true, -# solution_variables=cons2prim) +alive_callback = AliveCallback(analysis_interval = analysis_interval) -stepsize_callback = StepsizeCallback(cfl=2.0) +stepsize_callback = StepsizeCallback(cfl = 2.0) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, - # save_solution, stepsize_callback) ############################################################################### # run the simulation -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep = false, callback = callbacks); summary_callback() # print the timer summary diff --git a/examples/t8code_2d_dgsem/elixir_euler_sedov.jl b/examples/t8code_2d_dgsem/elixir_euler_sedov.jl index 117c54804b..965d794f8d 100644 --- a/examples/t8code_2d_dgsem/elixir_euler_sedov.jl +++ b/examples/t8code_2d_dgsem/elixir_euler_sedov.jl @@ -13,25 +13,25 @@ The Sedov blast wave setup based on Flash - http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000 """ function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations2D) - # Set up polar coordinates - inicenter = SVector(0.0, 0.0) - x_norm = x[1] - inicenter[1] - y_norm = x[2] - inicenter[2] - r = sqrt(x_norm^2 + y_norm^2) - - # Setup based on http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000 - r0 = 0.21875 # = 3.5 * smallest dx (for domain length=4 and max-ref=6) - E = 1.0 - p0_inner = 3 * (equations.gamma - 1) * E / (3 * pi * r0^2) - p0_outer = 1.0e-5 # = true Sedov setup - - # Calculate primitive variables - rho = 1.0 - v1 = 0.0 - v2 = 0.0 - p = r > r0 ? p0_outer : p0_inner - - return prim2cons(SVector(rho, v1, v2, p), equations) + # Set up polar coordinates + inicenter = SVector(0.0, 0.0) + x_norm = x[1] - inicenter[1] + y_norm = x[2] - inicenter[2] + r = sqrt(x_norm^2 + y_norm^2) + + # Setup based on http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000 + r0 = 0.21875 # = 3.5 * smallest dx (for domain length=4 and max-ref=6) + E = 1.0 + p0_inner = 3 * (equations.gamma - 1) * E / (3 * pi * r0^2) + p0_outer = 1.0e-5 # = true Sedov setup + + # Calculate primitive variables + rho = 1.0 + v1 = 0.0 + v2 = 0.0 + p = r > r0 ? p0_outer : p0_inner + + return prim2cons(SVector(rho, v1, v2, p), equations) end initial_condition = initial_condition_sedov_blast_wave @@ -42,28 +42,29 @@ volume_flux = flux_ranocha polydeg = 4 basis = LobattoLegendreBasis(polydeg) indicator_sc = IndicatorHennemannGassner(equations, basis, - alpha_max=1.0, - alpha_min=0.001, - alpha_smooth=true, - variable=density_pressure) + alpha_max = 1.0, + alpha_min = 0.001, + alpha_smooth = true, + variable = density_pressure) volume_integral = VolumeIntegralShockCapturingHG(indicator_sc; - volume_flux_dg=volume_flux, - volume_flux_fv=surface_flux) + volume_flux_dg = volume_flux, + volume_flux_fv = surface_flux) -solver = DGSEM(polydeg=polydeg, surface_flux=surface_flux, volume_integral=volume_integral) +solver = DGSEM(polydeg = polydeg, surface_flux = surface_flux, + volume_integral = volume_integral) ############################################################################### coordinates_min = (-1.0, -1.0) -coordinates_max = ( 1.0, 1.0) +coordinates_max = (1.0, 1.0) mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) trees_per_dimension = (4, 4) -mesh = T8codeMesh(trees_per_dimension, polydeg=4, - mapping=mapping, - initial_refinement_level=2, periodicity=true) +mesh = T8codeMesh(trees_per_dimension, polydeg = 4, + mapping = mapping, + initial_refinement_level = 2, periodicity = true) semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) @@ -76,27 +77,21 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() analysis_interval = 300 -analysis_callback = AnalysisCallback(semi, interval=analysis_interval) +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) -alive_callback = AliveCallback(analysis_interval=analysis_interval) +alive_callback = AliveCallback(analysis_interval = analysis_interval) -# Not implemented yet. -# save_solution = SaveSolutionCallback(interval=300, -# save_initial_solution=true, -# save_final_solution=true) - -stepsize_callback = StepsizeCallback(cfl=0.5) +stepsize_callback = StepsizeCallback(cfl = 0.5) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, - # save_solution, stepsize_callback) ############################################################################### # run the simulation -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep = false, callback = callbacks); summary_callback() # print the timer summary diff --git a/examples/t8code_2d_dgsem/elixir_euler_shockcapturing_ec.jl b/examples/t8code_2d_dgsem/elixir_euler_shockcapturing_ec.jl index e80d400fae..55a9063a00 100644 --- a/examples/t8code_2d_dgsem/elixir_euler_shockcapturing_ec.jl +++ b/examples/t8code_2d_dgsem/elixir_euler_shockcapturing_ec.jl @@ -13,28 +13,29 @@ volume_flux = flux_ranocha polydeg = 4 basis = LobattoLegendreBasis(polydeg) indicator_sc = IndicatorHennemannGassner(equations, basis, - alpha_max=1.0, - alpha_min=0.001, - alpha_smooth=true, - variable=density_pressure) + alpha_max = 1.0, + alpha_min = 0.001, + alpha_smooth = true, + variable = density_pressure) volume_integral = VolumeIntegralShockCapturingHG(indicator_sc; - volume_flux_dg=volume_flux, - volume_flux_fv=surface_flux) + volume_flux_dg = volume_flux, + volume_flux_fv = surface_flux) -solver = DGSEM(polydeg=polydeg, surface_flux=surface_flux, volume_integral=volume_integral) +solver = DGSEM(polydeg = polydeg, surface_flux = surface_flux, + volume_integral = volume_integral) ############################################################################### coordinates_min = (-1.0, -1.0) -coordinates_max = ( 1.0, 1.0) +coordinates_max = (1.0, 1.0) mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) trees_per_dimension = (4, 4) -mesh = T8codeMesh(trees_per_dimension, polydeg=4, - mapping=mapping, - initial_refinement_level=2,periodicity=true) +mesh = T8codeMesh(trees_per_dimension, polydeg = 4, + mapping = mapping, + initial_refinement_level = 2, periodicity = true) semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) @@ -47,27 +48,21 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() analysis_interval = 100 -analysis_callback = AnalysisCallback(semi, interval=analysis_interval) +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) -alive_callback = AliveCallback(analysis_interval=analysis_interval) +alive_callback = AliveCallback(analysis_interval = analysis_interval) -# Not implemented yet. -# save_solution = SaveSolutionCallback(interval=100, -# save_initial_solution=true, -# save_final_solution=true, -# solution_variables=cons2prim) - -stepsize_callback = StepsizeCallback(cfl=1.0) +stepsize_callback = StepsizeCallback(cfl = 1.0) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, - # save_solution, stepsize_callback) + ############################################################################### # run the simulation -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep = false, callback = callbacks); summary_callback() # print the timer summary diff --git a/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl b/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl index d6d37a1cdc..2fd5840a43 100644 --- a/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl @@ -106,21 +106,10 @@ analysis_callback = AnalysisCallback(semi, interval = analysis_interval) alive_callback = AliveCallback(analysis_interval = analysis_interval) -# Not implemented yet. -# save_restart = SaveRestartCallback(interval=100, -# save_final_restart=true) -# -# Not implemented yet. -# save_solution = SaveSolutionCallback(interval=100, -# save_initial_solution=true, -# save_final_solution=true, -# solution_variables=cons2prim) - stepsize_callback = StepsizeCallback(cfl = 0.8) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, - # save_restart, save_solution, stepsize_callback) ############################################################################### # run the simulation diff --git a/examples/t8code_2d_dgsem/elixir_mhd_rotor.jl b/examples/t8code_2d_dgsem/elixir_mhd_rotor.jl index 606eda2125..9a4bd99e44 100644 --- a/examples/t8code_2d_dgsem/elixir_mhd_rotor.jl +++ b/examples/t8code_2d_dgsem/elixir_mhd_rotor.jl @@ -15,78 +15,78 @@ The classical MHD rotor test case. Here, the setup is taken from [doi: 10.1365/s13291-018-0178-9](https://doi.org/10.1365/s13291-018-0178-9) """ function initial_condition_rotor(x, t, equations::IdealGlmMhdEquations2D) - # setup taken from Derigs et al. DMV article (2018) - # domain must be [0, 1] x [0, 1], γ = 1.4 - dx = x[1] - 0.5 - dy = x[2] - 0.5 - r = sqrt(dx^2 + dy^2) - f = (0.115 - r)/0.015 - if r <= 0.1 - rho = 10.0 - v1 = -20.0*dy - v2 = 20.0*dx - elseif r >= 0.115 - rho = 1.0 - v1 = 0.0 - v2 = 0.0 - else - rho = 1.0 + 9.0*f - v1 = -20.0*f*dy - v2 = 20.0*f*dx - end - v3 = 0.0 - p = 1.0 - B1 = 5.0/sqrt(4.0*pi) - B2 = 0.0 - B3 = 0.0 - psi = 0.0 - return prim2cons(SVector(rho, v1, v2, v3, p, B1, B2, B3, psi), equations) + # setup taken from Derigs et al. DMV article (2018) + # domain must be [0, 1] x [0, 1], γ = 1.4 + dx = x[1] - 0.5 + dy = x[2] - 0.5 + r = sqrt(dx^2 + dy^2) + f = (0.115 - r) / 0.015 + if r <= 0.1 + rho = 10.0 + v1 = -20.0 * dy + v2 = 20.0 * dx + elseif r >= 0.115 + rho = 1.0 + v1 = 0.0 + v2 = 0.0 + else + rho = 1.0 + 9.0 * f + v1 = -20.0 * f * dy + v2 = 20.0 * f * dx + end + v3 = 0.0 + p = 1.0 + B1 = 5.0 / sqrt(4.0 * pi) + B2 = 0.0 + B3 = 0.0 + psi = 0.0 + return prim2cons(SVector(rho, v1, v2, v3, p, B1, B2, B3, psi), equations) end initial_condition = initial_condition_rotor surface_flux = (flux_lax_friedrichs, flux_nonconservative_powell) -volume_flux = (flux_hindenlang_gassner, flux_nonconservative_powell) +volume_flux = (flux_hindenlang_gassner, flux_nonconservative_powell) polydeg = 4 basis = LobattoLegendreBasis(polydeg) indicator_sc = IndicatorHennemannGassner(equations, basis, - alpha_max=0.5, - alpha_min=0.001, - alpha_smooth=true, - variable=density_pressure) + alpha_max = 0.5, + alpha_min = 0.001, + alpha_smooth = true, + variable = density_pressure) volume_integral = VolumeIntegralShockCapturingHG(indicator_sc; - volume_flux_dg=volume_flux, - volume_flux_fv=surface_flux) + volume_flux_dg = volume_flux, + volume_flux_fv = surface_flux) solver = DGSEM(basis, surface_flux, volume_integral) # Affine type mapping to take the [-1,1]^2 domain from the mesh file # and put it onto the rotor domain [0,1]^2 and then warp it with a mapping # as described in https://arxiv.org/abs/2012.12040 function mapping_twist(xi, eta) - y = 0.5 * (eta + 1.0) + 0.05 * cos(1.5 * pi * (2.0 * xi - 1.0)) * cos(0.5 * pi * (2.0 * eta - 1.0)) - x = 0.5 * (xi + 1.0) + 0.05 * cos(0.5 * pi * (2.0 * xi - 1.0)) * cos(2.0 * pi * y) - return SVector(x, y) + y = 0.5 * (eta + 1.0) + + 0.05 * cos(1.5 * pi * (2.0 * xi - 1.0)) * cos(0.5 * pi * (2.0 * eta - 1.0)) + x = 0.5 * (xi + 1.0) + 0.05 * cos(0.5 * pi * (2.0 * xi - 1.0)) * cos(2.0 * pi * y) + return SVector(x, y) end - mesh_file = joinpath(@__DIR__, "square_unstructured_2.inp") -isfile(mesh_file) || download("https://gist.githubusercontent.com/efaulhaber/63ff2ea224409e55ee8423b3a33e316a/raw/7db58af7446d1479753ae718930741c47a3b79b7/square_unstructured_2.inp", - mesh_file) +isfile(mesh_file) || + download("https://gist.githubusercontent.com/efaulhaber/63ff2ea224409e55ee8423b3a33e316a/raw/7db58af7446d1479753ae718930741c47a3b79b7/square_unstructured_2.inp", + mesh_file) # INP mesh files are only support by p4est. Hence, we # create a p4est connecvity object first from which # we can create a t8code mesh. -conn = Trixi.read_inp_p4est(mesh_file,Val(2)) +conn = Trixi.read_inp_p4est(mesh_file, Val(2)) -mesh = T8codeMesh{2}(conn, polydeg=4, - mapping=mapping_twist, - initial_refinement_level=1) +mesh = T8codeMesh{2}(conn, polydeg = 4, + mapping = mapping_twist, + initial_refinement_level = 1) boundary_condition = BoundaryConditionDirichlet(initial_condition) -boundary_conditions = Dict( :all => boundary_condition ) +boundary_conditions = Dict(:all => boundary_condition) semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, - boundary_conditions=boundary_conditions) - + boundary_conditions = boundary_conditions) ############################################################################### # ODE solvers, callbacks etc. @@ -97,37 +97,30 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() analysis_interval = 100 -analysis_callback = AnalysisCallback(semi, interval=analysis_interval) - -alive_callback = AliveCallback(analysis_interval=analysis_interval) +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) -# Not implemented yet. -# save_solution = SaveSolutionCallback(interval=100, -# save_initial_solution=true, -# save_final_solution=true, -# solution_variables=cons2prim) +alive_callback = AliveCallback(analysis_interval = analysis_interval) amr_indicator = IndicatorLöhner(semi, - variable=density_pressure) + variable = density_pressure) amr_controller = ControllerThreeLevel(semi, amr_indicator, - base_level=1, - med_level =3, med_threshold=0.05, - max_level =5, max_threshold=0.1) + base_level = 1, + med_level = 3, med_threshold = 0.05, + max_level = 5, max_threshold = 0.1) amr_callback = AMRCallback(semi, amr_controller, - interval=5, - adapt_initial_condition=true, - adapt_initial_condition_only_refine=true) + interval = 5, + adapt_initial_condition = true, + adapt_initial_condition_only_refine = true) cfl = 0.5 -stepsize_callback = StepsizeCallback(cfl=cfl) +stepsize_callback = StepsizeCallback(cfl = cfl) -glm_speed_callback = GlmSpeedCallback(glm_scale=0.5, cfl=cfl) +glm_speed_callback = GlmSpeedCallback(glm_scale = 0.5, cfl = cfl) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, - # save_solution, amr_callback, stepsize_callback, glm_speed_callback) @@ -135,7 +128,7 @@ callbacks = CallbackSet(summary_callback, ############################################################################### # run the simulation -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep = false, callback = callbacks); summary_callback() # print the timer summary diff --git a/src/callbacks_step/amr.jl b/src/callbacks_step/amr.jl index 5820beceb3..4d80e6e113 100644 --- a/src/callbacks_step/amr.jl +++ b/src/callbacks_step/amr.jl @@ -471,7 +471,7 @@ function (amr_callback::AMRCallback)(u_ode::AbstractVector, mesh::P4estMesh, return has_changed end -function (amr_callback::AMRCallback)(u_ode::AbstractVector, mesh::T8codeMesh, +function (amr_callback::AMRCallback)(u_ode::AbstractVector, mesh::SerialT8codeMesh, equations, dg::DG, cache, semi, t, iter; only_refine = false, only_coarsen = false, diff --git a/src/callbacks_step/amr_dg2d.jl b/src/callbacks_step/amr_dg2d.jl index c3cf499198..1d37dfce03 100644 --- a/src/callbacks_step/amr_dg2d.jl +++ b/src/callbacks_step/amr_dg2d.jl @@ -333,14 +333,6 @@ function coarsen_elements!(u::AbstractArray{<:Any, 4}, element_id, end end -# this method is called when an `ControllerThreeLevel` is constructed -function create_cache(::Type{ControllerThreeLevel}, - mesh::Union{TreeMesh{2}, P4estMesh{2}, T8codeMesh{2}}, equations, - dg::DG, cache) - controller_value = Vector{Int}(undef, nelements(dg, cache)) - return (; controller_value) -end - # Coarsen and refine elements in the DG solver based on a difference list. function adapt!(u_ode::AbstractVector, adaptor, mesh::T8codeMesh{2}, equations, dg::DGSEM, cache, difference) @@ -410,6 +402,14 @@ function adapt!(u_ode::AbstractVector, adaptor, mesh::T8codeMesh{2}, equations, return nothing end +# this method is called when an `ControllerThreeLevel` is constructed +function create_cache(::Type{ControllerThreeLevel}, + mesh::Union{TreeMesh{2}, P4estMesh{2}, T8codeMesh{2}}, equations, + dg::DG, cache) + controller_value = Vector{Int}(undef, nelements(dg, cache)) + return (; controller_value) +end + function create_cache(::Type{ControllerThreeLevelCombined}, mesh::TreeMesh{2}, equations, dg::DG, cache) controller_value = Vector{Int}(undef, nelements(dg, cache)) diff --git a/src/meshes/mesh_io.jl b/src/meshes/mesh_io.jl index b479bda0c4..e51ccf32f0 100644 --- a/src/meshes/mesh_io.jl +++ b/src/meshes/mesh_io.jl @@ -222,7 +222,7 @@ end # TODO: Implement this function as soon as there is support for this in `t8code`. function save_mesh_file(mesh::T8codeMesh, output_directory, timestep, mpi_parallel) - @warn "Mesh file output not supported yet for `T8codeMesh`." + @error "Mesh file output not supported yet for `T8codeMesh`." return joinpath(output_directory, "dummy_mesh.h5") end diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 9de790f96d..f8485d925e 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -44,6 +44,7 @@ mutable struct T8codeMesh{NDIMS, RealT <: Real, IsParallel, NDIMSP2, NNODES} <: # Destroy 't8code' structs when the mesh is garbage collected. finalizer(function (mesh::T8codeMesh{NDIMS}) + # `cmesh` and `scheme` are automatically freed by this call. trixi_t8_unref_forest(mesh.forest) end, mesh) diff --git a/test/runtests.jl b/test/runtests.jl index f76811dddb..1d7eefe1fc 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,113 +4,119 @@ using MPI: mpiexec # run tests on Travis CI in parallel const TRIXI_TEST = get(ENV, "TRIXI_TEST", "all") const TRIXI_MPI_NPROCS = clamp(Sys.CPU_THREADS, 2, 3) -const TRIXI_NTHREADS = clamp(Sys.CPU_THREADS, 2, 3) +const TRIXI_NTHREADS = clamp(Sys.CPU_THREADS, 2, 3) @time @testset "Trixi.jl tests" begin - # This is placed first since tests error out otherwise if `TRIXI_TEST == "all"`, - # at least on some systems. - @time if TRIXI_TEST == "all" || TRIXI_TEST == "mpi" - # Do a dummy `@test true`: - # If the process errors out the testset would error out as well, - # cf. https://github.com/JuliaParallel/MPI.jl/pull/391 - @test true - - # There are spurious test failures of Trixi.jl with MPI on Windows, see - # https://github.com/trixi-framework/Trixi.jl/issues/901 - # To reduce their impact, we do not test MPI with coverage on Windows. - # This reduces the chance to hit a spurious test failure by one half. - # In addition, it looks like the Linux GitHub runners run out of memory during the 3D tests - # with coverage, so we currently do not test MPI with coverage on Linux. For more details, - # see the discussion at https://github.com/trixi-framework/Trixi.jl/pull/1062#issuecomment-1035901020 - cmd = string(Base.julia_cmd()) - coverage = occursin("--code-coverage", cmd) && !occursin("--code-coverage=none", cmd) - if !(coverage && Sys.iswindows()) && !(coverage && Sys.islinux()) - # We provide a `--heap-size-hint` to avoid/reduce out-of-memory errors during CI testing - mpiexec() do cmd - run(`$cmd -n $TRIXI_MPI_NPROCS $(Base.julia_cmd()) --threads=1 --check-bounds=yes --heap-size-hint=1G $(abspath("test_mpi.jl"))`) - end - end - end - - @time if TRIXI_TEST == "all" || TRIXI_TEST == "threaded" || TRIXI_TEST == "threaded_legacy" - # Do a dummy `@test true`: - # If the process errors out the testset would error out as well, - # cf. https://github.com/JuliaParallel/MPI.jl/pull/391 - @test true - - run(`$(Base.julia_cmd()) --threads=$TRIXI_NTHREADS --check-bounds=yes --code-coverage=none $(abspath("test_threaded.jl"))`) - end - - @time if TRIXI_TEST == "all" || TRIXI_TEST == "tree_part1" - include("test_tree_1d.jl") - include("test_tree_2d_part1.jl") - end - - @time if TRIXI_TEST == "all" || TRIXI_TEST == "tree_part2" - include("test_tree_2d_part2.jl") - end - - @time if TRIXI_TEST == "all" || TRIXI_TEST == "tree_part3" - include("test_tree_2d_part3.jl") - end - - @time if TRIXI_TEST == "all" || TRIXI_TEST == "tree_part4" - include("test_tree_3d_part1.jl") - end - - @time if TRIXI_TEST == "all" || TRIXI_TEST == "tree_part5" - include("test_tree_3d_part2.jl") - end - - @time if TRIXI_TEST == "all" || TRIXI_TEST == "tree_part6" - include("test_tree_3d_part3.jl") - end - - @time if TRIXI_TEST == "all" || TRIXI_TEST == "structured" - include("test_structured_1d.jl") - include("test_structured_2d.jl") - include("test_structured_3d.jl") - end - - @time if TRIXI_TEST == "all" || TRIXI_TEST == "p4est_part1" - include("test_p4est_2d.jl") - end - - @time if TRIXI_TEST == "all" || TRIXI_TEST == "p4est_part2" - include("test_p4est_3d.jl") - end - - @time if TRIXI_TEST == "all" || TRIXI_TEST == "unstructured_dgmulti" - include("test_unstructured_2d.jl") - include("test_dgmulti_1d.jl") - include("test_dgmulti_2d.jl") - include("test_dgmulti_3d.jl") - end - - @time if TRIXI_TEST == "all" || TRIXI_TEST == "parabolic" - include("test_parabolic_1d.jl") - include("test_parabolic_2d.jl") - include("test_parabolic_3d.jl") - end - - @time if TRIXI_TEST == "all" || TRIXI_TEST == "misc_part1" - include("test_unit.jl") - include("test_visualization.jl") - end - - @time if TRIXI_TEST == "all" || TRIXI_TEST == "misc_part2" - include("test_special_elixirs.jl") - end - - @time if TRIXI_TEST == "all" || TRIXI_TEST == "performance_specializations_part1" - include("test_performance_specializations_2d.jl") - end - - @time if TRIXI_TEST == "all" || TRIXI_TEST == "performance_specializations_part2" - include("test_performance_specializations_3d.jl") - end - - @time if TRIXI_TEST == "all" || TRIXI_TEST == "paper_self_gravitating_gas_dynamics" - include("test_paper_self_gravitating_gas_dynamics.jl") - end + # This is placed first since tests error out otherwise if `TRIXI_TEST == "all"`, + # at least on some systems. + @time if TRIXI_TEST == "all" || TRIXI_TEST == "mpi" + # Do a dummy `@test true`: + # If the process errors out the testset would error out as well, + # cf. https://github.com/JuliaParallel/MPI.jl/pull/391 + @test true + + # There are spurious test failures of Trixi.jl with MPI on Windows, see + # https://github.com/trixi-framework/Trixi.jl/issues/901 + # To reduce their impact, we do not test MPI with coverage on Windows. + # This reduces the chance to hit a spurious test failure by one half. + # In addition, it looks like the Linux GitHub runners run out of memory during the 3D tests + # with coverage, so we currently do not test MPI with coverage on Linux. For more details, + # see the discussion at https://github.com/trixi-framework/Trixi.jl/pull/1062#issuecomment-1035901020 + cmd = string(Base.julia_cmd()) + coverage = occursin("--code-coverage", cmd) && + !occursin("--code-coverage=none", cmd) + if !(coverage && Sys.iswindows()) && !(coverage && Sys.islinux()) + # We provide a `--heap-size-hint` to avoid/reduce out-of-memory errors during CI testing + mpiexec() do cmd + run(`$cmd -n $TRIXI_MPI_NPROCS $(Base.julia_cmd()) --threads=1 --check-bounds=yes --heap-size-hint=1G $(abspath("test_mpi.jl"))`) + end + end + end + + @time if TRIXI_TEST == "all" || TRIXI_TEST == "threaded" || + TRIXI_TEST == "threaded_legacy" + # Do a dummy `@test true`: + # If the process errors out the testset would error out as well, + # cf. https://github.com/JuliaParallel/MPI.jl/pull/391 + @test true + + run(`$(Base.julia_cmd()) --threads=$TRIXI_NTHREADS --check-bounds=yes --code-coverage=none $(abspath("test_threaded.jl"))`) + end + + @time if TRIXI_TEST == "all" || TRIXI_TEST == "tree_part1" + include("test_tree_1d.jl") + include("test_tree_2d_part1.jl") + end + + @time if TRIXI_TEST == "all" || TRIXI_TEST == "tree_part2" + include("test_tree_2d_part2.jl") + end + + @time if TRIXI_TEST == "all" || TRIXI_TEST == "tree_part3" + include("test_tree_2d_part3.jl") + end + + @time if TRIXI_TEST == "all" || TRIXI_TEST == "tree_part4" + include("test_tree_3d_part1.jl") + end + + @time if TRIXI_TEST == "all" || TRIXI_TEST == "tree_part5" + include("test_tree_3d_part2.jl") + end + + @time if TRIXI_TEST == "all" || TRIXI_TEST == "tree_part6" + include("test_tree_3d_part3.jl") + end + + @time if TRIXI_TEST == "all" || TRIXI_TEST == "structured" + include("test_structured_1d.jl") + include("test_structured_2d.jl") + include("test_structured_3d.jl") + end + + @time if TRIXI_TEST == "all" || TRIXI_TEST == "p4est_part1" + include("test_p4est_2d.jl") + end + + @time if TRIXI_TEST == "all" || TRIXI_TEST == "p4est_part2" + include("test_p4est_3d.jl") + end + + @time if TRIXI_TEST == "all" || TRIXI_TEST == "t8code_part1" + include("test_t8code_2d.jl") + end + + @time if TRIXI_TEST == "all" || TRIXI_TEST == "unstructured_dgmulti" + include("test_unstructured_2d.jl") + include("test_dgmulti_1d.jl") + include("test_dgmulti_2d.jl") + include("test_dgmulti_3d.jl") + end + + @time if TRIXI_TEST == "all" || TRIXI_TEST == "parabolic" + include("test_parabolic_1d.jl") + include("test_parabolic_2d.jl") + include("test_parabolic_3d.jl") + end + + @time if TRIXI_TEST == "all" || TRIXI_TEST == "misc_part1" + include("test_unit.jl") + include("test_visualization.jl") + end + + @time if TRIXI_TEST == "all" || TRIXI_TEST == "misc_part2" + include("test_special_elixirs.jl") + end + + @time if TRIXI_TEST == "all" || TRIXI_TEST == "performance_specializations_part1" + include("test_performance_specializations_2d.jl") + end + + @time if TRIXI_TEST == "all" || TRIXI_TEST == "performance_specializations_part2" + include("test_performance_specializations_3d.jl") + end + + @time if TRIXI_TEST == "all" || TRIXI_TEST == "paper_self_gravitating_gas_dynamics" + include("test_paper_self_gravitating_gas_dynamics.jl") + end end From 6ac73cf8bd356db64af11538f94c0a9c1da8c79a Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 11 Jul 2023 16:18:01 +0200 Subject: [PATCH 055/128] Removed superfluous outer constructor for T8codeMesh. --- src/meshes/t8code_mesh.jl | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index f8485d925e..ffd22a0552 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -25,7 +25,9 @@ mutable struct T8codeMesh{NDIMS, RealT <: Real, IsParallel, NDIMSP2, NNODES} <: nmortars :: Int nboundaries :: Int - function T8codeMesh{NDIMS}(cmesh, scheme, forest, nodes) where {NDIMS} + function T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, + boundary_names, + current_filename) where {NDIMS} # TODO: Implement MPI parallelization. # if mpi_isparallel() # if !T8code.uses_mpi() @@ -42,6 +44,11 @@ mutable struct T8codeMesh{NDIMS, RealT <: Real, IsParallel, NDIMSP2, NNODES} <: forest, is_parallel) + mesh.nodes = nodes + mesh.boundary_names = boundary_names + mesh.current_filename = current_filename + mesh.tree_node_coordinates = tree_node_coordinates + # Destroy 't8code' structs when the mesh is garbage collected. finalizer(function (mesh::T8codeMesh{NDIMS}) # `cmesh` and `scheme` are automatically freed by this call. @@ -52,19 +59,6 @@ mutable struct T8codeMesh{NDIMS, RealT <: Real, IsParallel, NDIMSP2, NNODES} <: end end -function T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, - boundary_names, - current_filename) where {NDIMS} - mesh = T8codeMesh{NDIMS}(cmesh, scheme, forest, nodes) - - mesh.nodes = nodes - mesh.boundary_names = boundary_names - mesh.current_filename = current_filename - mesh.tree_node_coordinates = tree_node_coordinates - - return mesh -end - const SerialT8codeMesh{NDIMS} = T8codeMesh{NDIMS, <:Real, <:False} @inline mpi_parallel(mesh::SerialT8codeMesh) = False() From 1e0fa0c93d86911755f56db298cdbd639a758dd4 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 11 Jul 2023 16:29:36 +0200 Subject: [PATCH 056/128] Added return statement for consistency. --- src/solvers/dgsem_t8code/containers.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/solvers/dgsem_t8code/containers.jl b/src/solvers/dgsem_t8code/containers.jl index 37bb581687..093feb2985 100644 --- a/src/solvers/dgsem_t8code/containers.jl +++ b/src/solvers/dgsem_t8code/containers.jl @@ -30,6 +30,8 @@ function count_required_surfaces!(mesh::T8codeMesh) mesh.nmortars = counts.mortars mesh.ninterfaces = counts.interfaces mesh.nboundaries = counts.boundaries + + return counts end # Compatibility to `dgsem_p4est/containers.jl`. From 0adfbe1334587a3c91494b155297b90b1f1aae03 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 11 Jul 2023 16:31:30 +0200 Subject: [PATCH 057/128] Fixed wrong indentation by autoformatter. --- src/solvers/dgsem_t8code/containers_2d.jl | 107 +++++++++++----------- src/solvers/dgsem_t8code/dg.jl | 43 ++++----- 2 files changed, 76 insertions(+), 74 deletions(-) diff --git a/src/solvers/dgsem_t8code/containers_2d.jl b/src/solvers/dgsem_t8code/containers_2d.jl index 86e2e7a9e4..029e6674af 100644 --- a/src/solvers/dgsem_t8code/containers_2d.jl +++ b/src/solvers/dgsem_t8code/containers_2d.jl @@ -1,57 +1,58 @@ @muladd begin - - # Interpolate tree_node_coordinates to each quadrant at the specified nodes. - function calc_node_coordinates!(node_coordinates, - mesh::T8codeMesh{2}, - nodes::AbstractVector) - # We use `StrideArray`s here since these buffers are used in performance-critical - # places and the additional information passed to the compiler makes them faster - # than native `Array`s. - tmp1 = StrideArray(undef, real(mesh), - StaticInt(2), static_length(nodes), static_length(mesh.nodes)) - matrix1 = StrideArray(undef, real(mesh), - static_length(nodes), static_length(mesh.nodes)) - matrix2 = similar(matrix1) - baryweights_in = barycentric_weights(mesh.nodes) - - num_local_trees = t8_forest_get_num_local_trees(mesh.forest) - - current_index = 0 - for itree in 0:(num_local_trees - 1) - tree_class = t8_forest_get_tree_class(mesh.forest, itree) - eclass_scheme = t8_forest_get_eclass_scheme(mesh.forest, tree_class) - num_elements_in_tree = t8_forest_get_tree_num_elements(mesh.forest, itree) - - for ielement in 0:(num_elements_in_tree - 1) - element = t8_forest_get_element_in_tree(mesh.forest, itree, ielement) - element_level = t8_element_level(eclass_scheme, element) - - element_length = t8_quad_len(element_level) / t8_quad_root_len - - element_coords = Array{Float64}(undef, 3) - t8_element_vertex_reference_coords(eclass_scheme, element, 0, - pointer(element_coords)) - - nodes_out_x = 2 * - (element_length * 1 / 2 * (nodes .+ 1) .+ element_coords[1]) .- - 1 - nodes_out_y = 2 * - (element_length * 1 / 2 * (nodes .+ 1) .+ element_coords[2]) .- - 1 - - polynomial_interpolation_matrix!(matrix1, mesh.nodes, nodes_out_x, - baryweights_in) - polynomial_interpolation_matrix!(matrix2, mesh.nodes, nodes_out_y, - baryweights_in) - - multiply_dimensionwise!(view(node_coordinates, :, :, :, current_index += 1), - matrix1, matrix2, - view(mesh.tree_node_coordinates, :, :, :, - itree + 1), - tmp1) - end +#! format: noindent + +# Interpolate tree_node_coordinates to each quadrant at the specified nodes. +function calc_node_coordinates!(node_coordinates, + mesh::T8codeMesh{2}, + nodes::AbstractVector) + # We use `StrideArray`s here since these buffers are used in performance-critical + # places and the additional information passed to the compiler makes them faster + # than native `Array`s. + tmp1 = StrideArray(undef, real(mesh), + StaticInt(2), static_length(nodes), static_length(mesh.nodes)) + matrix1 = StrideArray(undef, real(mesh), + static_length(nodes), static_length(mesh.nodes)) + matrix2 = similar(matrix1) + baryweights_in = barycentric_weights(mesh.nodes) + + num_local_trees = t8_forest_get_num_local_trees(mesh.forest) + + current_index = 0 + for itree in 0:(num_local_trees - 1) + tree_class = t8_forest_get_tree_class(mesh.forest, itree) + eclass_scheme = t8_forest_get_eclass_scheme(mesh.forest, tree_class) + num_elements_in_tree = t8_forest_get_tree_num_elements(mesh.forest, itree) + + for ielement in 0:(num_elements_in_tree - 1) + element = t8_forest_get_element_in_tree(mesh.forest, itree, ielement) + element_level = t8_element_level(eclass_scheme, element) + + element_length = t8_quad_len(element_level) / t8_quad_root_len + + element_coords = Array{Float64}(undef, 3) + t8_element_vertex_reference_coords(eclass_scheme, element, 0, + pointer(element_coords)) + + nodes_out_x = 2 * + (element_length * 1 / 2 * (nodes .+ 1) .+ element_coords[1]) .- + 1 + nodes_out_y = 2 * + (element_length * 1 / 2 * (nodes .+ 1) .+ element_coords[2]) .- + 1 + + polynomial_interpolation_matrix!(matrix1, mesh.nodes, nodes_out_x, + baryweights_in) + polynomial_interpolation_matrix!(matrix2, mesh.nodes, nodes_out_y, + baryweights_in) + + multiply_dimensionwise!(view(node_coordinates, :, :, :, current_index += 1), + matrix1, matrix2, + view(mesh.tree_node_coordinates, :, :, :, + itree + 1), + tmp1) end - - return node_coordinates end + + return node_coordinates +end end # @muladd diff --git a/src/solvers/dgsem_t8code/dg.jl b/src/solvers/dgsem_t8code/dg.jl index a126a1badb..16a9d7d35b 100644 --- a/src/solvers/dgsem_t8code/dg.jl +++ b/src/solvers/dgsem_t8code/dg.jl @@ -1,30 +1,31 @@ @muladd begin +#! format: noindent - # This method is called when a SemidiscretizationHyperbolic is constructed. - # It constructs the basic `cache` used throughout the simulation to compute - # the RHS etc. - function create_cache(mesh::T8codeMesh, equations::AbstractEquations, dg::DG, ::Any, - ::Type{uEltype}) where {uEltype <: Real} - count_required_surfaces!(mesh) +# This method is called when a SemidiscretizationHyperbolic is constructed. +# It constructs the basic `cache` used throughout the simulation to compute +# the RHS etc. +function create_cache(mesh::T8codeMesh, equations::AbstractEquations, dg::DG, ::Any, + ::Type{uEltype}) where {uEltype <: Real} + count_required_surfaces!(mesh) - elements = init_elements(mesh, equations, dg.basis, uEltype) - interfaces = init_interfaces(mesh, equations, dg.basis, elements) - boundaries = init_boundaries(mesh, equations, dg.basis, elements) - mortars = init_mortars(mesh, equations, dg.basis, elements) + elements = init_elements(mesh, equations, dg.basis, uEltype) + interfaces = init_interfaces(mesh, equations, dg.basis, elements) + boundaries = init_boundaries(mesh, equations, dg.basis, elements) + mortars = init_mortars(mesh, equations, dg.basis, elements) - trixi_t8_fill_mesh_info(mesh.forest, elements, interfaces, mortars, boundaries, - mesh.boundary_names) + trixi_t8_fill_mesh_info(mesh.forest, elements, interfaces, mortars, boundaries, + mesh.boundary_names) - cache = (; elements, interfaces, boundaries, mortars) + cache = (; elements, interfaces, boundaries, mortars) - # Add specialized parts of the cache required to compute the volume integral etc. - cache = (; cache..., - create_cache(mesh, equations, dg.volume_integral, dg, uEltype)...) - cache = (; cache..., create_cache(mesh, equations, dg.mortar, uEltype)...) + # Add specialized parts of the cache required to compute the volume integral etc. + cache = (; cache..., + create_cache(mesh, equations, dg.volume_integral, dg, uEltype)...) + cache = (; cache..., create_cache(mesh, equations, dg.mortar, uEltype)...) - return cache - end + return cache +end - include("containers.jl") - include("containers_2d.jl") +include("containers.jl") +include("containers_2d.jl") end # @muladd From e42f36e46140b9002f76d4a6bab7a71b4504baf4 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 11 Jul 2023 16:44:23 +0200 Subject: [PATCH 058/128] Added comments. --- examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl | 2 ++ examples/t8code_2d_dgsem/elixir_euler_free_stream.jl | 2 ++ ...elixir_euler_source_terms_nonconforming_unstructured_flag.jl | 2 ++ 3 files changed, 6 insertions(+) diff --git a/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl b/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl index ce9e612943..31a8bc9369 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl @@ -60,6 +60,8 @@ new_forest_ref = Ref{Trixi.t8_forest_t}() Trixi.t8_forest_init(new_forest_ref); new_forest = new_forest_ref[] +# Check out `examples/t8_step4_partition_balance_ghost.jl` in +# https://github.com/DLR-AMR/T8code.jl for detailed explanations. let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0 Trixi.t8_forest_set_user_data(new_forest, C_NULL) Trixi.t8_forest_set_adapt(new_forest, mesh.forest, diff --git a/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl b/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl index 114a112221..01e0449c67 100644 --- a/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl +++ b/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl @@ -77,6 +77,8 @@ new_forest_ref = Ref{Trixi.t8_forest_t}() Trixi.t8_forest_init(new_forest_ref); new_forest = new_forest_ref[] +# Check out `examples/t8_step4_partition_balance_ghost.jl` in +# https://github.com/DLR-AMR/T8code.jl for detailed explanations. let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0 Trixi.t8_forest_set_user_data(new_forest, C_NULL) Trixi.t8_forest_set_adapt(new_forest, mesh.forest, diff --git a/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl b/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl index 2fd5840a43..21f26d79ba 100644 --- a/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl @@ -77,6 +77,8 @@ new_forest_ref = Ref{Trixi.t8_forest_t}() Trixi.t8_forest_init(new_forest_ref); new_forest = new_forest_ref[] +# Check out `examples/t8_step4_partition_balance_ghost.jl` in +# https://github.com/DLR-AMR/T8code.jl for detailed explanations. let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0 Trixi.t8_forest_set_user_data(new_forest, C_NULL) Trixi.t8_forest_set_adapt(new_forest, mesh.forest, From aedea641c6e553f40bee4da3a53b4be82e986631 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Fri, 14 Jul 2023 09:05:34 +0200 Subject: [PATCH 059/128] Made sure an exception is thrown. --- src/meshes/mesh_io.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/meshes/mesh_io.jl b/src/meshes/mesh_io.jl index e51ccf32f0..32a15c25bc 100644 --- a/src/meshes/mesh_io.jl +++ b/src/meshes/mesh_io.jl @@ -222,7 +222,7 @@ end # TODO: Implement this function as soon as there is support for this in `t8code`. function save_mesh_file(mesh::T8codeMesh, output_directory, timestep, mpi_parallel) - @error "Mesh file output not supported yet for `T8codeMesh`." + error("Mesh file output not supported yet for `T8codeMesh`.") return joinpath(output_directory, "dummy_mesh.h5") end From 1456f1f9b47426d0699d86d5481c63dd73c10a2b Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Wed, 19 Jul 2023 15:06:09 +0200 Subject: [PATCH 060/128] Changed flags for sc_init for t8code initialization. --- src/auxiliary/t8code.jl | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index 4c6d069ef0..2d45a7c424 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -13,10 +13,16 @@ function init_t8code() end # Initialize the sc library, has to happen before we initialize t8code. - T8code.Libt8.sc_init(mpi_comm(), 1, 1, C_NULL, T8code.Libt8.SC_LP_ERROR) + let catch_signals = 0, print_backtrace = 0 + T8code.Libt8.sc_init(mpi_comm(), catch_signals, print_backtrace, C_NULL, + T8code.Libt8.SC_LP_ERROR) + end + + if T8code.Libt8.p4est_is_initialized() == 0 + # Initialize `p4est` with log level ERROR to prevent a lot of output in AMR simulations + T8code.Libt8.p4est_init(C_NULL, T8code.Libt8.SC_LP_ERROR) + end - # Initialize `p4est` with log level ERROR to prevent a lot of output in AMR simulations - T8code.Libt8.p4est_init(C_NULL, SC_LP_ERROR) # Initialize t8code with log level ERROR to prevent a lot of output in AMR simulations. t8_init(T8code.Libt8.SC_LP_ERROR) From 3857cc39bc751cb537f324291c1b0e9c08a2b56f Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Wed, 19 Jul 2023 15:31:45 +0200 Subject: [PATCH 061/128] Updated formatting. --- test/test_t8code_2d.jl | 244 ++++++++++++++++++++++++----------------- 1 file changed, 145 insertions(+), 99 deletions(-) diff --git a/test/test_t8code_2d.jl b/test/test_t8code_2d.jl index 348feba463..785f3b0753 100644 --- a/test/test_t8code_2d.jl +++ b/test/test_t8code_2d.jl @@ -9,112 +9,158 @@ EXAMPLES_DIR = joinpath(examples_dir(), "t8code_2d_dgsem") # Start with a clean environment: remove Trixi.jl output directory if it exists outdir = "out" -isdir(outdir) && rm(outdir, recursive=true) +isdir(outdir) && rm(outdir, recursive = true) mkdir(outdir) @testset "T8codeMesh2D" begin - @trixi_testset "elixir_advection_basic.jl" begin - @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_basic.jl"), - # Expected errors are exactly the same as with TreeMesh! - l2 = [8.311947673061856e-6], - linf = [6.627000273229378e-5]) - end - - @trixi_testset "elixir_advection_nonconforming_flag.jl" begin - @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_nonconforming_flag.jl"), - l2 = [3.198940059144588e-5], - linf = [0.00030636069494005547]) - - # Ensure that we do not have excessive memory allocations - # (e.g., from type instabilities) - let - t = sol.t[end] - u_ode = sol.u[end] - du_ode = similar(u_ode) - @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000 + @trixi_testset "elixir_advection_basic.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_basic.jl"), + # Expected errors are exactly the same as with TreeMesh! + l2=[8.311947673061856e-6], + linf=[6.627000273229378e-5]) end - end - - @trixi_testset "elixir_advection_unstructured_flag.jl" begin - @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_unstructured_flag.jl"), - l2 = [0.0005379687442422346], - linf = [0.007438525029884735]) - end - - @trixi_testset "elixir_advection_amr_unstructured_flag.jl" begin - @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_amr_unstructured_flag.jl"), - l2 = [0.001993165013217687], - linf = [0.032891018571625796], - coverage_override = (maxiters=6,)) - end - - @trixi_testset "elixir_advection_amr_solution_independent.jl" begin - @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_amr_solution_independent.jl"), - # Expected errors are exactly the same as with StructuredMesh! - l2 = [4.949660644033807e-5], - linf = [0.0004867846262313763], - coverage_override = (maxiters=6,)) - end - - @trixi_testset "elixir_euler_source_terms_nonconforming_unstructured_flag.jl" begin - @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_source_terms_nonconforming_unstructured_flag.jl"), - l2 = [0.0034516244508588046, 0.0023420334036925493, 0.0024261923964557187, 0.004731710454271893], - linf = [0.04155789011775046, 0.024772109862748914, 0.03759938693042297, 0.08039824959535657]) - end - - @trixi_testset "elixir_euler_free_stream.jl" begin - @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_free_stream.jl"), - l2 = [2.063350241405049e-15, 1.8571016296925367e-14, 3.1769447886391905e-14, 1.4104095258528071e-14], - linf = [1.9539925233402755e-14, 2e-12, 4.8e-12, 4e-12], - atol = 2.0e-12, # required to make CI tests pass on macOS - ) - end - - @trixi_testset "elixir_euler_shockcapturing_ec.jl" begin - @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_shockcapturing_ec.jl"), - l2 = [9.53984675e-02, 1.05633455e-01, 1.05636158e-01, 3.50747237e-01], - linf = [2.94357464e-01, 4.07893014e-01, 3.97334516e-01, 1.08142520e+00], - tspan = (0.0, 1.0)) - end - - @trixi_testset "elixir_euler_sedov.jl" begin - @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_sedov.jl"), - l2 = [3.76149952e-01, 2.46970327e-01, 2.46970327e-01, 1.28889042e+00], - linf = [1.22139001e+00, 1.17742626e+00, 1.17742626e+00, 6.20638482e+00], - tspan = (0.0, 0.3)) - end - - @trixi_testset "elixir_shallowwater_source_terms.jl" begin - @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_source_terms.jl"), - l2 = [9.168126407325352e-5, 0.0009795410115453788, 0.002546408320320785, 3.941189812642317e-6], - linf = [0.0009903782521019089, 0.0059752684687262025, 0.010941106525454103, 1.2129488214718265e-5], - tspan = (0.0, 0.1)) - end - - @trixi_testset "elixir_mhd_alfven_wave.jl" begin - @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_mhd_alfven_wave.jl"), - l2 = [1.0513414461545583e-5, 1.0517900957166411e-6, 1.0517900957304043e-6, 1.511816606372376e-6, - 1.0443997728645063e-6, 7.879639064990798e-7, 7.879639065049896e-7, 1.0628631669056271e-6, - 4.3382328912336153e-7], - linf = [4.255466285174592e-5, 1.0029706745823264e-5, 1.0029706747467781e-5, 1.2122265939010224e-5, - 5.4791097160444835e-6, 5.18922042269665e-6, 5.189220422141538e-6, 9.552667261422676e-6, - 1.4237578427628152e-6]) - end - - @trixi_testset "elixir_mhd_rotor.jl" begin - @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_mhd_rotor.jl"), - l2 = [0.44211360369891683, 0.8805178316216257, 0.8262710688468049, 0.0, - 0.9616090460973586, 0.10386643568745411, 0.15403457366543802, 0.0, - 2.8399715649715473e-5], - linf = [10.04369305341599, 17.995640564998403, 9.576041548174265, 0.0, - 19.429658884314534, 1.3821395681242314, 1.818559351543182, 0.0, - 0.002261930217575465], - tspan = (0.0, 0.02)) - end + @trixi_testset "elixir_advection_nonconforming_flag.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, + "elixir_advection_nonconforming_flag.jl"), + l2=[3.198940059144588e-5], + linf=[0.00030636069494005547]) + end + + @trixi_testset "elixir_advection_unstructured_flag.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_unstructured_flag.jl"), + l2=[0.0005379687442422346], + linf=[0.007438525029884735]) + end + + @trixi_testset "elixir_advection_amr_unstructured_flag.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, + "elixir_advection_amr_unstructured_flag.jl"), + l2=[0.001993165013217687], + linf=[0.032891018571625796], + coverage_override=(maxiters = 6,)) + end + + @trixi_testset "elixir_advection_amr_solution_independent.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, + "elixir_advection_amr_solution_independent.jl"), + # Expected errors are exactly the same as with StructuredMesh! + l2=[4.949660644033807e-5], + linf=[0.0004867846262313763], + coverage_override=(maxiters = 6,)) + end + + @trixi_testset "elixir_euler_source_terms_nonconforming_unstructured_flag.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, + "elixir_euler_source_terms_nonconforming_unstructured_flag.jl"), + l2=[ + 0.0034516244508588046, + 0.0023420334036925493, + 0.0024261923964557187, + 0.004731710454271893, + ], + linf=[ + 0.04155789011775046, + 0.024772109862748914, + 0.03759938693042297, + 0.08039824959535657, + ]) + end + + @trixi_testset "elixir_euler_free_stream.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_free_stream.jl"), + l2=[ + 2.063350241405049e-15, + 1.8571016296925367e-14, + 3.1769447886391905e-14, + 1.4104095258528071e-14, + ], + linf=[1.9539925233402755e-14, 2e-12, 4.8e-12, 4e-12], + atol=2.0e-12,) + end + + @trixi_testset "elixir_euler_shockcapturing_ec.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_shockcapturing_ec.jl"), + l2=[ + 9.53984675e-02, + 1.05633455e-01, + 1.05636158e-01, + 3.50747237e-01, + ], + linf=[ + 2.94357464e-01, + 4.07893014e-01, + 3.97334516e-01, + 1.08142520e+00, + ], + tspan=(0.0, 1.0)) + end + + @trixi_testset "elixir_euler_sedov.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_sedov.jl"), + l2=[ + 3.76149952e-01, + 2.46970327e-01, + 2.46970327e-01, + 1.28889042e+00, + ], + linf=[ + 1.22139001e+00, + 1.17742626e+00, + 1.17742626e+00, + 6.20638482e+00, + ], + tspan=(0.0, 0.3)) + end + + @trixi_testset "elixir_shallowwater_source_terms.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_source_terms.jl"), + l2=[ + 9.168126407325352e-5, + 0.0009795410115453788, + 0.002546408320320785, + 3.941189812642317e-6, + ], + linf=[ + 0.0009903782521019089, + 0.0059752684687262025, + 0.010941106525454103, + 1.2129488214718265e-5, + ], + tspan=(0.0, 0.1)) + end + + @trixi_testset "elixir_mhd_alfven_wave.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_mhd_alfven_wave.jl"), + l2=[1.0513414461545583e-5, 1.0517900957166411e-6, + 1.0517900957304043e-6, 1.511816606372376e-6, + 1.0443997728645063e-6, 7.879639064990798e-7, + 7.879639065049896e-7, 1.0628631669056271e-6, + 4.3382328912336153e-7], + linf=[4.255466285174592e-5, 1.0029706745823264e-5, + 1.0029706747467781e-5, 1.2122265939010224e-5, + 5.4791097160444835e-6, 5.18922042269665e-6, + 5.189220422141538e-6, 9.552667261422676e-6, + 1.4237578427628152e-6]) + end + + @trixi_testset "elixir_mhd_rotor.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_mhd_rotor.jl"), + l2=[0.44211360369891683, 0.8805178316216257, 0.8262710688468049, + 0.0, + 0.9616090460973586, 0.10386643568745411, + 0.15403457366543802, 0.0, + 2.8399715649715473e-5], + linf=[10.04369305341599, 17.995640564998403, 9.576041548174265, + 0.0, + 19.429658884314534, 1.3821395681242314, 1.818559351543182, + 0.0, + 0.002261930217575465], + tspan=(0.0, 0.02)) + end end # Clean up afterwards: delete Trixi.jl output directory -@test_nowarn rm(outdir, recursive=true) +@test_nowarn rm(outdir, recursive = true) end # module From df94655b02b2117233887c87b112b1c89e09cfdc Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Thu, 20 Jul 2023 11:14:36 +0200 Subject: [PATCH 062/128] Workaround for error about calling MPI routines after MPI has been finalized. --- src/auxiliary/t8code.jl | 2 +- src/meshes/t8code_mesh.jl | 15 --------------- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index 2d45a7c424..4ab1bde218 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -2,7 +2,7 @@ init_t8code() Initialize `t8code` by calling `sc_init`, `p4est_init`, and `t8_init` while -setting the log level to `SC_LP_ERROR`. This function will check if `t8code` +setting the log level to `SC_LP_ERROR`. This function will check if `t8code` is already initialized and if yes, do nothing, thus it is safe to call it multiple times. """ diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index ffd22a0552..872643a231 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -28,15 +28,6 @@ mutable struct T8codeMesh{NDIMS, RealT <: Real, IsParallel, NDIMSP2, NNODES} <: function T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes, boundary_names, current_filename) where {NDIMS} - # TODO: Implement MPI parallelization. - # if mpi_isparallel() - # if !T8code.uses_mpi() - # error("t8code library does not support MPI") - # end - # is_parallel = Val(true) - # else - # is_parallel = Val(false) - # end is_parallel = False() mesh = new{NDIMS, Float64, typeof(is_parallel), NDIMS + 2, length(nodes)}(cmesh, @@ -49,12 +40,6 @@ mutable struct T8codeMesh{NDIMS, RealT <: Real, IsParallel, NDIMSP2, NNODES} <: mesh.current_filename = current_filename mesh.tree_node_coordinates = tree_node_coordinates - # Destroy 't8code' structs when the mesh is garbage collected. - finalizer(function (mesh::T8codeMesh{NDIMS}) - # `cmesh` and `scheme` are automatically freed by this call. - trixi_t8_unref_forest(mesh.forest) - end, mesh) - return mesh end end From 40d45c915a3e587186d3e8e668a63f38fef9f3c3 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Fri, 21 Jul 2023 11:11:32 +0200 Subject: [PATCH 063/128] Upped to T8code v0.4.1. --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 4b2eb2718f..0935343451 100644 --- a/Project.toml +++ b/Project.toml @@ -81,7 +81,7 @@ StaticArrays = "1" StrideArrays = "0.1.18" StructArrays = "0.6" SummationByPartsOperators = "0.5.41" -T8code = "0.4.0" +T8code = "0.4.1" TimerOutputs = "0.5" Triangulate = "2.0" TriplotBase = "0.1" From 6074e87eb34df252822eb14c2648d6e2469d9a52 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Mon, 24 Jul 2023 13:35:09 +0200 Subject: [PATCH 064/128] Added mpi_finailize_hook for proper memory cleanup. --- src/auxiliary/t8code.jl | 4 ++++ src/meshes/t8code_mesh.jl | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index 4ab1bde218..8ed151c9b2 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -26,6 +26,10 @@ function init_t8code() # Initialize t8code with log level ERROR to prevent a lot of output in AMR simulations. t8_init(T8code.Libt8.SC_LP_ERROR) + MPI.add_finalize_hook!(function () + T8code.Libt8.sc_finalize() + end) + return nothing end diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 872643a231..c2586065dc 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -40,6 +40,10 @@ mutable struct T8codeMesh{NDIMS, RealT <: Real, IsParallel, NDIMSP2, NNODES} <: mesh.current_filename = current_filename mesh.tree_node_coordinates = tree_node_coordinates + MPI.add_finalize_hook!(function () + trixi_t8_unref_forest(mesh.forest) + end) + return mesh end end From 766abc9bc3c774379086b9a4001807e749c120f9 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Mon, 24 Jul 2023 13:54:17 +0200 Subject: [PATCH 065/128] Added t8code to test_threaded.jl --- .../elixir_eulergravity_convergence.jl | 77 +++++++++++++++++++ test/test_threaded.jl | 16 ++++ 2 files changed, 93 insertions(+) create mode 100644 examples/t8code_2d_dgsem/elixir_eulergravity_convergence.jl diff --git a/examples/t8code_2d_dgsem/elixir_eulergravity_convergence.jl b/examples/t8code_2d_dgsem/elixir_eulergravity_convergence.jl new file mode 100644 index 0000000000..32649eacff --- /dev/null +++ b/examples/t8code_2d_dgsem/elixir_eulergravity_convergence.jl @@ -0,0 +1,77 @@ +using OrdinaryDiffEq +using Trixi + +initial_condition = initial_condition_eoc_test_coupled_euler_gravity + +############################################################################### +# semidiscretization of the compressible Euler equations +gamma = 2.0 +equations_euler = CompressibleEulerEquations2D(gamma) + +polydeg = 3 +solver_euler = DGSEM(polydeg, flux_hll) + +coordinates_min = (0.0, 0.0) +coordinates_max = (2.0, 2.0) + +trees_per_dimension = (1, 1) + +mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) + +mesh = T8codeMesh(trees_per_dimension, polydeg = 1, + mapping = mapping, + initial_refinement_level = 2) + +semi_euler = SemidiscretizationHyperbolic(mesh, equations_euler, initial_condition, solver_euler, + source_terms=source_terms_eoc_test_coupled_euler_gravity) + + +############################################################################### +# semidiscretization of the hyperbolic diffusion equations +equations_gravity = HyperbolicDiffusionEquations2D() + +solver_gravity = DGSEM(polydeg, flux_lax_friedrichs) + +semi_gravity = SemidiscretizationHyperbolic(mesh, equations_gravity, initial_condition, solver_gravity, + source_terms=source_terms_harmonic) + + +############################################################################### +# combining both semidiscretizations for Euler + self-gravity +parameters = ParametersEulerGravity(background_density=2.0, # aka rho0 + # rho0 is (ab)used to add a "+8π" term to the source terms + # for the manufactured solution + gravitational_constant=1.0, # aka G + cfl=1.1, + resid_tol=1.0e-10, + n_iterations_max=1000, + timestep_gravity=timestep_gravity_erk52_3Sstar!) + +semi = SemidiscretizationEulerGravity(semi_euler, semi_gravity, parameters) + + +############################################################################### +# ODE solvers, callbacks etc. +tspan = (0.0, 0.5) +ode = semidiscretize(semi, tspan); + +summary_callback = SummaryCallback() + +stepsize_callback = StepsizeCallback(cfl=0.8) + +analysis_interval = 100 +alive_callback = AliveCallback(analysis_interval=analysis_interval) + +analysis_callback = AnalysisCallback(semi_euler, interval=analysis_interval, + save_analysis=true) + +callbacks = CallbackSet(summary_callback, stepsize_callback, + analysis_callback, alive_callback) + +############################################################################### +# run the simulation +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep=false, callback=callbacks); +summary_callback() # print the timer summary +println("Number of gravity subcycles: ", semi.gravity_counter.ncalls_since_readout) diff --git a/test/test_threaded.jl b/test/test_threaded.jl index 1e75070798..7f883c5004 100644 --- a/test/test_threaded.jl +++ b/test/test_threaded.jl @@ -96,6 +96,22 @@ Trixi.mpi_isroot() && isdir(outdir) && rm(outdir, recursive=true) end + @testset "T8codeMesh" begin + @trixi_testset "elixir_euler_source_terms_nonconforming_unstructured_flag.jl" begin + @test_trixi_include(joinpath(examples_dir(), "t8code_2d_dgsem", "elixir_euler_source_terms_nonconforming_unstructured_flag.jl"), + l2 = [0.0034516244508588046, 0.0023420334036925493, 0.0024261923964557187, 0.004731710454271893], + linf = [0.04155789011775046, 0.024772109862748914, 0.03759938693042297, 0.08039824959535657]) + end + + @trixi_testset "elixir_eulergravity_convergence.jl" begin + @test_trixi_include(joinpath(examples_dir(), "t8code_2d_dgsem", "elixir_eulergravity_convergence.jl"), + l2 = [0.00024871265138964204, 0.0003370077102132591, 0.0003370077102131964, 0.0007231525513793697], + linf = [0.0015813032944647087, 0.0020494288423820173, 0.0020494288423824614, 0.004793821195083758], + tspan = (0.0, 0.1)) + end + end + + @testset "DGMulti" begin @trixi_testset "elixir_euler_weakform.jl (SBP, EC)" begin @test_trixi_include(joinpath(examples_dir(), "dgmulti_2d", "elixir_euler_weakform.jl"), From 032f69e872210580d8d25db72c3c7195c25bf1bc Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 25 Jul 2023 10:08:28 +0200 Subject: [PATCH 066/128] Added a `save_mesh_file` call in order to satisfy code coverage. --- examples/t8code_2d_dgsem/elixir_advection_basic.jl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/examples/t8code_2d_dgsem/elixir_advection_basic.jl b/examples/t8code_2d_dgsem/elixir_advection_basic.jl index efc5122658..593e44bcfa 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_basic.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_basic.jl @@ -57,3 +57,12 @@ sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), # Print the timer summary summary_callback() + +# Save mesh file support will be added in the future. +# The following lines of code are here for +# satisfying code coverage. +try + Trixi.save_mesh_file(mesh, "dummy", timestep = 0) +catch + # Do nothing. +end From dde280214553302196518d27dc114c298c8309f4 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 25 Jul 2023 12:04:56 +0200 Subject: [PATCH 067/128] Improved finalizer logic for T8coeMesh. --- src/meshes/t8code_mesh.jl | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index c2586065dc..3e2c9fb78b 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -40,8 +40,25 @@ mutable struct T8codeMesh{NDIMS, RealT <: Real, IsParallel, NDIMSP2, NNODES} <: mesh.current_filename = current_filename mesh.tree_node_coordinates = tree_node_coordinates + finalizer(function (mesh::T8codeMesh) + # When finalizing `mesh.forest`, `mesh.scheme` and `mesh.cmesh` are + # also cleaned up from within `t8code`. The cleanup code for `cmesh` + # does some MPI calls for deallocating shared memory arrays. Due to + # garbage collection in Julia the order of shutdown is not + # deterministic. The following code might happen after MPI is already + # in finalized state. In this case the `finalize_hook` of the MPI + # module already took care of the cleanup. See further down. + if !MPI.Finalized() + trixi_t8_unref_forest(mesh.forest) + end + end, mesh) + MPI.add_finalize_hook!(function () - trixi_t8_unref_forest(mesh.forest) + try + trixi_t8_unref_forest(mesh.forest) + catch + # The `mesh` object was already finalized. Do nothing. + end end) return mesh From c9c1c64462717554b8398741f57804155ff6a3a5 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 25 Jul 2023 17:17:34 +0200 Subject: [PATCH 068/128] Refined code. --- src/auxiliary/t8code.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index 8ed151c9b2..b96918c022 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -26,9 +26,10 @@ function init_t8code() # Initialize t8code with log level ERROR to prevent a lot of output in AMR simulations. t8_init(T8code.Libt8.SC_LP_ERROR) - MPI.add_finalize_hook!(function () - T8code.Libt8.sc_finalize() - end) + # `sc_finalize` should always be called during shutdown of an application. + # It checks wether there is still un-freed memory by t8code and/or + # T8code.jl and throws an exception if this is the case. + MPI.add_finalize_hook!(T8code.Libt8.sc_finalize) return nothing end From 33fced539cf2bd6dcdda8a100cbadc32da1a6601 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 25 Jul 2023 17:18:08 +0200 Subject: [PATCH 069/128] Restructured to do blocks. --- src/meshes/t8code_mesh.jl | 41 ++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 3e2c9fb78b..5b11777874 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -40,26 +40,27 @@ mutable struct T8codeMesh{NDIMS, RealT <: Real, IsParallel, NDIMSP2, NNODES} <: mesh.current_filename = current_filename mesh.tree_node_coordinates = tree_node_coordinates - finalizer(function (mesh::T8codeMesh) - # When finalizing `mesh.forest`, `mesh.scheme` and `mesh.cmesh` are - # also cleaned up from within `t8code`. The cleanup code for `cmesh` - # does some MPI calls for deallocating shared memory arrays. Due to - # garbage collection in Julia the order of shutdown is not - # deterministic. The following code might happen after MPI is already - # in finalized state. In this case the `finalize_hook` of the MPI - # module already took care of the cleanup. See further down. - if !MPI.Finalized() - trixi_t8_unref_forest(mesh.forest) - end - end, mesh) - - MPI.add_finalize_hook!(function () - try - trixi_t8_unref_forest(mesh.forest) - catch - # The `mesh` object was already finalized. Do nothing. - end - end) + finalizer(mesh) do mesh + # When finalizing `mesh.forest`, `mesh.scheme` and `mesh.cmesh` are + # also cleaned up from within `t8code`. The cleanup code for + # `cmesh` does some MPI calls for deallocating shared memory + # arrays. Due to garbage collection in Julia the order of shutdown + # is not deterministic. The following code might happen after MPI + # is already in finalized state. In this case the `finalize_hook` + # of the MPI module already took care of the cleanup. See further + # down. + if !MPI.Finalized() + trixi_t8_unref_forest(mesh.forest) + end + end + + MPI.add_finalize_hook!() do + try + trixi_t8_unref_forest(mesh.forest) + catch + # The `mesh` object was already finalized. Do nothing. + end + end return mesh end From b2d97a4c453599f2b6144946bdbfc958400ee385 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 25 Jul 2023 17:18:58 +0200 Subject: [PATCH 070/128] Moved save_mesh_file call to test file. --- .../t8code_2d_dgsem/elixir_advection_basic.jl | 9 --------- test/test_t8code_2d.jl | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/examples/t8code_2d_dgsem/elixir_advection_basic.jl b/examples/t8code_2d_dgsem/elixir_advection_basic.jl index 593e44bcfa..efc5122658 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_basic.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_basic.jl @@ -57,12 +57,3 @@ sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), # Print the timer summary summary_callback() - -# Save mesh file support will be added in the future. -# The following lines of code are here for -# satisfying code coverage. -try - Trixi.save_mesh_file(mesh, "dummy", timestep = 0) -catch - # Do nothing. -end diff --git a/test/test_t8code_2d.jl b/test/test_t8code_2d.jl index 785f3b0753..a424c9df84 100644 --- a/test/test_t8code_2d.jl +++ b/test/test_t8code_2d.jl @@ -13,6 +13,22 @@ isdir(outdir) && rm(outdir, recursive = true) mkdir(outdir) @testset "T8codeMesh2D" begin + + @trixi_testset "test save_mesh_file" begin + @test_throws Exception begin + # Save mesh file support will be added in the future. The following + # lines of code are here for satisfying code coverage. + + # Create dummy mesh. + mesh = T8codeMesh((1, 1), polydeg = 1, + mapping = Trixi.coordinates2mapping((-1.0, -1.0), ( 1.0, 1.0)), + initial_refinement_level = 1) + + # This call throws an error. + Trixi.save_mesh_file(mesh, "dummy") + end + end + @trixi_testset "elixir_advection_basic.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_basic.jl"), # Expected errors are exactly the same as with TreeMesh! From 19950c8a0c0c944a4de4851621bd94eda728f835 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 25 Jul 2023 17:20:56 +0200 Subject: [PATCH 071/128] Fixed spelling error. --- src/auxiliary/t8code.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index b96918c022..90266d43ba 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -27,7 +27,7 @@ function init_t8code() t8_init(T8code.Libt8.SC_LP_ERROR) # `sc_finalize` should always be called during shutdown of an application. - # It checks wether there is still un-freed memory by t8code and/or + # It checks whether there is still un-freed memory by t8code and/or # T8code.jl and throws an exception if this is the case. MPI.add_finalize_hook!(T8code.Libt8.sc_finalize) From 12a20e5f7e46f1e4da0fcecf63044366f33c2d06 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Wed, 26 Jul 2023 09:01:45 +0200 Subject: [PATCH 072/128] Made sc_finalize optional. --- src/auxiliary/t8code.jl | 18 ++++++++++++------ src/meshes/t8code_mesh.jl | 19 ++++++++++++------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index 90266d43ba..37cb782bb9 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -13,8 +13,8 @@ function init_t8code() end # Initialize the sc library, has to happen before we initialize t8code. - let catch_signals = 0, print_backtrace = 0 - T8code.Libt8.sc_init(mpi_comm(), catch_signals, print_backtrace, C_NULL, + let catch_signals = 0, print_backtrace = 0, log_handler = C_NULL + T8code.Libt8.sc_init(mpi_comm(), catch_signals, print_backtrace, log_handler, T8code.Libt8.SC_LP_ERROR) end @@ -26,10 +26,16 @@ function init_t8code() # Initialize t8code with log level ERROR to prevent a lot of output in AMR simulations. t8_init(T8code.Libt8.SC_LP_ERROR) - # `sc_finalize` should always be called during shutdown of an application. - # It checks whether there is still un-freed memory by t8code and/or - # T8code.jl and throws an exception if this is the case. - MPI.add_finalize_hook!(T8code.Libt8.sc_finalize) + if haskey(ENV, "TRIXI_T8CODE_SC_FINALIZE") + # Normally, `sc_finalize` should always be called during shutdown of an + # application. It checks whether there is still un-freed memory by t8code + # and/or T8code.jl and throws an exception if this is the case. For + # production runs this is not mandatory, but is helpful during + # development. Hence, this option is only activated when environment + # variable TRIXI_T8CODE_SC_FINALIZE exists. + @warn "T8code.jl: sc_finalize will be called during shutdown of Trixi.jl." + MPI.add_finalize_hook!(T8code.Libt8.sc_finalize) + end return nothing end diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 5b11777874..985e4f8a4d 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -46,19 +46,24 @@ mutable struct T8codeMesh{NDIMS, RealT <: Real, IsParallel, NDIMSP2, NNODES} <: # `cmesh` does some MPI calls for deallocating shared memory # arrays. Due to garbage collection in Julia the order of shutdown # is not deterministic. The following code might happen after MPI - # is already in finalized state. In this case the `finalize_hook` - # of the MPI module already took care of the cleanup. See further - # down. + # is already in finalized state. + # If the environment variable `TRIXI_T8CODE_SC_FINALIZE` is set the + # `finalize_hook` of the MPI module takes care of the cleanup. See + # further down. However, this might cause a pile-up of `mesh` + # objects during long-running sessions. if !MPI.Finalized() trixi_t8_unref_forest(mesh.forest) end end - MPI.add_finalize_hook!() do - try + # This finalizer call is only recommended during development and not for + # production runs, especially long-running sesions since a reference to + # the `mesh` object will be kept throughout the lifetime of the session. + # See comments in `init_t8code()` in file `src/auxiliary/t8code.jl` for + # more information. + if haskey(ENV, "TRIXI_T8CODE_SC_FINALIZE") + MPI.add_finalize_hook!() do trixi_t8_unref_forest(mesh.forest) - catch - # The `mesh` object was already finalized. Do nothing. end end From 7f9b0e259e71f51e40ccf42d630d66195c4c9f5a Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Wed, 26 Jul 2023 09:02:47 +0200 Subject: [PATCH 073/128] Fixed spelling. --- src/meshes/t8code_mesh.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 985e4f8a4d..13edcc2971 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -57,7 +57,7 @@ mutable struct T8codeMesh{NDIMS, RealT <: Real, IsParallel, NDIMSP2, NNODES} <: end # This finalizer call is only recommended during development and not for - # production runs, especially long-running sesions since a reference to + # production runs, especially long-running sessions since a reference to # the `mesh` object will be kept throughout the lifetime of the session. # See comments in `init_t8code()` in file `src/auxiliary/t8code.jl` for # more information. From 7cb972d98110feec7f4dab9145fbdbc5283ffa55 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Wed, 26 Jul 2023 09:37:13 +0200 Subject: [PATCH 074/128] Cleaned up examples. --- .../t8code_3d_dgsem/elixir_advection_amr.jl | 6 -- ...lixir_advection_amr_unstructured_curved.jl | 10 ---- .../t8code_3d_dgsem/elixir_advection_basic.jl | 12 ---- .../elixir_advection_cubed_sphere.jl | 60 ------------------- .../elixir_advection_nonconforming.jl | 9 +-- .../elixir_advection_unstructured_curved.jl | 11 ---- examples/t8code_3d_dgsem/elixir_euler_ec.jl | 5 -- .../elixir_euler_free_stream.jl | 10 +--- .../t8code_3d_dgsem/elixir_euler_sedov.jl | 5 -- ...terms_nonconforming_unstructured_curved.jl | 9 +-- .../elixir_euler_source_terms_nonperiodic.jl | 7 --- 11 files changed, 7 insertions(+), 137 deletions(-) delete mode 100644 examples/t8code_3d_dgsem/elixir_advection_cubed_sphere.jl diff --git a/examples/t8code_3d_dgsem/elixir_advection_amr.jl b/examples/t8code_3d_dgsem/elixir_advection_amr.jl index eff5688381..a7f7dfd7b2 100644 --- a/examples/t8code_3d_dgsem/elixir_advection_amr.jl +++ b/examples/t8code_3d_dgsem/elixir_advection_amr.jl @@ -42,11 +42,6 @@ analysis_callback = AnalysisCallback(semi, interval=analysis_interval, alive_callback = AliveCallback(analysis_interval=analysis_interval) -# save_solution = SaveSolutionCallback(interval=100, -# save_initial_solution=true, -# save_final_solution=true, -# solution_variables=cons2prim) - amr_controller = ControllerThreeLevel(semi, IndicatorMax(semi, variable=first), base_level=4, med_level=5, med_threshold=0.1, @@ -61,7 +56,6 @@ stepsize_callback = StepsizeCallback(cfl=1.2) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, - # save_solution, amr_callback, stepsize_callback) diff --git a/examples/t8code_3d_dgsem/elixir_advection_amr_unstructured_curved.jl b/examples/t8code_3d_dgsem/elixir_advection_amr_unstructured_curved.jl index 57d8de47cb..63745fbeff 100644 --- a/examples/t8code_3d_dgsem/elixir_advection_amr_unstructured_curved.jl +++ b/examples/t8code_3d_dgsem/elixir_advection_amr_unstructured_curved.jl @@ -77,14 +77,6 @@ analysis_callback = AnalysisCallback(semi, interval=analysis_interval, alive_callback = AliveCallback(analysis_interval=analysis_interval) -# save_restart = SaveRestartCallback(interval=100, -# save_final_restart=true) -# -# save_solution = SaveSolutionCallback(interval=100, -# save_initial_solution=true, -# save_final_solution=true, -# solution_variables=cons2prim) - amr_controller = ControllerThreeLevel(semi, IndicatorMax(semi, variable=first), base_level=1, med_level=2, med_threshold=0.1, @@ -99,8 +91,6 @@ stepsize_callback = StepsizeCallback(cfl=1.2) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, - # save_restart, - # save_solution, amr_callback, stepsize_callback) diff --git a/examples/t8code_3d_dgsem/elixir_advection_basic.jl b/examples/t8code_3d_dgsem/elixir_advection_basic.jl index 2d1358f56d..ec1ef2353a 100644 --- a/examples/t8code_3d_dgsem/elixir_advection_basic.jl +++ b/examples/t8code_3d_dgsem/elixir_advection_basic.jl @@ -3,7 +3,6 @@ using OrdinaryDiffEq using Trixi -# using Debugger ############################################################################### # semidiscretization of the linear advection equation @@ -42,22 +41,11 @@ summary_callback = SummaryCallback() # The AnalysisCallback allows to analyse the solution in regular intervals and prints the results analysis_callback = AnalysisCallback(semi, interval=100) -# Not supported yet. -# # The SaveRestartCallback allows to save a file from which a Trixi.jl simulation can be restarted -# save_restart = SaveRestartCallback(interval=100, -# save_final_restart=true) - -# Not supported yet. -# # The SaveSolutionCallback allows to save the solution to a file in regular intervals -# save_solution = SaveSolutionCallback(interval=100, -# solution_variables=cons2prim) - # The StepsizeCallback handles the re-calculation of the maximum Δt after each time step stepsize_callback = StepsizeCallback(cfl=1.2) # Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver callbacks = CallbackSet(summary_callback, analysis_callback, - # save_restart, save_solution, stepsize_callback) ############################################################################### diff --git a/examples/t8code_3d_dgsem/elixir_advection_cubed_sphere.jl b/examples/t8code_3d_dgsem/elixir_advection_cubed_sphere.jl deleted file mode 100644 index fe8937a09e..0000000000 --- a/examples/t8code_3d_dgsem/elixir_advection_cubed_sphere.jl +++ /dev/null @@ -1,60 +0,0 @@ -using OrdinaryDiffEq -using Trixi - -############################################################################### -# semidiscretization of the linear advection equation - -advection_velocity = (0.2, -0.7, 0.5) -equations = LinearScalarAdvectionEquation3D(advection_velocity) - -# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux -solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) - -initial_condition = initial_condition_convergence_test - -boundary_condition = BoundaryConditionDirichlet(initial_condition) -boundary_conditions = Dict( - :inside => boundary_condition, - :outside => boundary_condition, -) - -mesh = Trixi.P4estMeshCubedSphere(5, 3, 0.5, 0.5, - polydeg=3, initial_refinement_level=0) - -# A semidiscretization collects data structures and functions for the spatial discretization -semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, boundary_conditions=boundary_conditions) - -############################################################################### -# ODE solvers, callbacks etc. - -# Create ODE problem with time span from 0.0 to 1.0 -tspan = (0.0, 1.0) -ode = semidiscretize(semi, tspan) - -# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup -# and resets the timers -summary_callback = SummaryCallback() - -# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results -analysis_callback = AnalysisCallback(semi, interval=100) - -# The SaveSolutionCallback allows to save the solution to a file in regular intervals -save_solution = SaveSolutionCallback(interval=100, - solution_variables=cons2prim) - -# The StepsizeCallback handles the re-calculation of the maximum Δt after each time step -stepsize_callback = StepsizeCallback(cfl=1.2) - -# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver -callbacks = CallbackSet(summary_callback, analysis_callback, save_solution, stepsize_callback) - -############################################################################### -# run the simulation - -# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); - -# Print the timer summary -summary_callback() diff --git a/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl b/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl index 71fb178f61..27c98f0caa 100644 --- a/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl +++ b/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl @@ -53,6 +53,8 @@ new_forest_ref = Ref{Trixi.t8_forest_t}() Trixi.t8_forest_init(new_forest_ref); new_forest = new_forest_ref[] +# Check out `examples/t8_step4_partition_balance_ghost.jl` in +# https://github.com/DLR-AMR/T8code.jl for detailed explanations. let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0, do_ghost = 1 Trixi.t8_forest_set_user_data(new_forest, C_NULL) Trixi.t8_forest_set_adapt(new_forest, mesh.forest, @Trixi.t8_adapt_callback(adapt_callback), recursive) @@ -81,16 +83,11 @@ summary_callback = SummaryCallback() # The AnalysisCallback allows to analyse the solution in regular intervals and prints the results analysis_callback = AnalysisCallback(semi, interval=100) -# Not supported yet. -# # The SaveSolutionCallback allows to save the solution to a file in regular intervals -# save_solution = SaveSolutionCallback(interval=100, -# solution_variables=cons2prim) - # The StepsizeCallback handles the re-calculation of the maximum Δt after each time step stepsize_callback = StepsizeCallback(cfl=1.6) # Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver -callbacks = CallbackSet(summary_callback, analysis_callback, # save_solution, +callbacks = CallbackSet(summary_callback, analysis_callback, stepsize_callback) ############################################################################### diff --git a/examples/t8code_3d_dgsem/elixir_advection_unstructured_curved.jl b/examples/t8code_3d_dgsem/elixir_advection_unstructured_curved.jl index e1b1488fe5..2c1162d636 100644 --- a/examples/t8code_3d_dgsem/elixir_advection_unstructured_curved.jl +++ b/examples/t8code_3d_dgsem/elixir_advection_unstructured_curved.jl @@ -76,22 +76,11 @@ analysis_callback = AnalysisCallback(semi, interval=analysis_interval) alive_callback = AliveCallback(analysis_interval=analysis_interval) -# Not supported yet. -# # The SaveRestartCallback allows to save a file from which a Trixi.jl simulation can be restarted -# save_restart = SaveRestartCallback(interval=100, -# save_final_restart=true) - -# Not supported yet. -# # The SaveSolutionCallback allows to save the solution to a file in regular intervals -# save_solution = SaveSolutionCallback(interval=100, -# solution_variables=cons2prim) - # The StepsizeCallback handles the re-calculation of the maximum Δt after each time step stepsize_callback = StepsizeCallback(cfl=1.2) # Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, - # save_restart, save_solution, stepsize_callback) ############################################################################### diff --git a/examples/t8code_3d_dgsem/elixir_euler_ec.jl b/examples/t8code_3d_dgsem/elixir_euler_ec.jl index 79d37a1084..4389f97760 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_ec.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_ec.jl @@ -76,16 +76,11 @@ analysis_callback = AnalysisCallback(semi, interval = analysis_interval) alive_callback = AliveCallback(analysis_interval = analysis_interval) -# save_solution = SaveSolutionCallback(interval=100, -# save_initial_solution=true, -# save_final_solution=true) - stepsize_callback = StepsizeCallback(cfl = 1.0) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, - # save_solution, stepsize_callback) ############################################################################### diff --git a/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl b/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl index 96385d1ce8..21d9845141 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl @@ -1,4 +1,3 @@ - using Downloads: download using OrdinaryDiffEq using Trixi @@ -101,6 +100,8 @@ new_forest_ref = Ref{Trixi.t8_forest_t}() Trixi.t8_forest_init(new_forest_ref); new_forest = new_forest_ref[] +# Check out `examples/t8_step4_partition_balance_ghost.jl` in +# https://github.com/DLR-AMR/T8code.jl for detailed explanations. let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0, do_ghost = 1 Trixi.t8_forest_set_user_data(new_forest, C_NULL) Trixi.t8_forest_set_adapt(new_forest, mesh.forest, @Trixi.t8_adapt_callback(adapt_callback), recursive) @@ -127,19 +128,12 @@ analysis_callback = AnalysisCallback(semi, interval=analysis_interval) alive_callback = AliveCallback(analysis_interval=analysis_interval) -# save_solution = SaveSolutionCallback(interval=100, -# save_initial_solution=true, -# save_final_solution=true, -# solution_variables=cons2prim) - stepsize_callback = StepsizeCallback(cfl=1.2) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, - # save_solution, stepsize_callback) - ############################################################################### # run the simulation diff --git a/examples/t8code_3d_dgsem/elixir_euler_sedov.jl b/examples/t8code_3d_dgsem/elixir_euler_sedov.jl index 3cbae73890..8aa91b8ddc 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_sedov.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_sedov.jl @@ -83,16 +83,11 @@ analysis_callback = AnalysisCallback(semi, interval = analysis_interval) alive_callback = AliveCallback(analysis_interval = analysis_interval) -# save_solution = SaveSolutionCallback(interval=100, -# save_initial_solution=true, -# save_final_solution=true) - stepsize_callback = StepsizeCallback(cfl = 0.5) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, - # save_solution, stepsize_callback) ############################################################################### diff --git a/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl b/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl index a8e54770a1..71bf481297 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl @@ -102,6 +102,8 @@ new_forest_ref = Ref{Trixi.t8_forest_t}() Trixi.t8_forest_init(new_forest_ref); new_forest = new_forest_ref[] +# Check out `examples/t8_step4_partition_balance_ghost.jl` in +# https://github.com/DLR-AMR/T8code.jl for detailed explanations. let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0, do_ghost = 1 Trixi.t8_forest_set_user_data(new_forest, C_NULL) Trixi.t8_forest_set_adapt(new_forest, mesh.forest, @Trixi.t8_adapt_callback(adapt_callback), recursive) @@ -130,17 +132,10 @@ analysis_callback = AnalysisCallback(semi, interval=analysis_interval) alive_callback = AliveCallback(analysis_interval=analysis_interval) -# Not supported yet. -# save_solution = SaveSolutionCallback(interval=100, -# save_initial_solution=true, -# save_final_solution=true, -# solution_variables=cons2prim) - stepsize_callback = StepsizeCallback(cfl=0.6) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, - # save_solution, stepsize_callback); ############################################################################### diff --git a/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonperiodic.jl b/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonperiodic.jl index b503da3c13..e75d92cbd9 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonperiodic.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonperiodic.jl @@ -49,19 +49,12 @@ analysis_callback = AnalysisCallback(semi, interval=analysis_interval) alive_callback = AliveCallback(analysis_interval=analysis_interval) -# save_solution = SaveSolutionCallback(interval=100, -# save_initial_solution=true, -# save_final_solution=true, -# solution_variables=cons2prim) - stepsize_callback = StepsizeCallback(cfl=0.6) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, - # save_solution, stepsize_callback) - ############################################################################### # run the simulation From 2c0861f0072765736b8ecc58cb05aa289f04863e Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Wed, 26 Jul 2023 09:41:08 +0200 Subject: [PATCH 075/128] Updated and cleaned t8code solver codes. --- src/solvers/dgsem_t8code/containers_2d.jl | 28 +- src/solvers/dgsem_t8code/containers_3d.jl | 441 +++++++++++----------- 2 files changed, 237 insertions(+), 232 deletions(-) diff --git a/src/solvers/dgsem_t8code/containers_2d.jl b/src/solvers/dgsem_t8code/containers_2d.jl index 4fb3e44080..79bb28d612 100644 --- a/src/solvers/dgsem_t8code/containers_2d.jl +++ b/src/solvers/dgsem_t8code/containers_2d.jl @@ -1,3 +1,7 @@ +# By default, Julia/LLVM does not use fused multiply-add operations (FMAs). +# Since these FMAs can increase the performance of many numerical algorithms, +# we need to opt-in explicitly. +# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details. @muladd begin #! format: noindent @@ -54,17 +58,17 @@ function calc_node_coordinates!(node_coordinates, end return node_coordinates - end +end - function trixi_t8_init_mortar_neighbor_ids!(mortars::P4estMortarContainer{2}, my_face, - other_face, orientation, neighbor_ielements, - mortar_id) - if orientation == 0 - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[1] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[2] + 1 - else - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 - end - end +function trixi_t8_init_mortar_neighbor_ids!(mortars::P4estMortarContainer{2}, my_face, + other_face, orientation, neighbor_ielements, + mortar_id) + if orientation == 0 + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[2] + 1 + else + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 + end +end end # @muladd diff --git a/src/solvers/dgsem_t8code/containers_3d.jl b/src/solvers/dgsem_t8code/containers_3d.jl index 65fb049f39..c8f09527f9 100644 --- a/src/solvers/dgsem_t8code/containers_3d.jl +++ b/src/solvers/dgsem_t8code/containers_3d.jl @@ -3,229 +3,230 @@ # we need to opt-in explicitly. # See https://ranocha.de/blog/Optimizing_EC_Trixi for further details. @muladd begin - - # Interpolate tree_node_coordinates to each quadrant at the specified nodes - function calc_node_coordinates!(node_coordinates, - mesh::T8codeMesh{3}, - nodes::AbstractVector) - # We use `StrideArray`s here since these buffers are used in performance-critical - # places and the additional information passed to the compiler makes them faster - # than native `Array`s. - tmp1 = StrideArray(undef, real(mesh), - StaticInt(3), static_length(nodes), static_length(mesh.nodes), - static_length(mesh.nodes)) - matrix1 = StrideArray(undef, real(mesh), - static_length(nodes), static_length(mesh.nodes)) - matrix2 = similar(matrix1) - matrix3 = similar(matrix1) - baryweights_in = barycentric_weights(mesh.nodes) - - num_local_trees = t8_forest_get_num_local_trees(mesh.forest) - - current_index = 0 - for itree in 0:(num_local_trees - 1) - tree_class = t8_forest_get_tree_class(mesh.forest, itree) - eclass_scheme = t8_forest_get_eclass_scheme(mesh.forest, tree_class) - num_elements_in_tree = t8_forest_get_tree_num_elements(mesh.forest, itree) - - for ielement in 0:(num_elements_in_tree - 1) - element = t8_forest_get_element_in_tree(mesh.forest, itree, ielement) - element_level = t8_element_level(eclass_scheme, element) - - element_length = t8_hex_len(element_level) / t8_hex_root_len - - element_coords = Vector{Cdouble}(undef, 3) - t8_element_vertex_reference_coords(eclass_scheme, element, 0, - pointer(element_coords)) - - nodes_out_x = 2 * - (element_length * 1 / 2 * (nodes .+ 1) .+ element_coords[1]) .- - 1 - nodes_out_y = 2 * - (element_length * 1 / 2 * (nodes .+ 1) .+ element_coords[2]) .- - 1 - nodes_out_z = 2 * - (element_length * 1 / 2 * (nodes .+ 1) .+ element_coords[3]) .- - 1 - - polynomial_interpolation_matrix!(matrix1, mesh.nodes, nodes_out_x, - baryweights_in) - polynomial_interpolation_matrix!(matrix2, mesh.nodes, nodes_out_y, - baryweights_in) - polynomial_interpolation_matrix!(matrix3, mesh.nodes, nodes_out_z, - baryweights_in) - - multiply_dimensionwise!(view(node_coordinates, :, :, :, :, - current_index += 1), - matrix1, matrix2, matrix3, - view(mesh.tree_node_coordinates, :, :, :, :, - itree + 1), - tmp1) - end +#! format: noindent + +# Interpolate tree_node_coordinates to each quadrant at the specified nodes +function calc_node_coordinates!(node_coordinates, + mesh::T8codeMesh{3}, + nodes::AbstractVector) + # We use `StrideArray`s here since these buffers are used in performance-critical + # places and the additional information passed to the compiler makes them faster + # than native `Array`s. + tmp1 = StrideArray(undef, real(mesh), + StaticInt(3), static_length(nodes), static_length(mesh.nodes), + static_length(mesh.nodes)) + matrix1 = StrideArray(undef, real(mesh), + static_length(nodes), static_length(mesh.nodes)) + matrix2 = similar(matrix1) + matrix3 = similar(matrix1) + baryweights_in = barycentric_weights(mesh.nodes) + + num_local_trees = t8_forest_get_num_local_trees(mesh.forest) + + current_index = 0 + for itree in 0:(num_local_trees - 1) + tree_class = t8_forest_get_tree_class(mesh.forest, itree) + eclass_scheme = t8_forest_get_eclass_scheme(mesh.forest, tree_class) + num_elements_in_tree = t8_forest_get_tree_num_elements(mesh.forest, itree) + + for ielement in 0:(num_elements_in_tree - 1) + element = t8_forest_get_element_in_tree(mesh.forest, itree, ielement) + element_level = t8_element_level(eclass_scheme, element) + + element_length = t8_hex_len(element_level) / t8_hex_root_len + + element_coords = Vector{Cdouble}(undef, 3) + t8_element_vertex_reference_coords(eclass_scheme, element, 0, + pointer(element_coords)) + + nodes_out_x = 2 * + (element_length * 1 / 2 * (nodes .+ 1) .+ element_coords[1]) .- + 1 + nodes_out_y = 2 * + (element_length * 1 / 2 * (nodes .+ 1) .+ element_coords[2]) .- + 1 + nodes_out_z = 2 * + (element_length * 1 / 2 * (nodes .+ 1) .+ element_coords[3]) .- + 1 + + polynomial_interpolation_matrix!(matrix1, mesh.nodes, nodes_out_x, + baryweights_in) + polynomial_interpolation_matrix!(matrix2, mesh.nodes, nodes_out_y, + baryweights_in) + polynomial_interpolation_matrix!(matrix3, mesh.nodes, nodes_out_z, + baryweights_in) + + multiply_dimensionwise!(view(node_coordinates, :, :, :, :, + current_index += 1), + matrix1, matrix2, matrix3, + view(mesh.tree_node_coordinates, :, :, :, :, + itree + 1), + tmp1) end - - return node_coordinates end - # This routine was copied and adapated from `src/dgsem_p4est/containers_3d.jl`: `orientation_to_indices_p4est`. - function trixi_t8_init_mortar_neighbor_ids!(mortars::P4estMortarContainer{3}, my_face, - other_face, orientation, neighbor_ielements, - mortar_id) - # my_face and other_face are the face directions (zero-based) - # of "my side" and "other side" respectively. - # Face corner 0 of the face with the lower face direction connects to a corner of the other face. - # The number of this corner is the orientation code in `p4est`. - lower = my_face <= other_face - - # x_pos, y_neg, and z_pos are the directions in which the face has right-handed coordinates - # when looked at from the outside. - my_right_handed = my_face in (1, 2, 5) - other_right_handed = other_face in (1, 2, 5) - - # If both or none are right-handed when looked at from the outside, they will have different - # orientations when looked at from the same side of the interface. - flipped = my_right_handed == other_right_handed - - # In the following illustrations, the face corner numbering of `p4est` is shown. - # ξ and η are the local coordinates of the respective face. - # We're looking at both faces from the same side of the interface, so that "other side" - # (in the illustrations on the left) has right-handed coordinates. - if !flipped - if orientation == 0 - # Corner 0 of other side matches corner 0 of my side - # 2┌──────┐3 2┌──────┐3 - # │ │ │ │ - # │ │ │ │ - # 0└──────┘1 0└──────┘1 - # η η - # ↑ ↑ - # │ │ - # └───> ξ └───> ξ - - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[1] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[2] + 1 - mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[3] + 1 - mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[4] + 1 - - elseif ((lower && orientation == 2) # Corner 0 of my side matches corner 2 of other side - || - (!lower && orientation == 1)) # Corner 0 of other side matches corner 1 of my side - # 2┌──────┐3 0┌──────┐2 - # │ │ │ │ - # │ │ │ │ - # 0└──────┘1 1└──────┘3 - # η ┌───> η - # ↑ │ - # │ ↓ - # └───> ξ ξ - - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[4] + 1 - mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[1] + 1 - mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[3] + 1 - - elseif ((lower && orientation == 1) # Corner 0 of my side matches corner 1 of other side - || - (!lower && orientation == 2)) # Corner 0 of other side matches corner 2 of my side - # 2┌──────┐3 3┌──────┐1 - # │ │ │ │ - # │ │ │ │ - # 0└──────┘1 2└──────┘0 - # η ξ - # ↑ ↑ - # │ │ - # └───> ξ η <───┘ - - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[3] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 - mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[4] + 1 - mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[2] + 1 - - else # orientation == 3 - # Corner 0 of my side matches corner 3 of other side and - # corner 0 of other side matches corner 3 of my side. - # 2┌──────┐3 1┌──────┐0 - # │ │ │ │ - # │ │ │ │ - # 0└──────┘1 3└──────┘2 - # η ξ <───┐ - # ↑ │ - # │ ↓ - # └───> ξ η - - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[4] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[3] + 1 - mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[2] + 1 - mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[1] + 1 - end - else # flipped - if orientation == 0 - # Corner 0 of other side matches corner 0 of my side - # 2┌──────┐3 1┌──────┐3 - # │ │ │ │ - # │ │ │ │ - # 0└──────┘1 0└──────┘2 - # η ξ - # ↑ ↑ - # │ │ - # └───> ξ └───> η - - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[1] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[3] + 1 - mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[2] + 1 - mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[4] + 1 - - elseif orientation == 2 - # Corner 0 of my side matches corner 2 of other side and - # corner 0 of other side matches corner 2 of my side. - # 2┌──────┐3 0┌──────┐1 - # │ │ │ │ - # │ │ │ │ - # 0└──────┘1 2└──────┘3 - # η ┌───> ξ - # ↑ │ - # │ ↓ - # └───> ξ η - - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[3] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[4] + 1 - mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[1] + 1 - mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[2] + 1 - - elseif orientation == 1 - # Corner 0 of my side matches corner 1 of other side and - # corner 0 of other side matches corner 1 of my side. - # 2┌──────┐3 3┌──────┐2 - # │ │ │ │ - # │ │ │ │ - # 0└──────┘1 1└──────┘0 - # η η - # ↑ ↑ - # │ │ - # └───> ξ ξ <───┘ - - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 - mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[4] + 1 - mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[3] + 1 - - else # orientation == 3 - # Corner 0 of my side matches corner 3 of other side and - # corner 0 of other side matches corner 3 of my side. - # 2┌──────┐3 2┌──────┐0 - # │ │ │ │ - # │ │ │ │ - # 0└──────┘1 3└──────┘1 - # η η <───┐ - # ↑ │ - # │ ↓ - # └───> ξ ξ - - mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[4] + 1 - mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[2] + 1 - mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[3] + 1 - mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[1] + 1 - end + return node_coordinates +end + +# This routine was copied and adapated from `src/dgsem_p4est/containers_3d.jl`: `orientation_to_indices_p4est`. +function trixi_t8_init_mortar_neighbor_ids!(mortars::P4estMortarContainer{3}, my_face, + other_face, orientation, neighbor_ielements, + mortar_id) + # my_face and other_face are the face directions (zero-based) + # of "my side" and "other side" respectively. + # Face corner 0 of the face with the lower face direction connects to a corner of the other face. + # The number of this corner is the orientation code in `p4est`. + lower = my_face <= other_face + + # x_pos, y_neg, and z_pos are the directions in which the face has right-handed coordinates + # when looked at from the outside. + my_right_handed = my_face in (1, 2, 5) + other_right_handed = other_face in (1, 2, 5) + + # If both or none are right-handed when looked at from the outside, they will have different + # orientations when looked at from the same side of the interface. + flipped = my_right_handed == other_right_handed + + # In the following illustrations, the face corner numbering of `p4est` is shown. + # ξ and η are the local coordinates of the respective face. + # We're looking at both faces from the same side of the interface, so that "other side" + # (in the illustrations on the left) has right-handed coordinates. + if !flipped + if orientation == 0 + # Corner 0 of other side matches corner 0 of my side + # 2┌──────┐3 2┌──────┐3 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 0└──────┘1 + # η η + # ↑ ↑ + # │ │ + # └───> ξ └───> ξ + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[3] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[4] + 1 + + elseif ((lower && orientation == 2) # Corner 0 of my side matches corner 2 of other side + || + (!lower && orientation == 1)) # Corner 0 of other side matches corner 1 of my side + # 2┌──────┐3 0┌──────┐2 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 1└──────┘3 + # η ┌───> η + # ↑ │ + # │ ↓ + # └───> ξ ξ + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[4] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[3] + 1 + + elseif ((lower && orientation == 1) # Corner 0 of my side matches corner 1 of other side + || + (!lower && orientation == 2)) # Corner 0 of other side matches corner 2 of my side + # 2┌──────┐3 3┌──────┐1 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 2└──────┘0 + # η ξ + # ↑ ↑ + # │ │ + # └───> ξ η <───┘ + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[3] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[4] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[2] + 1 + + else # orientation == 3 + # Corner 0 of my side matches corner 3 of other side and + # corner 0 of other side matches corner 3 of my side. + # 2┌──────┐3 1┌──────┐0 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 3└──────┘2 + # η ξ <───┐ + # ↑ │ + # │ ↓ + # └───> ξ η + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[4] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[3] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[1] + 1 + end + else # flipped + if orientation == 0 + # Corner 0 of other side matches corner 0 of my side + # 2┌──────┐3 1┌──────┐3 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 0└──────┘2 + # η ξ + # ↑ ↑ + # │ │ + # └───> ξ └───> η + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[3] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[4] + 1 + + elseif orientation == 2 + # Corner 0 of my side matches corner 2 of other side and + # corner 0 of other side matches corner 2 of my side. + # 2┌──────┐3 0┌──────┐1 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 2└──────┘3 + # η ┌───> ξ + # ↑ │ + # │ ↓ + # └───> ξ η + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[3] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[4] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[2] + 1 + + elseif orientation == 1 + # Corner 0 of my side matches corner 1 of other side and + # corner 0 of other side matches corner 1 of my side. + # 2┌──────┐3 3┌──────┐2 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 1└──────┘0 + # η η + # ↑ ↑ + # │ │ + # └───> ξ ξ <───┘ + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[4] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[3] + 1 + + else # orientation == 3 + # Corner 0 of my side matches corner 3 of other side and + # corner 0 of other side matches corner 3 of my side. + # 2┌──────┐3 2┌──────┐0 + # │ │ │ │ + # │ │ │ │ + # 0└──────┘1 3└──────┘1 + # η η <───┐ + # ↑ │ + # │ ↓ + # └───> ξ ξ + + mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[4] + 1 + mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[2] + 1 + mortars.neighbor_ids[3, mortar_id] = neighbor_ielements[3] + 1 + mortars.neighbor_ids[4, mortar_id] = neighbor_ielements[1] + 1 end end +end end # @muladd From 0e49cb5713459f405b4ef1c1e41a012c80e04c75 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Wed, 26 Jul 2023 09:42:16 +0200 Subject: [PATCH 076/128] Updated tests for t8code 3D code. --- .github/workflows/ci.yml | 1 + test/runtests.jl | 4 ++++ test/test_t8code_3d.jl | 15 --------------- 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4790f93d91..f5b037fba7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,6 +70,7 @@ jobs: - p4est_part1 - p4est_part2 - t8code_part1 + - t8code_part2 - unstructured_dgmulti - parabolic - paper_self_gravitating_gas_dynamics diff --git a/test/runtests.jl b/test/runtests.jl index 1d7eefe1fc..f718acc26b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -86,6 +86,10 @@ const TRIXI_NTHREADS = clamp(Sys.CPU_THREADS, 2, 3) include("test_t8code_2d.jl") end + @time if TRIXI_TEST == "all" || TRIXI_TEST == "t8code_part2" + include("test_t8code_3d.jl") + end + @time if TRIXI_TEST == "all" || TRIXI_TEST == "unstructured_dgmulti" include("test_unstructured_2d.jl") include("test_dgmulti_1d.jl") diff --git a/test/test_t8code_3d.jl b/test/test_t8code_3d.jl index db2e7debaf..01fda989aa 100644 --- a/test/test_t8code_3d.jl +++ b/test/test_t8code_3d.jl @@ -31,15 +31,6 @@ mkdir(outdir) @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_nonconforming.jl"), l2=[0.00253595715323843], linf=[0.016486952252155795]) - - # Ensure that we do not have excessive memory allocations - # (e.g., from type instabilities) - let - t = sol.t[end] - u_ode = sol.u[end] - du_ode = similar(u_ode) - @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000 - end end @trixi_testset "elixir_advection_amr.jl" begin @@ -61,12 +52,6 @@ mkdir(outdir) base_level = 0, med_level = 1, max_level = 2)) end - # @trixi_testset "elixir_advection_cubed_sphere.jl" begin - # @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_cubed_sphere.jl"), - # l2 = [0.002006918015656413], - # linf = [0.027655117058380085]) - # end - @trixi_testset "elixir_euler_source_terms_nonconforming_unstructured_curved.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_source_terms_nonconforming_unstructured_curved.jl"), From a36001d6413737c4f440a3509406bc11878cb51b Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Wed, 26 Jul 2023 09:45:39 +0200 Subject: [PATCH 077/128] Fixed spelling. --- src/solvers/dgsem_t8code/containers_3d.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solvers/dgsem_t8code/containers_3d.jl b/src/solvers/dgsem_t8code/containers_3d.jl index c8f09527f9..bc01ce6753 100644 --- a/src/solvers/dgsem_t8code/containers_3d.jl +++ b/src/solvers/dgsem_t8code/containers_3d.jl @@ -68,7 +68,7 @@ function calc_node_coordinates!(node_coordinates, return node_coordinates end -# This routine was copied and adapated from `src/dgsem_p4est/containers_3d.jl`: `orientation_to_indices_p4est`. +# This routine was copied and adapted from `src/dgsem_p4est/containers_3d.jl`: `orientation_to_indices_p4est`. function trixi_t8_init_mortar_neighbor_ids!(mortars::P4estMortarContainer{3}, my_face, other_face, orientation, neighbor_ielements, mortar_id) From e2599f040a7656da6681f0bce0b63bafbae9429c Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Wed, 26 Jul 2023 10:01:49 +0200 Subject: [PATCH 078/128] Update elixir_euler_source_terms_nonconforming_unstructured_curved.jl --- ..._terms_nonconforming_unstructured_curved.jl | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/examples/p4est_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl b/examples/p4est_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl index 8b6b359ea4..fd77352233 100644 --- a/examples/p4est_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl +++ b/examples/p4est_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl @@ -113,21 +113,3 @@ sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), save_everystep=false, callback=callbacks); summary_callback() # print the timer summary - - - -data = [sol.u[2]] - -dataname = ["test"] -filename = "final.vtu" -write_data = true - -num_cells = Trixi.ncells(mesh) - -coords = reshape(semi.cache.elements.node_coordinates,size(semi.cache.elements.node_coordinates,1),prod(size(semi.cache.elements.node_coordinates)[2:end])) - -rd = Trixi.StartUpDG.RefElemData(Trixi.StartUpDG.Hex(), 4) - -Trixi.StartUpDG.MeshData_to_vtk(rd, num_cells, coords, data, dataname, filename, write_data); - - From a8d5de770cb6a8a3d243043e0777f72923620c1a Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Wed, 26 Jul 2023 10:02:10 +0200 Subject: [PATCH 079/128] Update elixir_euler_source_terms_nonconforming_unstructured_curved.jl --- ...lixir_euler_source_terms_nonconforming_unstructured_curved.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/p4est_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl b/examples/p4est_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl index fd77352233..dd39409175 100644 --- a/examples/p4est_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl +++ b/examples/p4est_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl @@ -112,4 +112,3 @@ sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback save_everystep=false, callback=callbacks); summary_callback() # print the timer summary - From f30a61b2fb12a1b8f2b2f7a79e604033ba0e48d9 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Wed, 26 Jul 2023 16:08:41 +0200 Subject: [PATCH 080/128] Fixed indentation. --- src/meshes/t8code_mesh.jl | 1 - src/solvers/dgsem_t8code/containers_2d.jl | 1 - src/solvers/dgsem_t8code/dg.jl | 1 - 3 files changed, 3 deletions(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 0fcd731008..3f4456627c 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -256,7 +256,6 @@ conforming mesh from a `t8_cmesh` data structure. function T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh}; mapping = nothing, polydeg = 1, RealT = Float64, initial_refinement_level = 0) where {NDIMS} - scheme = t8_scheme_new_default_cxx() forest = t8_forest_new_uniform(cmesh, scheme, initial_refinement_level, 0, mpi_comm()) diff --git a/src/solvers/dgsem_t8code/containers_2d.jl b/src/solvers/dgsem_t8code/containers_2d.jl index 163ff8659e..79bb28d612 100644 --- a/src/solvers/dgsem_t8code/containers_2d.jl +++ b/src/solvers/dgsem_t8code/containers_2d.jl @@ -71,5 +71,4 @@ function trixi_t8_init_mortar_neighbor_ids!(mortars::P4estMortarContainer{2}, my mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] + 1 end end - end # @muladd diff --git a/src/solvers/dgsem_t8code/dg.jl b/src/solvers/dgsem_t8code/dg.jl index d66f6c1b1c..6e9660c917 100644 --- a/src/solvers/dgsem_t8code/dg.jl +++ b/src/solvers/dgsem_t8code/dg.jl @@ -29,5 +29,4 @@ end include("containers.jl") include("containers_2d.jl") include("containers_3d.jl") - end # @muladd From 79ab0cb5f1a0a4d5f35c4d31b2fdfbff65029f04 Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Thu, 31 Aug 2023 11:00:01 +0200 Subject: [PATCH 081/128] Update src/solvers/dgsem_structured/dg_3d.jl Co-authored-by: Hendrik Ranocha --- src/solvers/dgsem_structured/dg_3d.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/solvers/dgsem_structured/dg_3d.jl b/src/solvers/dgsem_structured/dg_3d.jl index 5a898e9b15..0b163e60e2 100644 --- a/src/solvers/dgsem_structured/dg_3d.jl +++ b/src/solvers/dgsem_structured/dg_3d.jl @@ -270,9 +270,9 @@ end # [arXiv: 2008.12044v2](https://arxiv.org/pdf/2008.12044) @inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, fstar3_L, fstar3_R, u, - mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3 - } - }, nonconservative_terms::False, + mesh::Union{StructuredMesh{3}, P4estMesh{3}, + T8codeMesh{3}} + nonconservative_terms::False, equations, volume_flux_fv, dg::DGSEM, element, cache) @unpack contravariant_vectors = cache.elements @unpack weights, derivative_matrix = dg.basis From 1453ad576598a6df509279aa7b158bd01eea2533 Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Thu, 31 Aug 2023 11:00:20 +0200 Subject: [PATCH 082/128] Update src/solvers/dgsem_t8code/containers_3d.jl Co-authored-by: Andrew Winters --- src/solvers/dgsem_t8code/containers_3d.jl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/solvers/dgsem_t8code/containers_3d.jl b/src/solvers/dgsem_t8code/containers_3d.jl index bc01ce6753..e2fe996313 100644 --- a/src/solvers/dgsem_t8code/containers_3d.jl +++ b/src/solvers/dgsem_t8code/containers_3d.jl @@ -39,15 +39,15 @@ function calc_node_coordinates!(node_coordinates, t8_element_vertex_reference_coords(eclass_scheme, element, 0, pointer(element_coords)) - nodes_out_x = 2 * - (element_length * 1 / 2 * (nodes .+ 1) .+ element_coords[1]) .- - 1 - nodes_out_y = 2 * - (element_length * 1 / 2 * (nodes .+ 1) .+ element_coords[2]) .- - 1 - nodes_out_z = 2 * - (element_length * 1 / 2 * (nodes .+ 1) .+ element_coords[3]) .- - 1 + nodes_out_x = (2 * + (element_length * 0.5 * (nodes .+ 1) .+ element_coords[1]) .- + 1) + nodes_out_y = (2 * + (element_length * 0.5 * (nodes .+ 1) .+ element_coords[2]) .- + 1) + nodes_out_z = (2 * + (element_length * 0.5 * (nodes .+ 1) .+ element_coords[3]) .- + 1) polynomial_interpolation_matrix!(matrix1, mesh.nodes, nodes_out_x, baryweights_in) From 54655e3e817f5711b13efcb0a41edcaf1865d003 Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Thu, 31 Aug 2023 11:01:00 +0200 Subject: [PATCH 083/128] Update src/callbacks_step/amr_dg3d.jl Co-authored-by: Andrew Winters --- src/callbacks_step/amr_dg3d.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/callbacks_step/amr_dg3d.jl b/src/callbacks_step/amr_dg3d.jl index 52baa58c8b..de5030e2ad 100644 --- a/src/callbacks_step/amr_dg3d.jl +++ b/src/callbacks_step/amr_dg3d.jl @@ -327,7 +327,7 @@ function adapt!(u_ode::AbstractVector, adaptor, mesh::T8codeMesh{3}, equations, old_index = 1 new_index = 1 - # Note: This is true for `hexs`. + # Note: This is only true for `hexs`. T8_CHILDREN = 8 # Retain current solution data. From 0372153d845a724e775ac5b878d77cf448693091 Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Thu, 31 Aug 2023 11:01:26 +0200 Subject: [PATCH 084/128] Update examples/t8code_3d_dgsem/elixir_euler_ec.jl Co-authored-by: Andrew Winters --- examples/t8code_3d_dgsem/elixir_euler_ec.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/t8code_3d_dgsem/elixir_euler_ec.jl b/examples/t8code_3d_dgsem/elixir_euler_ec.jl index 4389f97760..6e55e6a3eb 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_ec.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_ec.jl @@ -51,7 +51,7 @@ isfile(mesh_file) || mesh_file) # INP mesh files are only support by p4est. Hence, we -# create a p4est connecvity object first from which +# create a p4est connectivity object first from which # we can create a t8code mesh. conn = Trixi.read_inp_p4est(mesh_file, Val(3)) From 7a7ccb1d76812cc5d15c8d4e676bea53b7a08ba9 Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Thu, 31 Aug 2023 11:01:43 +0200 Subject: [PATCH 085/128] Update examples/t8code_3d_dgsem/elixir_advection_unstructured_curved.jl Co-authored-by: Andrew Winters --- .../t8code_3d_dgsem/elixir_advection_unstructured_curved.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/t8code_3d_dgsem/elixir_advection_unstructured_curved.jl b/examples/t8code_3d_dgsem/elixir_advection_unstructured_curved.jl index 2c1162d636..6640cac021 100644 --- a/examples/t8code_3d_dgsem/elixir_advection_unstructured_curved.jl +++ b/examples/t8code_3d_dgsem/elixir_advection_unstructured_curved.jl @@ -49,7 +49,7 @@ isfile(mesh_file) || download("https://gist.githubusercontent.com/efaulhaber/d45 mesh_file) # INP mesh files are only support by p4est. Hence, we -# create a p4est connecvity object first from which +# create a p4est connectivity object first from which # we can create a t8code mesh. conn = Trixi.read_inp_p4est(mesh_file,Val(3)) From 70bf2d32b34dc5ec890c22ec729181a8d9d0851e Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Thu, 31 Aug 2023 11:02:15 +0200 Subject: [PATCH 086/128] Update examples/t8code_3d_dgsem/elixir_advection_amr_unstructured_curved.jl Co-authored-by: Andrew Winters --- .../t8code_3d_dgsem/elixir_advection_amr_unstructured_curved.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/t8code_3d_dgsem/elixir_advection_amr_unstructured_curved.jl b/examples/t8code_3d_dgsem/elixir_advection_amr_unstructured_curved.jl index 63745fbeff..8d72b9b627 100644 --- a/examples/t8code_3d_dgsem/elixir_advection_amr_unstructured_curved.jl +++ b/examples/t8code_3d_dgsem/elixir_advection_amr_unstructured_curved.jl @@ -52,7 +52,7 @@ isfile(mesh_file) || download("https://gist.githubusercontent.com/efaulhaber/b8d mesh_file) # INP mesh files are only support by p4est. Hence, we -# create a p4est connecvity object first from which +# create a p4est connectivity object first from which # we can create a t8code mesh. conn = Trixi.read_inp_p4est(mesh_file,Val(3)) From 5de7c73d07dac1a743c9d47b1fe4a852022103d3 Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Thu, 31 Aug 2023 11:03:07 +0200 Subject: [PATCH 087/128] Update src/solvers/dgsem_structured/dg_3d.jl Co-authored-by: Hendrik Ranocha --- src/solvers/dgsem_structured/dg_3d.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/solvers/dgsem_structured/dg_3d.jl b/src/solvers/dgsem_structured/dg_3d.jl index 0b163e60e2..a2942d26f0 100644 --- a/src/solvers/dgsem_structured/dg_3d.jl +++ b/src/solvers/dgsem_structured/dg_3d.jl @@ -366,9 +366,9 @@ end # # Calculate the finite volume fluxes inside curvilinear elements (**with non-conservative terms**). @inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, fstar3_L, fstar3_R, u, - mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3 - } - }, nonconservative_terms::True, + mesh::Union{StructuredMesh{3}, P4estMesh{3}, + T8codeMesh{3}}, + nonconservative_terms::True, equations, volume_flux_fv, dg::DGSEM, element, cache) @unpack contravariant_vectors = cache.elements @unpack weights, derivative_matrix = dg.basis From 2246f83f7405a6465567f83054a6b81922be4fe5 Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Thu, 31 Aug 2023 11:04:10 +0200 Subject: [PATCH 088/128] Update src/meshes/t8code_mesh.jl Co-authored-by: Hendrik Ranocha --- src/meshes/t8code_mesh.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 3f4456627c..ca593b9c94 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -173,7 +173,7 @@ function T8codeMesh(trees_per_dimension; polydeg, # Non-periodic boundaries. boundary_names = fill(Symbol("---"), 2 * NDIMS, prod(trees_per_dimension)) - if mapping == nothing + if mapping === nothing mapping_ = coordinates2mapping(ntuple(_ -> -1.0, NDIMS), ntuple(_ -> 1.0, NDIMS)) else mapping_ = mapping From 7b0baf7e43bb47724387fe4740bed0280bd92a56 Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Thu, 31 Aug 2023 11:04:59 +0200 Subject: [PATCH 089/128] Update src/callbacks_step/analysis_dg3d.jl Co-authored-by: Hendrik Ranocha --- src/callbacks_step/analysis_dg3d.jl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/callbacks_step/analysis_dg3d.jl b/src/callbacks_step/analysis_dg3d.jl index 295f27c73a..3dafe75fb1 100644 --- a/src/callbacks_step/analysis_dg3d.jl +++ b/src/callbacks_step/analysis_dg3d.jl @@ -252,9 +252,8 @@ function integrate(func::Func, u, end function analyze(::typeof(entropy_timederivative), du, u, t, - mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3 - } - }, + mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, + T8codeMesh{3}}, equations, dg::DG, cache) # Calculate ∫(∂S/∂u ⋅ ∂u/∂t)dΩ integrate_via_indices(u, mesh, equations, dg, cache, From ccdd90e56b531fc403dabe0210eaa4da1a457534 Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Thu, 31 Aug 2023 11:05:31 +0200 Subject: [PATCH 090/128] Update examples/t8code_3d_dgsem/elixir_euler_free_stream.jl Co-authored-by: Andrew Winters --- examples/t8code_3d_dgsem/elixir_euler_free_stream.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl b/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl index 21d9845141..10d77cd7ff 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl @@ -50,7 +50,7 @@ isfile(mesh_file) || download("https://gist.githubusercontent.com/efaulhaber/d45 mesh_file) # INP mesh files are only support by p4est. Hence, we -# create a p4est connecvity object first from which +# create a p4est connectivity object first from which # we can create a t8code mesh. conn = Trixi.read_inp_p4est(mesh_file,Val(3)) From ed47329cec0bb0dc88ac4a5ce4bca6a1ded664d0 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Thu, 31 Aug 2023 11:08:36 +0200 Subject: [PATCH 091/128] Removed NDIMS from T8codeMesh construction in case of p4est/p8est connectivity input. --- .../elixir_advection_amr_unstructured_flag.jl | 6 +- .../elixir_advection_unstructured_flag.jl | 6 +- .../elixir_euler_free_stream.jl | 6 +- ...e_terms_nonconforming_unstructured_flag.jl | 6 +- examples/t8code_2d_dgsem/elixir_mhd_rotor.jl | 6 +- ...lixir_advection_amr_unstructured_curved.jl | 92 ++++++------ .../elixir_advection_unstructured_curved.jl | 77 +++++----- examples/t8code_3d_dgsem/elixir_euler_ec.jl | 6 +- .../elixir_euler_free_stream.jl | 136 ++++++++--------- .../elixir_euler_free_stream_extruded.jl | 6 +- ...terms_nonconforming_unstructured_curved.jl | 138 +++++++++--------- src/meshes/t8code_mesh.jl | 12 +- 12 files changed, 254 insertions(+), 243 deletions(-) diff --git a/examples/t8code_2d_dgsem/elixir_advection_amr_unstructured_flag.jl b/examples/t8code_2d_dgsem/elixir_advection_amr_unstructured_flag.jl index adf1d009a5..f285d24fc6 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_amr_unstructured_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_amr_unstructured_flag.jl @@ -41,9 +41,9 @@ isfile(mesh_file) || # we can create a t8code mesh. conn = Trixi.read_inp_p4est(mesh_file, Val(2)) -mesh = T8codeMesh{2}(conn, polydeg = 3, - mapping = mapping_flag, - initial_refinement_level = 1) +mesh = T8codeMesh(conn, polydeg = 3, + mapping = mapping_flag, + initial_refinement_level = 1) semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, boundary_conditions = boundary_conditions) diff --git a/examples/t8code_2d_dgsem/elixir_advection_unstructured_flag.jl b/examples/t8code_2d_dgsem/elixir_advection_unstructured_flag.jl index df9cbc26f6..5ba1ab1548 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_unstructured_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_unstructured_flag.jl @@ -38,9 +38,9 @@ isfile(mesh_file) || # we can create a t8code mesh. conn = Trixi.read_inp_p4est(mesh_file, Val(2)) -mesh = T8codeMesh{2}(conn, polydeg = 3, - mapping = mapping_flag, - initial_refinement_level = 2) +mesh = T8codeMesh(conn, polydeg = 3, + mapping = mapping_flag, + initial_refinement_level = 2) # A semidiscretization collects data structures and functions for the spatial discretization. semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, diff --git a/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl b/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl index 01e0449c67..46d6d8a7ed 100644 --- a/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl +++ b/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl @@ -40,9 +40,9 @@ isfile(mesh_file) || # we can create a t8code mesh. conn = Trixi.read_inp_p4est(mesh_file, Val(2)) -mesh = T8codeMesh{2}(conn, polydeg = 3, - mapping = mapping, - initial_refinement_level = 1) +mesh = T8codeMesh(conn, polydeg = 3, + mapping = mapping, + initial_refinement_level = 1) function adapt_callback(forest, forest_from, diff --git a/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl b/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl index 21f26d79ba..83da26c745 100644 --- a/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl @@ -40,9 +40,9 @@ isfile(mesh_file) || # we can create a t8code mesh. conn = Trixi.read_inp_p4est(mesh_file, Val(2)) -mesh = T8codeMesh{2}(conn, polydeg = 3, - mapping = mapping_flag, - initial_refinement_level = 1) +mesh = T8codeMesh(conn, polydeg = 3, + mapping = mapping_flag, + initial_refinement_level = 1) function adapt_callback(forest, forest_from, diff --git a/examples/t8code_2d_dgsem/elixir_mhd_rotor.jl b/examples/t8code_2d_dgsem/elixir_mhd_rotor.jl index 9a4bd99e44..adb154948f 100644 --- a/examples/t8code_2d_dgsem/elixir_mhd_rotor.jl +++ b/examples/t8code_2d_dgsem/elixir_mhd_rotor.jl @@ -78,9 +78,9 @@ isfile(mesh_file) || # we can create a t8code mesh. conn = Trixi.read_inp_p4est(mesh_file, Val(2)) -mesh = T8codeMesh{2}(conn, polydeg = 4, - mapping = mapping_twist, - initial_refinement_level = 1) +mesh = T8codeMesh(conn, polydeg = 4, + mapping = mapping_twist, + initial_refinement_level = 1) boundary_condition = BoundaryConditionDirichlet(initial_condition) boundary_conditions = Dict(:all => boundary_condition) diff --git a/examples/t8code_3d_dgsem/elixir_advection_amr_unstructured_curved.jl b/examples/t8code_3d_dgsem/elixir_advection_amr_unstructured_curved.jl index 63745fbeff..25ad3e3262 100644 --- a/examples/t8code_3d_dgsem/elixir_advection_amr_unstructured_curved.jl +++ b/examples/t8code_3d_dgsem/elixir_advection_amr_unstructured_curved.jl @@ -11,57 +11,59 @@ equations = LinearScalarAdvectionEquation3D(advection_velocity) # Solver with polydeg=4 to ensure free stream preservation (FSP) on non-conforming meshes. # The polydeg of the solver must be at least twice as big as the polydeg of the mesh. # See https://doi.org/10.1007/s10915-018-00897-9, Section 6. -solver = DGSEM(polydeg=4, surface_flux=flux_lax_friedrichs) +solver = DGSEM(polydeg = 4, surface_flux = flux_lax_friedrichs) initial_condition = initial_condition_gauss boundary_condition = BoundaryConditionDirichlet(initial_condition) -boundary_conditions = Dict( - :all => boundary_condition -) +boundary_conditions = Dict(:all => boundary_condition) # Mapping as described in https://arxiv.org/abs/2012.12040 but with less warping. # The mapping will be interpolated at tree level, and then refined without changing # the geometry interpolant. The original mapping applied to this unstructured mesh # causes some Jacobians to be negative, which makes the mesh invalid. function mapping(xi, eta, zeta) - # Don't transform input variables between -1 and 1 onto [0,3] to obtain curved boundaries - # xi = 1.5 * xi_ + 1.5 - # eta = 1.5 * eta_ + 1.5 - # zeta = 1.5 * zeta_ + 1.5 - - y = eta + 1/4 * (cos(1.5 * pi * (2 * xi - 3)/3) * - cos(0.5 * pi * (2 * eta - 3)/3) * - cos(0.5 * pi * (2 * zeta - 3)/3)) - - x = xi + 1/4 * (cos(0.5 * pi * (2 * xi - 3)/3) * - cos(2 * pi * (2 * y - 3)/3) * - cos(0.5 * pi * (2 * zeta - 3)/3)) - - z = zeta + 1/4 * (cos(0.5 * pi * (2 * x - 3)/3) * - cos(pi * (2 * y - 3)/3) * - cos(0.5 * pi * (2 * zeta - 3)/3)) - - # Transform the weird deformed cube to be approximately the size of [-5,5]^3 to match IC - return SVector(5 * x, 5 * y, 5 * z) + # Don't transform input variables between -1 and 1 onto [0,3] to obtain curved boundaries + # xi = 1.5 * xi_ + 1.5 + # eta = 1.5 * eta_ + 1.5 + # zeta = 1.5 * zeta_ + 1.5 + + y = eta + + 1 / 4 * (cos(1.5 * pi * (2 * xi - 3) / 3) * + cos(0.5 * pi * (2 * eta - 3) / 3) * + cos(0.5 * pi * (2 * zeta - 3) / 3)) + + x = xi + + 1 / 4 * (cos(0.5 * pi * (2 * xi - 3) / 3) * + cos(2 * pi * (2 * y - 3) / 3) * + cos(0.5 * pi * (2 * zeta - 3) / 3)) + + z = zeta + + 1 / 4 * (cos(0.5 * pi * (2 * x - 3) / 3) * + cos(pi * (2 * y - 3) / 3) * + cos(0.5 * pi * (2 * zeta - 3) / 3)) + + # Transform the weird deformed cube to be approximately the size of [-5,5]^3 to match IC + return SVector(5 * x, 5 * y, 5 * z) end # Unstructured mesh with 48 cells of the cube domain [-1, 1]^3 mesh_file = joinpath(@__DIR__, "cube_unstructured_2.inp") -isfile(mesh_file) || download("https://gist.githubusercontent.com/efaulhaber/b8df0033798e4926dec515fc045e8c2c/raw/b9254cde1d1fb64b6acc8416bc5ccdd77a240227/cube_unstructured_2.inp", - mesh_file) +isfile(mesh_file) || + download("https://gist.githubusercontent.com/efaulhaber/b8df0033798e4926dec515fc045e8c2c/raw/b9254cde1d1fb64b6acc8416bc5ccdd77a240227/cube_unstructured_2.inp", + mesh_file) # INP mesh files are only support by p4est. Hence, we # create a p4est connecvity object first from which # we can create a t8code mesh. -conn = Trixi.read_inp_p4est(mesh_file,Val(3)) - -mesh = T8codeMesh{3}(conn, polydeg=2, - mapping=mapping, - initial_refinement_level=1) +conn = Trixi.read_inp_p4est(mesh_file, Val(3)) -semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, boundary_conditions=boundary_conditions) +mesh = T8codeMesh(conn, polydeg = 2, + mapping = mapping, + initial_refinement_level = 1) +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, + boundary_conditions = boundary_conditions) ############################################################################### # ODE solvers, callbacks etc. @@ -72,21 +74,21 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() analysis_interval = 100 -analysis_callback = AnalysisCallback(semi, interval=analysis_interval, - extra_analysis_integrals=(entropy,)) +analysis_callback = AnalysisCallback(semi, interval = analysis_interval, + extra_analysis_integrals = (entropy,)) -alive_callback = AliveCallback(analysis_interval=analysis_interval) +alive_callback = AliveCallback(analysis_interval = analysis_interval) -amr_controller = ControllerThreeLevel(semi, IndicatorMax(semi, variable=first), - base_level=1, - med_level=2, med_threshold=0.1, - max_level=3, max_threshold=0.6) +amr_controller = ControllerThreeLevel(semi, IndicatorMax(semi, variable = first), + base_level = 1, + med_level = 2, med_threshold = 0.1, + max_level = 3, max_threshold = 0.6) amr_callback = AMRCallback(semi, amr_controller, - interval=5, - adapt_initial_condition=true, - adapt_initial_condition_only_refine=true) + interval = 5, + adapt_initial_condition = true, + adapt_initial_condition_only_refine = true) -stepsize_callback = StepsizeCallback(cfl=1.2) +stepsize_callback = StepsizeCallback(cfl = 1.2) callbacks = CallbackSet(summary_callback, analysis_callback, @@ -97,7 +99,7 @@ callbacks = CallbackSet(summary_callback, ############################################################################### # run the simulation -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep = false, callback = callbacks); summary_callback() # print the timer summary diff --git a/examples/t8code_3d_dgsem/elixir_advection_unstructured_curved.jl b/examples/t8code_3d_dgsem/elixir_advection_unstructured_curved.jl index 2c1162d636..5470444c69 100644 --- a/examples/t8code_3d_dgsem/elixir_advection_unstructured_curved.jl +++ b/examples/t8code_3d_dgsem/elixir_advection_unstructured_curved.jl @@ -9,56 +9,59 @@ advection_velocity = (0.2, -0.7, 0.5) equations = LinearScalarAdvectionEquation3D(advection_velocity) # Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux -solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) +solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs) initial_condition = initial_condition_convergence_test boundary_condition = BoundaryConditionDirichlet(initial_condition) -boundary_conditions = Dict( - :all => boundary_condition -) +boundary_conditions = Dict(:all => boundary_condition) # Mapping as described in https://arxiv.org/abs/2012.12040 but with less warping. # The mapping will be interpolated at tree level, and then refined without changing # the geometry interpolant. The original mapping applied to this unstructured mesh # causes some Jacobians to be negative, which makes the mesh invalid. function mapping(xi, eta, zeta) - # Don't transform input variables between -1 and 1 onto [0,3] to obtain curved boundaries - # xi = 1.5 * xi_ + 1.5 - # eta = 1.5 * eta_ + 1.5 - # zeta = 1.5 * zeta_ + 1.5 - - y = eta + 1/6 * (cos(1.5 * pi * (2 * xi - 3)/3) * - cos(0.5 * pi * (2 * eta - 3)/3) * - cos(0.5 * pi * (2 * zeta - 3)/3)) - - x = xi + 1/6 * (cos(0.5 * pi * (2 * xi - 3)/3) * - cos(2 * pi * (2 * y - 3)/3) * - cos(0.5 * pi * (2 * zeta - 3)/3)) - - z = zeta + 1/6 * (cos(0.5 * pi * (2 * x - 3)/3) * - cos(pi * (2 * y - 3)/3) * - cos(0.5 * pi * (2 * zeta - 3)/3)) - - return SVector(x, y, z) + # Don't transform input variables between -1 and 1 onto [0,3] to obtain curved boundaries + # xi = 1.5 * xi_ + 1.5 + # eta = 1.5 * eta_ + 1.5 + # zeta = 1.5 * zeta_ + 1.5 + + y = eta + + 1 / 6 * (cos(1.5 * pi * (2 * xi - 3) / 3) * + cos(0.5 * pi * (2 * eta - 3) / 3) * + cos(0.5 * pi * (2 * zeta - 3) / 3)) + + x = xi + + 1 / 6 * (cos(0.5 * pi * (2 * xi - 3) / 3) * + cos(2 * pi * (2 * y - 3) / 3) * + cos(0.5 * pi * (2 * zeta - 3) / 3)) + + z = zeta + + 1 / 6 * (cos(0.5 * pi * (2 * x - 3) / 3) * + cos(pi * (2 * y - 3) / 3) * + cos(0.5 * pi * (2 * zeta - 3) / 3)) + + return SVector(x, y, z) end # Unstructured mesh with 68 cells of the cube domain [-1, 1]^3 mesh_file = joinpath(@__DIR__, "cube_unstructured_1.inp") -isfile(mesh_file) || download("https://gist.githubusercontent.com/efaulhaber/d45c8ac1e248618885fa7cc31a50ab40/raw/37fba24890ab37cfa49c39eae98b44faf4502882/cube_unstructured_1.inp", - mesh_file) +isfile(mesh_file) || + download("https://gist.githubusercontent.com/efaulhaber/d45c8ac1e248618885fa7cc31a50ab40/raw/37fba24890ab37cfa49c39eae98b44faf4502882/cube_unstructured_1.inp", + mesh_file) # INP mesh files are only support by p4est. Hence, we # create a p4est connecvity object first from which # we can create a t8code mesh. -conn = Trixi.read_inp_p4est(mesh_file,Val(3)) +conn = Trixi.read_inp_p4est(mesh_file, Val(3)) -mesh = T8codeMesh{3}(conn, polydeg=3, - mapping=mapping, - initial_refinement_level=2) +mesh = T8codeMesh(conn, polydeg = 3, + mapping = mapping, + initial_refinement_level = 2) # A semidiscretization collects data structures and functions for the spatial discretization -semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, boundary_conditions=boundary_conditions) +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, + boundary_conditions = boundary_conditions) ############################################################################### # ODE solvers, callbacks etc. @@ -72,24 +75,24 @@ summary_callback = SummaryCallback() # The AnalysisCallback allows to analyse the solution in regular intervals and prints the results analysis_interval = 100 -analysis_callback = AnalysisCallback(semi, interval=analysis_interval) +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) -alive_callback = AliveCallback(analysis_interval=analysis_interval) +alive_callback = AliveCallback(analysis_interval = analysis_interval) # The StepsizeCallback handles the re-calculation of the maximum Δt after each time step -stepsize_callback = StepsizeCallback(cfl=1.2) +stepsize_callback = StepsizeCallback(cfl = 1.2) # Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver -callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, - stepsize_callback) +callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, + stepsize_callback) ############################################################################### # run the simulation # OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep = false, callback = callbacks); # Print the timer summary summary_callback() diff --git a/examples/t8code_3d_dgsem/elixir_euler_ec.jl b/examples/t8code_3d_dgsem/elixir_euler_ec.jl index 4389f97760..9c616fac2b 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_ec.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_ec.jl @@ -55,9 +55,9 @@ isfile(mesh_file) || # we can create a t8code mesh. conn = Trixi.read_inp_p4est(mesh_file, Val(3)) -mesh = T8codeMesh{3}(conn, polydeg = 5, - mapping = mapping, - initial_refinement_level = 0) +mesh = T8codeMesh(conn, polydeg = 5, + mapping = mapping, + initial_refinement_level = 0) # Create the semidiscretization object. semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, diff --git a/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl b/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl index 21d9845141..f6b9ddf577 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl @@ -9,63 +9,65 @@ equations = CompressibleEulerEquations3D(1.4) initial_condition = initial_condition_constant -boundary_conditions = Dict( - :all => BoundaryConditionDirichlet(initial_condition) -) +boundary_conditions = Dict(:all => BoundaryConditionDirichlet(initial_condition)) # Solver with polydeg=4 to ensure free stream preservation (FSP) on non-conforming meshes. # The polydeg of the solver must be at least twice as big as the polydeg of the mesh. # See https://doi.org/10.1007/s10915-018-00897-9, Section 6. -solver = DGSEM(polydeg=4, surface_flux=flux_lax_friedrichs, - volume_integral=VolumeIntegralWeakForm()) +solver = DGSEM(polydeg = 4, surface_flux = flux_lax_friedrichs, + volume_integral = VolumeIntegralWeakForm()) # Mapping as described in https://arxiv.org/abs/2012.12040 but with less warping. # The mapping will be interpolated at tree level, and then refined without changing # the geometry interpolant. This can yield problematic geometries if the unrefined mesh # is not fine enough. function mapping(xi_, eta_, zeta_) - # Transform input variables between -1 and 1 onto [0,3] - xi = 1.5 * xi_ + 1.5 - eta = 1.5 * eta_ + 1.5 - zeta = 1.5 * zeta_ + 1.5 - - y = eta + 1/6 * (cos(1.5 * pi * (2 * xi - 3)/3) * - cos(0.5 * pi * (2 * eta - 3)/3) * - cos(0.5 * pi * (2 * zeta - 3)/3)) - - x = xi + 1/6 * (cos(0.5 * pi * (2 * xi - 3)/3) * - cos(2 * pi * (2 * y - 3)/3) * - cos(0.5 * pi * (2 * zeta - 3)/3)) - - z = zeta + 1/6 * (cos(0.5 * pi * (2 * x - 3)/3) * - cos(pi * (2 * y - 3)/3) * - cos(0.5 * pi * (2 * zeta - 3)/3)) - - return SVector(x, y, z) + # Transform input variables between -1 and 1 onto [0,3] + xi = 1.5 * xi_ + 1.5 + eta = 1.5 * eta_ + 1.5 + zeta = 1.5 * zeta_ + 1.5 + + y = eta + + 1 / 6 * (cos(1.5 * pi * (2 * xi - 3) / 3) * + cos(0.5 * pi * (2 * eta - 3) / 3) * + cos(0.5 * pi * (2 * zeta - 3) / 3)) + + x = xi + + 1 / 6 * (cos(0.5 * pi * (2 * xi - 3) / 3) * + cos(2 * pi * (2 * y - 3) / 3) * + cos(0.5 * pi * (2 * zeta - 3) / 3)) + + z = zeta + + 1 / 6 * (cos(0.5 * pi * (2 * x - 3) / 3) * + cos(pi * (2 * y - 3) / 3) * + cos(0.5 * pi * (2 * zeta - 3) / 3)) + + return SVector(x, y, z) end # Unstructured mesh with 68 cells of the cube domain [-1, 1]^3 mesh_file = joinpath(@__DIR__, "cube_unstructured_1.inp") -isfile(mesh_file) || download("https://gist.githubusercontent.com/efaulhaber/d45c8ac1e248618885fa7cc31a50ab40/raw/37fba24890ab37cfa49c39eae98b44faf4502882/cube_unstructured_1.inp", - mesh_file) +isfile(mesh_file) || + download("https://gist.githubusercontent.com/efaulhaber/d45c8ac1e248618885fa7cc31a50ab40/raw/37fba24890ab37cfa49c39eae98b44faf4502882/cube_unstructured_1.inp", + mesh_file) # INP mesh files are only support by p4est. Hence, we # create a p4est connecvity object first from which # we can create a t8code mesh. -conn = Trixi.read_inp_p4est(mesh_file,Val(3)) +conn = Trixi.read_inp_p4est(mesh_file, Val(3)) -mesh = T8codeMesh{3}(conn, polydeg=2, - mapping=mapping, - initial_refinement_level=0) +mesh = T8codeMesh(conn, polydeg = 2, + mapping = mapping, + initial_refinement_level = 0) # Note: This is actually a `p8est_quadrant_t` which is much bigger than the # following struct. But we only need the first four fields for our purpose. struct t8_dhex_t - x :: Int32 - y :: Int32 - z :: Int32 - level :: Int8 - # [...] # See `p8est.h` in `p4est` for more info. + x::Int32 + y::Int32 + z::Int32 + level::Int8 + # [...] # See `p8est.h` in `p4est` for more info. end # Refine bottom left quadrant of each second tree to level 2 @@ -74,26 +76,26 @@ function adapt_callback(forest, which_tree, lelement_id, ts, - is_family, + is_family, num_elements, - elements_ptr) :: Cint - - vertex = Vector{Cdouble}(undef,3) - elements = unsafe_wrap(Array, elements_ptr, num_elements) - Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) - - el = unsafe_load(Ptr{t8_dhex_t}(elements[1])) - - if iseven(convert(Int, which_tree)) && el.x == 0 && el.y == 0 && el.z == 0 && el.level < 2 - # return true (refine) - return 1 - else - # return false (don't refine) - return 0 - end + elements_ptr)::Cint + vertex = Vector{Cdouble}(undef, 3) + elements = unsafe_wrap(Array, elements_ptr, num_elements) + Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) + + el = unsafe_load(Ptr{t8_dhex_t}(elements[1])) + + if iseven(convert(Int, which_tree)) && el.x == 0 && el.y == 0 && el.z == 0 && + el.level < 2 + # return true (refine) + return 1 + else + # return false (don't refine) + return 0 + end end -@assert(Trixi.t8_forest_is_committed(mesh.forest) != 0); +@assert(Trixi.t8_forest_is_committed(mesh.forest)!=0); # Init new forest. new_forest_ref = Ref{Trixi.t8_forest_t}() @@ -102,18 +104,22 @@ new_forest = new_forest_ref[] # Check out `examples/t8_step4_partition_balance_ghost.jl` in # https://github.com/DLR-AMR/T8code.jl for detailed explanations. -let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0, do_ghost = 1 - Trixi.t8_forest_set_user_data(new_forest, C_NULL) - Trixi.t8_forest_set_adapt(new_forest, mesh.forest, @Trixi.t8_adapt_callback(adapt_callback), recursive) - Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) - Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - Trixi.t8_forest_set_ghost(new_forest, do_ghost, Trixi.T8_GHOST_FACES); - Trixi.t8_forest_commit(new_forest) +let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0, + do_ghost = 1 + + Trixi.t8_forest_set_user_data(new_forest, C_NULL) + Trixi.t8_forest_set_adapt(new_forest, mesh.forest, + Trixi.@t8_adapt_callback(adapt_callback), recursive) + Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) + Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) + Trixi.t8_forest_set_ghost(new_forest, do_ghost, Trixi.T8_GHOST_FACES) + Trixi.t8_forest_commit(new_forest) end mesh.forest = new_forest -semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, boundary_conditions=boundary_conditions) +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, + boundary_conditions = boundary_conditions) ############################################################################### # ODE solvers, callbacks etc. @@ -124,11 +130,11 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() analysis_interval = 100 -analysis_callback = AnalysisCallback(semi, interval=analysis_interval) +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) -alive_callback = AliveCallback(analysis_interval=analysis_interval) +alive_callback = AliveCallback(analysis_interval = analysis_interval) -stepsize_callback = StepsizeCallback(cfl=1.2) +stepsize_callback = StepsizeCallback(cfl = 1.2) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, @@ -137,7 +143,7 @@ callbacks = CallbackSet(summary_callback, ############################################################################### # run the simulation -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep = false, callback = callbacks); summary_callback() # print the timer summary diff --git a/examples/t8code_3d_dgsem/elixir_euler_free_stream_extruded.jl b/examples/t8code_3d_dgsem/elixir_euler_free_stream_extruded.jl index d68b0bc226..f9d32b8087 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_free_stream_extruded.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_free_stream_extruded.jl @@ -45,9 +45,9 @@ isfile(mesh_file) || # we can create a t8code mesh. conn = Trixi.read_inp_p4est(mesh_file, Val(3)) -mesh = T8codeMesh{3}(conn, polydeg = 3, - mapping = mapping, - initial_refinement_level = 0) +mesh = T8codeMesh(conn, polydeg = 3, + mapping = mapping, + initial_refinement_level = 0) # Note: This is actually a `p8est_quadrant_t` which is much bigger than the # following struct. But we only need the first four fields for our purpose. diff --git a/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl b/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl index 71bf481297..fff91e92da 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl @@ -10,65 +10,67 @@ equations = CompressibleEulerEquations3D(1.4) initial_condition = initial_condition_convergence_test boundary_condition = BoundaryConditionDirichlet(initial_condition) -boundary_conditions = Dict( - :all => boundary_condition -) +boundary_conditions = Dict(:all => boundary_condition) # Solver with polydeg=4 to ensure free stream preservation (FSP) on non-conforming meshes. # The polydeg of the solver must be at least twice as big as the polydeg of the mesh. # See https://doi.org/10.1007/s10915-018-00897-9, Section 6. -solver = DGSEM(polydeg=4, surface_flux=flux_lax_friedrichs, - volume_integral=VolumeIntegralWeakForm()) +solver = DGSEM(polydeg = 4, surface_flux = flux_lax_friedrichs, + volume_integral = VolumeIntegralWeakForm()) # Mapping as described in https://arxiv.org/abs/2012.12040 but with less warping. # The mapping will be interpolated at tree level, and then refined without changing # the geometry interpolant. The original mapping applied to this unstructured mesh # causes some Jacobians to be negative, which makes the mesh invalid. function mapping(xi, eta, zeta) - # Don't transform input variables between -1 and 1 onto [0,3] to obtain curved boundaries - # xi = 1.5 * xi_ + 1.5 - # eta = 1.5 * eta_ + 1.5 - # zeta = 1.5 * zeta_ + 1.5 - - y = eta + 1/6 * (cos(1.5 * pi * (2 * xi - 3)/3) * - cos(0.5 * pi * (2 * eta - 3)/3) * - cos(0.5 * pi * (2 * zeta - 3)/3)) - - x = xi + 1/6 * (cos(0.5 * pi * (2 * xi - 3)/3) * - cos(2 * pi * (2 * y - 3)/3) * - cos(0.5 * pi * (2 * zeta - 3)/3)) - - z = zeta + 1/6 * (cos(0.5 * pi * (2 * x - 3)/3) * - cos(pi * (2 * y - 3)/3) * - cos(0.5 * pi * (2 * zeta - 3)/3)) - - # Transform the weird deformed cube to be approximately the cube [0,2]^3 - return SVector(x + 1, y + 1, z + 1) + # Don't transform input variables between -1 and 1 onto [0,3] to obtain curved boundaries + # xi = 1.5 * xi_ + 1.5 + # eta = 1.5 * eta_ + 1.5 + # zeta = 1.5 * zeta_ + 1.5 + + y = eta + + 1 / 6 * (cos(1.5 * pi * (2 * xi - 3) / 3) * + cos(0.5 * pi * (2 * eta - 3) / 3) * + cos(0.5 * pi * (2 * zeta - 3) / 3)) + + x = xi + + 1 / 6 * (cos(0.5 * pi * (2 * xi - 3) / 3) * + cos(2 * pi * (2 * y - 3) / 3) * + cos(0.5 * pi * (2 * zeta - 3) / 3)) + + z = zeta + + 1 / 6 * (cos(0.5 * pi * (2 * x - 3) / 3) * + cos(pi * (2 * y - 3) / 3) * + cos(0.5 * pi * (2 * zeta - 3) / 3)) + + # Transform the weird deformed cube to be approximately the cube [0,2]^3 + return SVector(x + 1, y + 1, z + 1) end # Unstructured mesh with 68 cells of the cube domain [-1, 1]^3 mesh_file = joinpath(@__DIR__, "cube_unstructured_1.inp") -isfile(mesh_file) || download("https://gist.githubusercontent.com/efaulhaber/d45c8ac1e248618885fa7cc31a50ab40/raw/37fba24890ab37cfa49c39eae98b44faf4502882/cube_unstructured_1.inp", - mesh_file) +isfile(mesh_file) || + download("https://gist.githubusercontent.com/efaulhaber/d45c8ac1e248618885fa7cc31a50ab40/raw/37fba24890ab37cfa49c39eae98b44faf4502882/cube_unstructured_1.inp", + mesh_file) # INP mesh files are only support by p4est. Hence, we # create a p4est connecvity object first from which # we can create a t8code mesh. -conn = Trixi.read_inp_p4est(mesh_file,Val(3)) +conn = Trixi.read_inp_p4est(mesh_file, Val(3)) # Mesh polydeg of 2 (half the solver polydeg) to ensure FSP (see above). -mesh = T8codeMesh{3}(conn, polydeg=2, - mapping=mapping, - initial_refinement_level=0) +mesh = T8codeMesh(conn, polydeg = 2, + mapping = mapping, + initial_refinement_level = 0) # Note: This is actually a `p8est_quadrant_t` which is much bigger than the # following struct. But we only need the first four fields for our purpose. struct t8_dhex_t - x :: Int32 - y :: Int32 - z :: Int32 - level :: Int8 - # [...] # See `p8est.h` in `p4est` for more info. + x::Int32 + y::Int32 + z::Int32 + level::Int8 + # [...] # See `p8est.h` in `p4est` for more info. end function adapt_callback(forest, @@ -76,26 +78,25 @@ function adapt_callback(forest, which_tree, lelement_id, ts, - is_family, + is_family, num_elements, - elements_ptr) :: Cint - - vertex = Vector{Cdouble}(undef,3) - elements = unsafe_wrap(Array, elements_ptr, num_elements) - Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) - - el = unsafe_load(Ptr{t8_dhex_t}(elements[1])) - - if el.x == 0 && el.y == 0 && el.z == 0 && el.level < 2 - # return true (refine) - return 1 - else - # return false (don't refine) - return 0 - end + elements_ptr)::Cint + vertex = Vector{Cdouble}(undef, 3) + elements = unsafe_wrap(Array, elements_ptr, num_elements) + Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) + + el = unsafe_load(Ptr{t8_dhex_t}(elements[1])) + + if el.x == 0 && el.y == 0 && el.z == 0 && el.level < 2 + # return true (refine) + return 1 + else + # return false (don't refine) + return 0 + end end -@assert(Trixi.t8_forest_is_committed(mesh.forest) != 0); +@assert(Trixi.t8_forest_is_committed(mesh.forest)!=0); # Init new forest. new_forest_ref = Ref{Trixi.t8_forest_t}() @@ -104,20 +105,23 @@ new_forest = new_forest_ref[] # Check out `examples/t8_step4_partition_balance_ghost.jl` in # https://github.com/DLR-AMR/T8code.jl for detailed explanations. -let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0, do_ghost = 1 - Trixi.t8_forest_set_user_data(new_forest, C_NULL) - Trixi.t8_forest_set_adapt(new_forest, mesh.forest, @Trixi.t8_adapt_callback(adapt_callback), recursive) - Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) - Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - Trixi.t8_forest_set_ghost(new_forest, do_ghost, Trixi.T8_GHOST_FACES); - Trixi.t8_forest_commit(new_forest) +let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0, + do_ghost = 1 + + Trixi.t8_forest_set_user_data(new_forest, C_NULL) + Trixi.t8_forest_set_adapt(new_forest, mesh.forest, + Trixi.@t8_adapt_callback(adapt_callback), recursive) + Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) + Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) + Trixi.t8_forest_set_ghost(new_forest, do_ghost, Trixi.T8_GHOST_FACES) + Trixi.t8_forest_commit(new_forest) end mesh.forest = new_forest semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, - source_terms=source_terms_convergence_test, - boundary_conditions=boundary_conditions) + source_terms = source_terms_convergence_test, + boundary_conditions = boundary_conditions) ############################################################################### # ODE solvers, callbacks etc. @@ -128,11 +132,11 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() analysis_interval = 100 -analysis_callback = AnalysisCallback(semi, interval=analysis_interval) +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) -alive_callback = AliveCallback(analysis_interval=analysis_interval) +alive_callback = AliveCallback(analysis_interval = analysis_interval) -stepsize_callback = StepsizeCallback(cfl=0.6) +stepsize_callback = StepsizeCallback(cfl = 0.6) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, @@ -141,7 +145,7 @@ callbacks = CallbackSet(summary_callback, ############################################################################### # run the simulation -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep = false, callback = callbacks); summary_callback() # print the timer summary diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 3f4456627c..91cdd5ca06 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -360,12 +360,10 @@ conforming mesh from a `p4est_connectivity` data structure. - `RealT::Type`: the type that should be used for coordinates. - `initial_refinement_level::Integer`: refine the mesh uniformly to this level before the simulation starts. """ -function T8codeMesh{NDIMS}(conn::Ptr{p4est_connectivity}; kwargs...) where {NDIMS} - @assert NDIMS == 2 # Only support for NDIMS = 2. - +function T8codeMesh(conn::Ptr{p4est_connectivity}; kwargs...) cmesh = t8_cmesh_new_from_p4est(conn, mpi_comm(), 0) - return T8codeMesh{NDIMS}(cmesh; kwargs...) + return T8codeMesh{2}(cmesh; kwargs...) end """ @@ -388,12 +386,10 @@ conforming mesh from a `p4est_connectivity` data structure. - `RealT::Type`: the type that should be used for coordinates. - `initial_refinement_level::Integer`: refine the mesh uniformly to this level before the simulation starts. """ -function T8codeMesh{NDIMS}(conn::Ptr{p8est_connectivity}; kwargs...) where {NDIMS} - @assert NDIMS == 3 # Only support for NDIMS = 3. - +function T8codeMesh(conn::Ptr{p8est_connectivity}; kwargs...) cmesh = t8_cmesh_new_from_p8est(conn, mpi_comm(), 0) - return T8codeMesh{NDIMS}(cmesh; kwargs...) + return T8codeMesh{3}(cmesh; kwargs...) end """ From 30d81053d12c888042e9143977cb95b498446e51 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Thu, 31 Aug 2023 11:31:41 +0200 Subject: [PATCH 092/128] Aligned T8codeMesh constructur with other mesh constructors. --- ...ixir_advection_amr_solution_independent.jl | 4 +- .../t8code_2d_dgsem/elixir_advection_basic.jl | 4 +- .../t8code_2d_dgsem/elixir_euler_sedov.jl | 4 +- .../elixir_euler_shockcapturing_ec.jl | 4 +- .../elixir_eulergravity_convergence.jl | 43 +++++------ .../t8code_2d_dgsem/elixir_mhd_alfven_wave.jl | 30 ++++---- .../elixir_shallowwater_source_terms.jl | 27 +++---- .../t8code_3d_dgsem/elixir_advection_amr.jl | 40 +++++----- .../t8code_3d_dgsem/elixir_advection_basic.jl | 29 ++++--- .../elixir_advection_nonconforming.jl | 75 ++++++++++--------- .../t8code_3d_dgsem/elixir_euler_sedov.jl | 4 +- src/meshes/t8code_mesh.jl | 37 ++++++++- src/solvers/dgsem_structured/dg_3d.jl | 6 +- 13 files changed, 159 insertions(+), 148 deletions(-) diff --git a/examples/t8code_2d_dgsem/elixir_advection_amr_solution_independent.jl b/examples/t8code_2d_dgsem/elixir_advection_amr_solution_independent.jl index 653bab41e2..0589e76a6a 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_amr_solution_independent.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_amr_solution_independent.jl @@ -93,12 +93,10 @@ solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs) coordinates_min = (-5.0, -5.0) coordinates_max = (5.0, 5.0) -mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) - trees_per_dimension = (1, 1) mesh = T8codeMesh(trees_per_dimension, polydeg = 3, - mapping = mapping, + coordinates_min = coordinates_min, coordinates_max = coordinates_max, initial_refinement_level = 1) semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) diff --git a/examples/t8code_2d_dgsem/elixir_advection_basic.jl b/examples/t8code_2d_dgsem/elixir_advection_basic.jl index efc5122658..26ced0970f 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_basic.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_basic.jl @@ -16,12 +16,10 @@ solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs) coordinates_min = (-1.0, -1.0) # minimum coordinates (min(x), min(y)) coordinates_max = (1.0, 1.0) # maximum coordinates (max(x), max(y)) -mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) - trees_per_dimension = (8, 8) mesh = T8codeMesh(trees_per_dimension, polydeg = 3, - mapping = mapping, + coordinates_min = coordinates_min, coordinates_max = coordinates_max, initial_refinement_level = 1) # A semidiscretization collects data structures and functions for the spatial discretization diff --git a/examples/t8code_2d_dgsem/elixir_euler_sedov.jl b/examples/t8code_2d_dgsem/elixir_euler_sedov.jl index 965d794f8d..82770a4050 100644 --- a/examples/t8code_2d_dgsem/elixir_euler_sedov.jl +++ b/examples/t8code_2d_dgsem/elixir_euler_sedov.jl @@ -58,12 +58,10 @@ solver = DGSEM(polydeg = polydeg, surface_flux = surface_flux, coordinates_min = (-1.0, -1.0) coordinates_max = (1.0, 1.0) -mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) - trees_per_dimension = (4, 4) mesh = T8codeMesh(trees_per_dimension, polydeg = 4, - mapping = mapping, + coordinates_min = coordinates_min, coordinates_max = coordinates_max, initial_refinement_level = 2, periodicity = true) semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) diff --git a/examples/t8code_2d_dgsem/elixir_euler_shockcapturing_ec.jl b/examples/t8code_2d_dgsem/elixir_euler_shockcapturing_ec.jl index 55a9063a00..9ebbd1d28c 100644 --- a/examples/t8code_2d_dgsem/elixir_euler_shockcapturing_ec.jl +++ b/examples/t8code_2d_dgsem/elixir_euler_shockcapturing_ec.jl @@ -29,12 +29,10 @@ solver = DGSEM(polydeg = polydeg, surface_flux = surface_flux, coordinates_min = (-1.0, -1.0) coordinates_max = (1.0, 1.0) -mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) - trees_per_dimension = (4, 4) mesh = T8codeMesh(trees_per_dimension, polydeg = 4, - mapping = mapping, + coordinates_min = coordinates_min, coordinates_max = coordinates_max, initial_refinement_level = 2, periodicity = true) semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) diff --git a/examples/t8code_2d_dgsem/elixir_eulergravity_convergence.jl b/examples/t8code_2d_dgsem/elixir_eulergravity_convergence.jl index 32649eacff..98a9a5521a 100644 --- a/examples/t8code_2d_dgsem/elixir_eulergravity_convergence.jl +++ b/examples/t8code_2d_dgsem/elixir_eulergravity_convergence.jl @@ -16,15 +16,13 @@ coordinates_max = (2.0, 2.0) trees_per_dimension = (1, 1) -mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) - mesh = T8codeMesh(trees_per_dimension, polydeg = 1, - mapping = mapping, + coordinates_min = coordinates_min, coordinates_max = coordinates_max, initial_refinement_level = 2) -semi_euler = SemidiscretizationHyperbolic(mesh, equations_euler, initial_condition, solver_euler, - source_terms=source_terms_eoc_test_coupled_euler_gravity) - +semi_euler = SemidiscretizationHyperbolic(mesh, equations_euler, initial_condition, + solver_euler, + source_terms = source_terms_eoc_test_coupled_euler_gravity) ############################################################################### # semidiscretization of the hyperbolic diffusion equations @@ -32,24 +30,23 @@ equations_gravity = HyperbolicDiffusionEquations2D() solver_gravity = DGSEM(polydeg, flux_lax_friedrichs) -semi_gravity = SemidiscretizationHyperbolic(mesh, equations_gravity, initial_condition, solver_gravity, - source_terms=source_terms_harmonic) - +semi_gravity = SemidiscretizationHyperbolic(mesh, equations_gravity, initial_condition, + solver_gravity, + source_terms = source_terms_harmonic) ############################################################################### # combining both semidiscretizations for Euler + self-gravity -parameters = ParametersEulerGravity(background_density=2.0, # aka rho0 +parameters = ParametersEulerGravity(background_density = 2.0, # aka rho0 # rho0 is (ab)used to add a "+8π" term to the source terms # for the manufactured solution - gravitational_constant=1.0, # aka G - cfl=1.1, - resid_tol=1.0e-10, - n_iterations_max=1000, - timestep_gravity=timestep_gravity_erk52_3Sstar!) + gravitational_constant = 1.0, # aka G + cfl = 1.1, + resid_tol = 1.0e-10, + n_iterations_max = 1000, + timestep_gravity = timestep_gravity_erk52_3Sstar!) semi = SemidiscretizationEulerGravity(semi_euler, semi_gravity, parameters) - ############################################################################### # ODE solvers, callbacks etc. tspan = (0.0, 0.5) @@ -57,21 +54,21 @@ ode = semidiscretize(semi, tspan); summary_callback = SummaryCallback() -stepsize_callback = StepsizeCallback(cfl=0.8) +stepsize_callback = StepsizeCallback(cfl = 0.8) analysis_interval = 100 -alive_callback = AliveCallback(analysis_interval=analysis_interval) +alive_callback = AliveCallback(analysis_interval = analysis_interval) -analysis_callback = AnalysisCallback(semi_euler, interval=analysis_interval, - save_analysis=true) +analysis_callback = AnalysisCallback(semi_euler, interval = analysis_interval, + save_analysis = true) callbacks = CallbackSet(summary_callback, stepsize_callback, analysis_callback, alive_callback) ############################################################################### # run the simulation -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep = false, callback = callbacks); summary_callback() # print the timer summary println("Number of gravity subcycles: ", semi.gravity_counter.ncalls_since_readout) diff --git a/examples/t8code_2d_dgsem/elixir_mhd_alfven_wave.jl b/examples/t8code_2d_dgsem/elixir_mhd_alfven_wave.jl index 463f916fa2..e11d46bb53 100644 --- a/examples/t8code_2d_dgsem/elixir_mhd_alfven_wave.jl +++ b/examples/t8code_2d_dgsem/elixir_mhd_alfven_wave.jl @@ -4,26 +4,24 @@ using Trixi ############################################################################### # Semidiscretization of the compressible ideal GLM-MHD equations. -gamma = 5/3 +gamma = 5 / 3 equations = IdealGlmMhdEquations2D(gamma) initial_condition = initial_condition_convergence_test # Get the DG approximation space volume_flux = (flux_central, flux_nonconservative_powell) -solver = DGSEM(polydeg=4, surface_flux=(flux_hll, flux_nonconservative_powell), - volume_integral=VolumeIntegralFluxDifferencing(volume_flux)) +solver = DGSEM(polydeg = 4, surface_flux = (flux_hll, flux_nonconservative_powell), + volume_integral = VolumeIntegralFluxDifferencing(volume_flux)) -coordinates_min = (0.0 , 0.0 ) +coordinates_min = (0.0, 0.0) coordinates_max = (sqrt(2.0), sqrt(2.0)) -mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) - trees_per_dimension = (8, 8) -mesh = T8codeMesh(trees_per_dimension, polydeg=3, - mapping=mapping, - initial_refinement_level=0, periodicity=true) +mesh = T8codeMesh(trees_per_dimension, polydeg = 3, + coordinates_min = coordinates_min, coordinates_max = coordinates_max, + initial_refinement_level = 0, periodicity = true) semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) @@ -36,14 +34,14 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() analysis_interval = 100 -analysis_callback = AnalysisCallback(semi, interval=analysis_interval) +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) -alive_callback = AliveCallback(analysis_interval=analysis_interval) +alive_callback = AliveCallback(analysis_interval = analysis_interval) cfl = 0.9 -stepsize_callback = StepsizeCallback(cfl=cfl) +stepsize_callback = StepsizeCallback(cfl = cfl) -glm_speed_callback = GlmSpeedCallback(glm_scale=0.5, cfl=cfl) +glm_speed_callback = GlmSpeedCallback(glm_scale = 0.5, cfl = cfl) callbacks = CallbackSet(summary_callback, analysis_callback, @@ -54,7 +52,7 @@ callbacks = CallbackSet(summary_callback, ############################################################################### # run the simulation -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep = false, callback = callbacks); summary_callback() # print the timer summary diff --git a/examples/t8code_2d_dgsem/elixir_shallowwater_source_terms.jl b/examples/t8code_2d_dgsem/elixir_shallowwater_source_terms.jl index c19f440ebc..3610639d55 100644 --- a/examples/t8code_2d_dgsem/elixir_shallowwater_source_terms.jl +++ b/examples/t8code_2d_dgsem/elixir_shallowwater_source_terms.jl @@ -4,17 +4,17 @@ using Trixi ############################################################################### # Semidiscretization of the shallow water equations. -equations = ShallowWaterEquations2D(gravity_constant=9.81) +equations = ShallowWaterEquations2D(gravity_constant = 9.81) initial_condition = initial_condition_convergence_test # MMS EOC test - ############################################################################### # Get the DG approximation space volume_flux = (flux_wintermeyer_etal, flux_nonconservative_wintermeyer_etal) -solver = DGSEM(polydeg=3, surface_flux=(flux_lax_friedrichs, flux_nonconservative_fjordholm_etal), - volume_integral=VolumeIntegralFluxDifferencing(volume_flux)) +solver = DGSEM(polydeg = 3, + surface_flux = (flux_lax_friedrichs, flux_nonconservative_fjordholm_etal), + volume_integral = VolumeIntegralFluxDifferencing(volume_flux)) ############################################################################### # Get the P4estMesh and setup a periodic mesh @@ -22,18 +22,15 @@ solver = DGSEM(polydeg=3, surface_flux=(flux_lax_friedrichs, flux_nonconservativ coordinates_min = (0.0, 0.0) # minimum coordinates (min(x), min(y)) coordinates_max = (sqrt(2.0), sqrt(2.0)) # maximum coordinates (max(x), max(y)) -mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) - trees_per_dimension = (8, 8) -mesh = T8codeMesh(trees_per_dimension, polydeg=3, - mapping=mapping, - initial_refinement_level=1) +mesh = T8codeMesh(trees_per_dimension, polydeg = 3, + coordinates_min = coordinates_min, coordinates_max = coordinates_max, + initial_refinement_level = 1) # A semidiscretization collects data structures and functions for the spatial discretization semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, - source_terms=source_terms_convergence_test) - + source_terms = source_terms_convergence_test) ############################################################################### # ODE solvers, callbacks etc. @@ -45,9 +42,9 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() analysis_interval = 500 -analysis_callback = AnalysisCallback(semi, interval=analysis_interval) +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) -alive_callback = AliveCallback(analysis_interval=analysis_interval) +alive_callback = AliveCallback(analysis_interval = analysis_interval) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback) @@ -55,6 +52,6 @@ callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback) # run the simulation # use a Runge-Kutta method with automatic (error based) time step size control -sol = solve(ode, RDPK3SpFSAL49(); abstol=1.0e-8, reltol=1.0e-8, - ode_default_options()..., callback=callbacks); +sol = solve(ode, RDPK3SpFSAL49(); abstol = 1.0e-8, reltol = 1.0e-8, + ode_default_options()..., callback = callbacks); summary_callback() # print the timer summary diff --git a/examples/t8code_3d_dgsem/elixir_advection_amr.jl b/examples/t8code_3d_dgsem/elixir_advection_amr.jl index a7f7dfd7b2..5a4b2218d5 100644 --- a/examples/t8code_3d_dgsem/elixir_advection_amr.jl +++ b/examples/t8code_3d_dgsem/elixir_advection_amr.jl @@ -11,20 +11,18 @@ advection_velocity = (0.2, -0.7, 0.5) equations = LinearScalarAdvectionEquation3D(advection_velocity) initial_condition = initial_condition_gauss -solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) +solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs) coordinates_min = (-5.0, -5.0, -5.0) -coordinates_max = ( 5.0, 5.0, 5.0) +coordinates_max = (5.0, 5.0, 5.0) trees_per_dimension = (1, 1, 1) -mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) - # Note that it is not necessary to use mesh polydeg lower than the solver polydeg # on a Cartesian mesh. # See https://doi.org/10.1007/s10915-018-00897-9, Section 6. -mesh = T8codeMesh(trees_per_dimension, polydeg=1, - mapping=mapping, - initial_refinement_level=4) +mesh = T8codeMesh(trees_per_dimension, polydeg = 1, + coordinates_min = coordinates_min, coordinates_max = coordinates_max, + initial_refinement_level = 4) semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) @@ -37,21 +35,21 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() analysis_interval = 100 -analysis_callback = AnalysisCallback(semi, interval=analysis_interval, - extra_analysis_integrals=(entropy,)) +analysis_callback = AnalysisCallback(semi, interval = analysis_interval, + extra_analysis_integrals = (entropy,)) -alive_callback = AliveCallback(analysis_interval=analysis_interval) +alive_callback = AliveCallback(analysis_interval = analysis_interval) -amr_controller = ControllerThreeLevel(semi, IndicatorMax(semi, variable=first), - base_level=4, - med_level=5, med_threshold=0.1, - max_level=6, max_threshold=0.6) +amr_controller = ControllerThreeLevel(semi, IndicatorMax(semi, variable = first), + base_level = 4, + med_level = 5, med_threshold = 0.1, + max_level = 6, max_threshold = 0.6) amr_callback = AMRCallback(semi, amr_controller, - interval=5, - adapt_initial_condition=true, - adapt_initial_condition_only_refine=true) + interval = 5, + adapt_initial_condition = true, + adapt_initial_condition_only_refine = true) -stepsize_callback = StepsizeCallback(cfl=1.2) +stepsize_callback = StepsizeCallback(cfl = 1.2) callbacks = CallbackSet(summary_callback, analysis_callback, @@ -62,7 +60,7 @@ callbacks = CallbackSet(summary_callback, ############################################################################### # run the simulation -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep = false, callback = callbacks); summary_callback() # print the timer summary diff --git a/examples/t8code_3d_dgsem/elixir_advection_basic.jl b/examples/t8code_3d_dgsem/elixir_advection_basic.jl index ec1ef2353a..f49462035a 100644 --- a/examples/t8code_3d_dgsem/elixir_advection_basic.jl +++ b/examples/t8code_3d_dgsem/elixir_advection_basic.jl @@ -11,21 +11,20 @@ advection_velocity = (0.2, -0.7, 0.5) equations = LinearScalarAdvectionEquation3D(advection_velocity) # Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux -solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) +solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs) coordinates_min = (-1.0, -1.0, -1.0) # minimum coordinates (min(x), min(y), min(z)) -coordinates_max = ( 1.0, 1.0, 1.0) # maximum coordinates (max(x), max(y), max(z)) - -mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) +coordinates_max = (1.0, 1.0, 1.0) # maximum coordinates (max(x), max(y), max(z)) # Create P4estMesh with 8 x 8 x 8 elements (note `refinement_level=1`) trees_per_dimension = (4, 4, 4) -mesh = T8codeMesh(trees_per_dimension, polydeg=3, - mapping=mapping, - initial_refinement_level=1) +mesh = T8codeMesh(trees_per_dimension, polydeg = 3, + coordinates_min = coordinates_min, coordinates_max = coordinates_max, + initial_refinement_level = 1) # A semidiscretization collects data structures and functions for the spatial discretization -semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_convergence_test, solver) +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_convergence_test, + solver) ############################################################################### # ODE solvers, callbacks etc. @@ -39,22 +38,22 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() # The AnalysisCallback allows to analyse the solution in regular intervals and prints the results -analysis_callback = AnalysisCallback(semi, interval=100) +analysis_callback = AnalysisCallback(semi, interval = 100) # The StepsizeCallback handles the re-calculation of the maximum Δt after each time step -stepsize_callback = StepsizeCallback(cfl=1.2) +stepsize_callback = StepsizeCallback(cfl = 1.2) # Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver -callbacks = CallbackSet(summary_callback, analysis_callback, - stepsize_callback) +callbacks = CallbackSet(summary_callback, analysis_callback, + stepsize_callback) ############################################################################### # run the simulation # OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep = false, callback = callbacks); # Print the timer summary summary_callback() diff --git a/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl b/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl index 27c98f0caa..7a90f97b8f 100644 --- a/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl +++ b/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl @@ -8,45 +8,42 @@ advection_velocity = (0.2, -0.7, 0.5) equations = LinearScalarAdvectionEquation3D(advection_velocity) # Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux. -solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) +solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs) coordinates_min = (-1.0, -1.0, -1.0) # minimum coordinates (min(x), min(y), min(z)) -coordinates_max = ( 1.0, 1.0, 1.0) # maximum coordinates (max(x), max(y), max(z)) +coordinates_max = (1.0, 1.0, 1.0) # maximum coordinates (max(x), max(y), max(z)) trees_per_dimension = (1, 1, 1) -mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) - # Note that it is not necessary to use mesh polydeg lower than the solver polydeg # on a Cartesian mesh. # See https://doi.org/10.1007/s10915-018-00897-9, Section 6. -mesh = T8codeMesh(trees_per_dimension, polydeg=3, - mapping=mapping, - initial_refinement_level=2) +mesh = T8codeMesh(trees_per_dimension, polydeg = 3, + coordinates_min = coordinates_min, coordinates_max = coordinates_max, + initial_refinement_level = 2) function adapt_callback(forest, forest_from, which_tree, lelement_id, ts, - is_family, + is_family, num_elements, - elements_ptr) :: Cint - - vertex = Vector{Cdouble}(undef,3) - elements = unsafe_wrap(Array, elements_ptr, num_elements) - Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) - level = Trixi.t8_element_level(ts,elements[1]) - - if all(vertex .< 1e-8) && level < 3 - # return true (refine) - return 1 - else - # return false (don't refine) - return 0 - end + elements_ptr)::Cint + vertex = Vector{Cdouble}(undef, 3) + elements = unsafe_wrap(Array, elements_ptr, num_elements) + Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) + level = Trixi.t8_element_level(ts, elements[1]) + + if all(vertex .< 1e-8) && level < 3 + # return true (refine) + return 1 + else + # return false (don't refine) + return 0 + end end -@assert(Trixi.t8_forest_is_committed(mesh.forest) != 0); +@assert(Trixi.t8_forest_is_committed(mesh.forest)!=0); # Init new forest. new_forest_ref = Ref{Trixi.t8_forest_t}() @@ -55,19 +52,23 @@ new_forest = new_forest_ref[] # Check out `examples/t8_step4_partition_balance_ghost.jl` in # https://github.com/DLR-AMR/T8code.jl for detailed explanations. -let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0, do_ghost = 1 - Trixi.t8_forest_set_user_data(new_forest, C_NULL) - Trixi.t8_forest_set_adapt(new_forest, mesh.forest, @Trixi.t8_adapt_callback(adapt_callback), recursive) - Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) - Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - Trixi.t8_forest_set_ghost(new_forest, do_ghost, Trixi.T8_GHOST_FACES); - Trixi.t8_forest_commit(new_forest) +let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0, + do_ghost = 1 + + Trixi.t8_forest_set_user_data(new_forest, C_NULL) + Trixi.t8_forest_set_adapt(new_forest, mesh.forest, + Trixi.@t8_adapt_callback(adapt_callback), recursive) + Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) + Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) + Trixi.t8_forest_set_ghost(new_forest, do_ghost, Trixi.T8_GHOST_FACES) + Trixi.t8_forest_commit(new_forest) end mesh.forest = new_forest # A semidiscretization collects data structures and functions for the spatial discretization -semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_convergence_test, solver) +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_convergence_test, + solver) ############################################################################### # ODE solvers, callbacks etc. @@ -81,22 +82,22 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() # The AnalysisCallback allows to analyse the solution in regular intervals and prints the results -analysis_callback = AnalysisCallback(semi, interval=100) +analysis_callback = AnalysisCallback(semi, interval = 100) # The StepsizeCallback handles the re-calculation of the maximum Δt after each time step -stepsize_callback = StepsizeCallback(cfl=1.6) +stepsize_callback = StepsizeCallback(cfl = 1.6) # Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver callbacks = CallbackSet(summary_callback, analysis_callback, - stepsize_callback) + stepsize_callback) ############################################################################### # run the simulation # OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep = false, callback = callbacks); # Print the timer summary summary_callback() diff --git a/examples/t8code_3d_dgsem/elixir_euler_sedov.jl b/examples/t8code_3d_dgsem/elixir_euler_sedov.jl index 8aa91b8ddc..4c689610b0 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_sedov.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_sedov.jl @@ -59,12 +59,10 @@ solver = DGSEM(polydeg = polydeg, surface_flux = surface_flux, coordinates_min = (-1.0, -1.0, -1.0) coordinates_max = (1.0, 1.0, 1.0) -mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) - trees_per_dimension = (4, 4, 4) mesh = T8codeMesh(trees_per_dimension, polydeg = 4, initial_refinement_level = 0, - mapping = mapping, + coordinates_min = coordinates_min, coordinates_max = coordinates_max, periodicity = true) # create the semi discretization object diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index d32e8e9f91..9a8fcd5796 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -117,16 +117,47 @@ Non-periodic boundaries will be called ':x_neg', ':x_pos', ':y_neg', ':y_pos', ' - 'polydeg::Integer': polynomial degree used to store the geometry of the mesh. The mapping will be approximated by an interpolation polynomial of the specified degree for each tree. -- 'mapping': a function of 'NDIMS' variables to describe the mapping that transforms - the reference mesh ('[-1, 1]^n') to the physical domain. +- `mapping`: a function of `NDIMS` variables to describe the mapping that transforms + the reference mesh (`[-1, 1]^n`) to the physical domain. + Use only one of `mapping`, `faces` and `coordinates_min`/`coordinates_max`. +- `faces::NTuple{2*NDIMS}`: a tuple of `2 * NDIMS` functions that describe the faces of the domain. + Each function must take `NDIMS-1` arguments. + `faces[1]` describes the face onto which the face in negative x-direction + of the unit hypercube is mapped. The face in positive x-direction of + the unit hypercube will be mapped onto the face described by `faces[2]`. + `faces[3:4]` describe the faces in positive and negative y-direction respectively + (in 2D and 3D). + `faces[5:6]` describe the faces in positive and negative z-direction respectively (in 3D). + Use only one of `mapping`, `faces` and `coordinates_min`/`coordinates_max`. +- `coordinates_min`: vector or tuple of the coordinates of the corner in the negative direction of each dimension + to create a rectangular mesh. + Use only one of `mapping`, `faces` and `coordinates_min`/`coordinates_max`. +- `coordinates_max`: vector or tuple of the coordinates of the corner in the positive direction of each dimension + to create a rectangular mesh. + Use only one of `mapping`, `faces` and `coordinates_min`/`coordinates_max`. - 'RealT::Type': the type that should be used for coordinates. - 'initial_refinement_level::Integer': refine the mesh uniformly to this level before the simulation starts. - 'periodicity': either a 'Bool' deciding if all of the boundaries are periodic or an 'NTuple{NDIMS, Bool}' deciding for each dimension if the boundaries in this dimension are periodic. """ function T8codeMesh(trees_per_dimension; polydeg, - mapping = nothing, RealT = Float64, initial_refinement_level = 0, + mapping = nothing, faces = nothing, coordinates_min = nothing, + coordinates_max = nothing, + RealT = Float64, initial_refinement_level = 0, periodicity = true) + @assert ((coordinates_min === nothing)===(coordinates_max === nothing)) "Either both or none of coordinates_min and coordinates_max must be specified" + + @assert count(i -> i !== nothing, + (mapping, faces, coordinates_min))==1 "Exactly one of mapping, faces and coordinates_min/max must be specified" + + # Extract mapping + if faces !== nothing + validate_faces(faces) + mapping = transfinite_mapping(faces) + elseif coordinates_min !== nothing + mapping = coordinates2mapping(coordinates_min, coordinates_max) + end + NDIMS = length(trees_per_dimension) # Convert periodicity to a Tuple of a Bool for every dimension diff --git a/src/solvers/dgsem_structured/dg_3d.jl b/src/solvers/dgsem_structured/dg_3d.jl index a2942d26f0..dad369735f 100644 --- a/src/solvers/dgsem_structured/dg_3d.jl +++ b/src/solvers/dgsem_structured/dg_3d.jl @@ -270,8 +270,8 @@ end # [arXiv: 2008.12044v2](https://arxiv.org/pdf/2008.12044) @inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, fstar3_L, fstar3_R, u, - mesh::Union{StructuredMesh{3}, P4estMesh{3}, - T8codeMesh{3}} + mesh::Union{StructuredMesh{3}, P4estMesh{3}, + T8codeMesh{3}}, nonconservative_terms::False, equations, volume_flux_fv, dg::DGSEM, element, cache) @unpack contravariant_vectors = cache.elements @@ -366,7 +366,7 @@ end # # Calculate the finite volume fluxes inside curvilinear elements (**with non-conservative terms**). @inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, fstar3_L, fstar3_R, u, - mesh::Union{StructuredMesh{3}, P4estMesh{3}, + mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, nonconservative_terms::True, equations, volume_flux_fv, dg::DGSEM, element, cache) From 7e932bcd3b71ea9cd8cbe219189d76959eeddc24 Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Thu, 31 Aug 2023 11:55:26 +0200 Subject: [PATCH 093/128] Update examples/t8code_3d_dgsem/elixir_euler_sedov.jl Co-authored-by: Andrew Winters --- examples/t8code_3d_dgsem/elixir_euler_sedov.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/t8code_3d_dgsem/elixir_euler_sedov.jl b/examples/t8code_3d_dgsem/elixir_euler_sedov.jl index 8aa91b8ddc..e3a6f6a666 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_sedov.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_sedov.jl @@ -22,7 +22,7 @@ function initial_condition_medium_sedov_blast_wave(x, t, z_norm = x[3] - inicenter[3] r = sqrt(x_norm^2 + y_norm^2 + z_norm^2) - # Setup based on http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000 + # Setup based on https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114000000000000000 r0 = 0.21875 # = 3.5 * smallest dx (for domain length=4 and max-ref=6) E = 1.0 p0_inner = 3 * (equations.gamma - 1) * E / (4 * pi * r0^2) From 641765abca1010916451e0d1c9a16379bf5629c2 Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Thu, 31 Aug 2023 11:55:44 +0200 Subject: [PATCH 094/128] Update examples/t8code_3d_dgsem/elixir_euler_sedov.jl Co-authored-by: Andrew Winters --- examples/t8code_3d_dgsem/elixir_euler_sedov.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/t8code_3d_dgsem/elixir_euler_sedov.jl b/examples/t8code_3d_dgsem/elixir_euler_sedov.jl index e3a6f6a666..0768c1b466 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_sedov.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_sedov.jl @@ -10,7 +10,7 @@ equations = CompressibleEulerEquations3D(1.4) initial_condition_medium_sedov_blast_wave(x, t, equations::CompressibleEulerEquations3D) The Sedov blast wave setup based on Flash -- http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000 +- https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114000000000000000 with smaller strength of the initial discontinuity. """ function initial_condition_medium_sedov_blast_wave(x, t, From 7562f7f662fc8e18cda379a7ddb3e6ec9706d8d4 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Thu, 31 Aug 2023 12:16:40 +0200 Subject: [PATCH 095/128] Cleanup up. --- .../elixir_advection_nonconforming_flag.jl | 22 +++++++++++-------- .../elixir_advection_nonconforming.jl | 18 +++++++++++---- .../elixir_euler_free_stream.jl | 3 --- .../elixir_euler_free_stream_extruded.jl | 14 ++---------- ...terms_nonconforming_unstructured_curved.jl | 3 --- 5 files changed, 29 insertions(+), 31 deletions(-) diff --git a/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl b/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl index d52cb9a202..4aadc0c963 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl @@ -27,6 +27,16 @@ mesh = T8codeMesh(trees_per_dimension, polydeg = 3, mapping = mapping, initial_refinement_level = 1) +# Note: This is actually a `p4est_quadrant_t` which is much bigger than the +# following struct. But we only need the first three fields for our purpose. +struct t8_dhex_t + x::Int32 + y::Int32 + level::Int8 + # [...] # See `p4est.h` in `p4est` for more info. +end + +# Refine quadrants of each tree at lower left edge to level 4. function adapt_callback(forest, forest_from, which_tree, @@ -35,16 +45,10 @@ function adapt_callback(forest, is_family, num_elements, elements_ptr)::Cint - vertex = Vector{Cdouble}(undef, 3) - elements = unsafe_wrap(Array, elements_ptr, num_elements) + el = unsafe_load(Ptr{t8_dhex_t}(elements[1])) - Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) - - level = Trixi.t8_element_level(ts, elements[1]) - - # TODO: Make this condition more general. - if vertex[1] < 1e-8 && vertex[2] < 1e-8 && level < 4 + if el.x == 0 && el.y == 0 && el.level < 4 # return true (refine) return 1 else @@ -53,7 +57,7 @@ function adapt_callback(forest, end end -Trixi.@T8_ASSERT(Trixi.t8_forest_is_committed(mesh.forest)!=0); +@assert(Trixi.t8_forest_is_committed(mesh.forest)!=0); # Init new forest. new_forest_ref = Ref{Trixi.t8_forest_t}() diff --git a/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl b/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl index 7a90f97b8f..04f8467c6d 100644 --- a/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl +++ b/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl @@ -21,6 +21,17 @@ mesh = T8codeMesh(trees_per_dimension, polydeg = 3, coordinates_min = coordinates_min, coordinates_max = coordinates_max, initial_refinement_level = 2) +# Note: This is actually a `p8est_quadrant_t` which is much bigger than the +# following struct. But we only need the first four fields for our purpose. +struct t8_dhex_t + x::Int32 + y::Int32 + z::Int32 + level::Int8 + # [...] # See `p8est.h` in `p4est` for more info. +end + +# Refine bottom left quadrant of each second tree to level 2 function adapt_callback(forest, forest_from, which_tree, @@ -29,12 +40,11 @@ function adapt_callback(forest, is_family, num_elements, elements_ptr)::Cint - vertex = Vector{Cdouble}(undef, 3) elements = unsafe_wrap(Array, elements_ptr, num_elements) - Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) - level = Trixi.t8_element_level(ts, elements[1]) + el = unsafe_load(Ptr{t8_dhex_t}(elements[1])) - if all(vertex .< 1e-8) && level < 3 + if iseven(convert(Int, which_tree)) && el.x == 0 && el.y == 0 && el.z == 0 && + el.level < 3 # return true (refine) return 1 else diff --git a/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl b/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl index f4d97b9162..fba5d73af2 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl @@ -79,10 +79,7 @@ function adapt_callback(forest, is_family, num_elements, elements_ptr)::Cint - vertex = Vector{Cdouble}(undef, 3) elements = unsafe_wrap(Array, elements_ptr, num_elements) - Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) - el = unsafe_load(Ptr{t8_dhex_t}(elements[1])) if iseven(convert(Int, which_tree)) && el.x == 0 && el.y == 0 && el.z == 0 && diff --git a/examples/t8code_3d_dgsem/elixir_euler_free_stream_extruded.jl b/examples/t8code_3d_dgsem/elixir_euler_free_stream_extruded.jl index f9d32b8087..a357ec5ddc 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_free_stream_extruded.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_free_stream_extruded.jl @@ -68,10 +68,7 @@ function adapt_callback(forest, is_family, num_elements, elements_ptr)::Cint - vertex = Vector{Cdouble}(undef, 3) elements = unsafe_wrap(Array, elements_ptr, num_elements) - Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) - el = unsafe_load(Ptr{t8_dhex_t}(elements[1])) if convert(Int, which_tree) < 4 && el.x == 0 && el.y == 0 && el.level < 2 @@ -90,6 +87,8 @@ new_forest_ref = Ref{Trixi.t8_forest_t}() Trixi.t8_forest_init(new_forest_ref); new_forest = new_forest_ref[] +# Check out `examples/t8_step4_partition_balance_ghost.jl` in +# https://github.com/DLR-AMR/T8code.jl for detailed explanations. let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0, do_ghost = 1 @@ -120,19 +119,10 @@ analysis_callback = AnalysisCallback(semi, interval = analysis_interval) alive_callback = AliveCallback(analysis_interval = analysis_interval) -# save_restart = SaveRestartCallback(interval=100, -# save_final_restart=true) -# -# save_solution = SaveSolutionCallback(interval=100, -# save_initial_solution=true, -# save_final_solution=true, -# solution_variables=cons2prim) - stepsize_callback = StepsizeCallback(cfl = 1.2) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, - # save_restart, save_solution, stepsize_callback) ############################################################################### diff --git a/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl b/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl index fff91e92da..13ec6789d9 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl @@ -81,10 +81,7 @@ function adapt_callback(forest, is_family, num_elements, elements_ptr)::Cint - vertex = Vector{Cdouble}(undef, 3) elements = unsafe_wrap(Array, elements_ptr, num_elements) - Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) - el = unsafe_load(Ptr{t8_dhex_t}(elements[1])) if el.x == 0 && el.y == 0 && el.z == 0 && el.level < 2 From 80fe0e6a02fca8b0886040f0f7eca738d6826c7c Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Thu, 31 Aug 2023 13:07:07 +0200 Subject: [PATCH 096/128] Added @allocated test. --- test/test_t8code_2d.jl | 33 ++++++++++++++++++++------------- test/test_t8code_3d.jl | 8 ++++++++ 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/test/test_t8code_2d.jl b/test/test_t8code_2d.jl index a424c9df84..4f222dcb33 100644 --- a/test/test_t8code_2d.jl +++ b/test/test_t8code_2d.jl @@ -13,20 +13,19 @@ isdir(outdir) && rm(outdir, recursive = true) mkdir(outdir) @testset "T8codeMesh2D" begin - @trixi_testset "test save_mesh_file" begin - @test_throws Exception begin - # Save mesh file support will be added in the future. The following - # lines of code are here for satisfying code coverage. - - # Create dummy mesh. - mesh = T8codeMesh((1, 1), polydeg = 1, - mapping = Trixi.coordinates2mapping((-1.0, -1.0), ( 1.0, 1.0)), - initial_refinement_level = 1) - - # This call throws an error. - Trixi.save_mesh_file(mesh, "dummy") - end + @test_throws Exception begin + # Save mesh file support will be added in the future. The following + # lines of code are here for satisfying code coverage. + + # Create dummy mesh. + mesh = T8codeMesh((1, 1), polydeg = 1, + mapping = Trixi.coordinates2mapping((-1.0, -1.0), (1.0, 1.0)), + initial_refinement_level = 1) + + # This call throws an error. + Trixi.save_mesh_file(mesh, "dummy") + end end @trixi_testset "elixir_advection_basic.jl" begin @@ -41,6 +40,14 @@ mkdir(outdir) "elixir_advection_nonconforming_flag.jl"), l2=[3.198940059144588e-5], linf=[0.00030636069494005547]) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000 + end end @trixi_testset "elixir_advection_unstructured_flag.jl" begin diff --git a/test/test_t8code_3d.jl b/test/test_t8code_3d.jl index 01fda989aa..a45d34fc6b 100644 --- a/test/test_t8code_3d.jl +++ b/test/test_t8code_3d.jl @@ -31,6 +31,14 @@ mkdir(outdir) @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_nonconforming.jl"), l2=[0.00253595715323843], linf=[0.016486952252155795]) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000 + end end @trixi_testset "elixir_advection_amr.jl" begin From 1421434624a8ad9bd7878a4a576676027851ca34 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Thu, 31 Aug 2023 13:14:01 +0200 Subject: [PATCH 097/128] Fixed formatting. --- src/callbacks_step/analysis_dg3d.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/callbacks_step/analysis_dg3d.jl b/src/callbacks_step/analysis_dg3d.jl index 3dafe75fb1..bc835e8a6a 100644 --- a/src/callbacks_step/analysis_dg3d.jl +++ b/src/callbacks_step/analysis_dg3d.jl @@ -252,7 +252,7 @@ function integrate(func::Func, u, end function analyze(::typeof(entropy_timederivative), du, u, t, - mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, + mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}}, equations, dg::DG, cache) # Calculate ∫(∂S/∂u ⋅ ∂u/∂t)dΩ From 0026a724b27600c52cba023ecfc27ec15275cb72 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Mon, 18 Dec 2023 10:52:56 +0100 Subject: [PATCH 098/128] Applied formatter. --- .../elixir_euler_source_terms_nonperiodic.jl | 42 +++++++++---------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonperiodic.jl b/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonperiodic.jl index e75d92cbd9..7cb03bb312 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonperiodic.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonperiodic.jl @@ -9,17 +9,15 @@ equations = CompressibleEulerEquations3D(1.4) initial_condition = initial_condition_convergence_test boundary_condition = BoundaryConditionDirichlet(initial_condition) -boundary_conditions = Dict( - :x_neg => boundary_condition, - :x_pos => boundary_condition, - :y_neg => boundary_condition, - :y_pos => boundary_condition, - :z_neg => boundary_condition, - :z_pos => boundary_condition -) - -solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs, - volume_integral=VolumeIntegralWeakForm()) +boundary_conditions = Dict(:x_neg => boundary_condition, + :x_pos => boundary_condition, + :y_neg => boundary_condition, + :y_pos => boundary_condition, + :z_neg => boundary_condition, + :z_pos => boundary_condition) + +solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs, + volume_integral = VolumeIntegralWeakForm()) coordinates_min = (0.0, 0.0, 0.0) coordinates_max = (2.0, 2.0, 2.0) @@ -28,13 +26,13 @@ trees_per_dimension = (2, 2, 2) mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max) -mesh = T8codeMesh(trees_per_dimension, polydeg=1, - mapping=mapping, - periodicity=false, initial_refinement_level=1) +mesh = T8codeMesh(trees_per_dimension, polydeg = 1, + mapping = mapping, + periodicity = false, initial_refinement_level = 1) semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, - source_terms=source_terms_convergence_test, - boundary_conditions=boundary_conditions) + source_terms = source_terms_convergence_test, + boundary_conditions = boundary_conditions) ############################################################################### # ODE solvers, callbacks etc. @@ -45,11 +43,11 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() analysis_interval = 100 -analysis_callback = AnalysisCallback(semi, interval=analysis_interval) +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) -alive_callback = AliveCallback(analysis_interval=analysis_interval) +alive_callback = AliveCallback(analysis_interval = analysis_interval) -stepsize_callback = StepsizeCallback(cfl=0.6) +stepsize_callback = StepsizeCallback(cfl = 0.6) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, @@ -58,7 +56,7 @@ callbacks = CallbackSet(summary_callback, ############################################################################### # run the simulation -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep = false, callback = callbacks); summary_callback() # print the timer summary From ff56688bbaf88e88a87b3bf07c56183cba086288 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Thu, 11 Jan 2024 11:56:44 +0100 Subject: [PATCH 099/128] Code cleanup. --- src/auxiliary/t8code.jl | 6 +++--- src/solvers/dgsem_t8code/containers_2d.jl | 6 +++--- src/solvers/dgsem_t8code/containers_3d.jl | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index 99a8b8b6bb..83e3dd20d5 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -233,9 +233,9 @@ function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundari mortars.neighbor_ids[end, mortar_id] = current_index + 1 # Fill in the `mortars.neighbor_ids` array and reorder if necessary. - trixi_t8_init_mortar_neighbor_ids!(mortars, faces[2], faces[1], - orientation, neighbor_ielements, - mortar_id) + init_mortar_neighbor_ids!(mortars, faces[2], faces[1], + orientation, neighbor_ielements, + mortar_id) # Fill in the `mortars.node_indices` array. init_mortar_node_indices!(mortars, faces, orientation, mortar_id) diff --git a/src/solvers/dgsem_t8code/containers_2d.jl b/src/solvers/dgsem_t8code/containers_2d.jl index 79bb28d612..360c6a2384 100644 --- a/src/solvers/dgsem_t8code/containers_2d.jl +++ b/src/solvers/dgsem_t8code/containers_2d.jl @@ -60,9 +60,9 @@ function calc_node_coordinates!(node_coordinates, return node_coordinates end -function trixi_t8_init_mortar_neighbor_ids!(mortars::P4estMortarContainer{2}, my_face, - other_face, orientation, neighbor_ielements, - mortar_id) +function init_mortar_neighbor_ids!(mortars::P4estMortarContainer{2}, my_face, + other_face, orientation, neighbor_ielements, + mortar_id) if orientation == 0 mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[1] + 1 mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[2] + 1 diff --git a/src/solvers/dgsem_t8code/containers_3d.jl b/src/solvers/dgsem_t8code/containers_3d.jl index e2fe996313..b5d7648f46 100644 --- a/src/solvers/dgsem_t8code/containers_3d.jl +++ b/src/solvers/dgsem_t8code/containers_3d.jl @@ -69,9 +69,9 @@ function calc_node_coordinates!(node_coordinates, end # This routine was copied and adapted from `src/dgsem_p4est/containers_3d.jl`: `orientation_to_indices_p4est`. -function trixi_t8_init_mortar_neighbor_ids!(mortars::P4estMortarContainer{3}, my_face, - other_face, orientation, neighbor_ielements, - mortar_id) +function init_mortar_neighbor_ids!(mortars::P4estMortarContainer{3}, my_face, + other_face, orientation, neighbor_ielements, + mortar_id) # my_face and other_face are the face directions (zero-based) # of "my side" and "other side" respectively. # Face corner 0 of the face with the lower face direction connects to a corner of the other face. From 0bfc644cd6615ab6a5e80d3555d951a5b19a48d8 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Mon, 15 Jan 2024 10:45:41 +0100 Subject: [PATCH 100/128] Removed unused member variable. --- src/meshes/t8code_mesh.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 9a8fcd5796..c4e6ff748d 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -12,8 +12,6 @@ mutable struct T8codeMesh{NDIMS, RealT <: Real, IsParallel, NDIMSP2, NNODES} <: forest :: Ptr{t8_forest} # cpointer to forest is_parallel :: IsParallel - unsaved_changes::Bool - # This specifies the geometry interpolation for each tree. tree_node_coordinates::Array{RealT, NDIMSP2} # [dimension, i, j, k, tree] From f66d2845f7a2e97c9c9080af1c33fda77bd042bd Mon Sep 17 00:00:00 2001 From: Benedict <135045760+bgeihe@users.noreply.github.com> Date: Mon, 15 Jan 2024 11:54:02 +0100 Subject: [PATCH 101/128] Apply suggestions from code review Co-authored-by: Daniel Doehring --- src/meshes/t8code_mesh.jl | 2 +- test/test_t8code_3d.jl | 64 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 9a8fcd5796..3876a39774 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -214,8 +214,8 @@ function T8codeMesh(trees_per_dimension; polydeg, veptr = t8_cmesh_get_tree_vertices(cmesh, itree - 1) verts = unsafe_wrap(Array, veptr, (3, 1 << NDIMS)) + # Calculate node coordinates of reference mesh. if NDIMS == 2 - # Calculate node coordinates of reference mesh. cell_x_offset = (verts[1, 1] - 0.5 * (trees_per_dimension[1] - 1)) * dx[1] cell_y_offset = (verts[2, 1] - 0.5 * (trees_per_dimension[2] - 1)) * dx[2] diff --git a/test/test_t8code_3d.jl b/test/test_t8code_3d.jl index a45d34fc6b..1318b3777f 100644 --- a/test/test_t8code_3d.jl +++ b/test/test_t8code_3d.jl @@ -48,6 +48,14 @@ mkdir(outdir) linf=[0.0007889950196294793], coverage_override=(maxiters = 6, initial_refinement_level = 1, base_level = 1, med_level = 2, max_level = 3)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000 + end end @trixi_testset "elixir_advection_amr_unstructured_curved.jl" begin @@ -58,6 +66,14 @@ mkdir(outdir) tspan=(0.0, 1.0), coverage_override=(maxiters = 6, initial_refinement_level = 0, base_level = 0, med_level = 1, max_level = 2)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000 + end end @trixi_testset "elixir_euler_source_terms_nonconforming_unstructured_curved.jl" begin @@ -78,6 +94,14 @@ mkdir(outdir) 0.008526972236273522, ], tspan=(0.0, 0.01)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000 + end end @trixi_testset "elixir_euler_source_terms_nonperiodic.jl" begin @@ -98,6 +122,14 @@ mkdir(outdir) 0.01562861968368434, ], tspan=(0.0, 1.0)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000 + end end @trixi_testset "elixir_euler_free_stream.jl" begin @@ -117,6 +149,14 @@ mkdir(outdir) 9.412914891981927e-12, ], tspan=(0.0, 0.03)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000 + end end @trixi_testset "elixir_euler_free_stream_extruded.jl" begin @@ -136,6 +176,14 @@ mkdir(outdir) 9.592326932761353e-13, ], tspan=(0.0, 0.1)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000 + end end @trixi_testset "elixir_euler_ec.jl" begin @@ -156,6 +204,14 @@ mkdir(outdir) ], tspan=(0.0, 0.2), coverage_override=(polydeg = 3,)) # Prevent long compile time in CI + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000 + end end @trixi_testset "elixir_euler_sedov.jl" begin @@ -176,6 +232,14 @@ mkdir(outdir) ], tspan=(0.0, 0.3), coverage_override=(polydeg = 3,)) # Prevent long compile time in CI + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000 + end end end From edb0fba2362c2a2a074a6c55ebed025af68b9ac8 Mon Sep 17 00:00:00 2001 From: Benedict Geihe Date: Mon, 15 Jan 2024 11:57:01 +0100 Subject: [PATCH 102/128] suggestions from review --- src/meshes/t8code_mesh.jl | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 3876a39774..afaea65aec 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -159,6 +159,7 @@ function T8codeMesh(trees_per_dimension; polydeg, end NDIMS = length(trees_per_dimension) + @assert (NDIMS == 2 || NDIMS == 3) "NDIMS should be 2 or 3." # Convert periodicity to a Tuple of a Bool for every dimension if all(periodicity) @@ -238,8 +239,6 @@ function T8codeMesh(trees_per_dimension; polydeg, cell_z_offset + dx[3] * nodes[k] / 2) end - else - throw(ArgumentError("NDIMS should be 2 or 3.")) end if !periodicity[1] @@ -287,6 +286,8 @@ conforming mesh from a `t8_cmesh` data structure. function T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh}; mapping = nothing, polydeg = 1, RealT = Float64, initial_refinement_level = 0) where {NDIMS} + @assert (NDIMS == 2 || NDIMS == 3) "NDIMS should be 2 or 3." + scheme = t8_scheme_new_default_cxx() forest = t8_forest_new_uniform(cmesh, scheme, initial_refinement_level, 0, mpi_comm()) @@ -314,6 +315,7 @@ function T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh}; v = verts[:, 3] - verts[:, 1] w = [0.0, 0.0, 1.0] + # triple product gives volume of spanned parallelepiped vol = dot(cross(u, v), w) if vol < 0.0 @@ -358,8 +360,6 @@ function T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh}; data_in, tmp1) end - else - throw(ArgumentError("NDIMS should be 2 or 3.")) end map_node_coordinates!(tree_node_coordinates, mapping) @@ -372,9 +372,7 @@ function T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh}; end """ - T8codeMesh{NDIMS}(conn::Ptr{p4est_connectivity}, - mapping=nothing, polydeg=1, RealT=Float64, - initial_refinement_level=0) + T8codeMesh(conn::Ptr{p4est_connectivity}; kwargs...) Main mesh constructor for the `T8codeMesh` that imports an unstructured, conforming mesh from a `p4est_connectivity` data structure. @@ -398,9 +396,7 @@ function T8codeMesh(conn::Ptr{p4est_connectivity}; kwargs...) end """ - T8codeMesh{NDIMS}(conn::Ptr{p8est_connectivity}, - mapping=nothing, polydeg=1, RealT=Float64, - initial_refinement_level=0) + T8codeMesh(conn::Ptr{p8est_connectivity}; kwargs...) Main mesh constructor for the `T8codeMesh` that imports an unstructured, conforming mesh from a `p4est_connectivity` data structure. @@ -424,9 +420,7 @@ function T8codeMesh(conn::Ptr{p8est_connectivity}; kwargs...) end """ - T8codeMesh{NDIMS}(meshfile::String; - mapping=nothing, polydeg=1, RealT=Float64, - initial_refinement_level=0) + T8codeMesh{NDIMS}(meshfile::String; kwargs...) Main mesh constructor for the `T8codeMesh` that imports an unstructured, conforming mesh from a Gmsh mesh file (`.msh`). From 2374fe5512854ef351c63e153260f8174501caf6 Mon Sep 17 00:00:00 2001 From: Benedict Geihe Date: Mon, 15 Jan 2024 12:04:58 +0100 Subject: [PATCH 103/128] fix format (strange?) --- src/meshes/t8code_mesh.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index afaea65aec..4eb1ac0e8c 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -159,7 +159,7 @@ function T8codeMesh(trees_per_dimension; polydeg, end NDIMS = length(trees_per_dimension) - @assert (NDIMS == 2 || NDIMS == 3) "NDIMS should be 2 or 3." + @assert (NDIMS == 2||NDIMS == 3) "NDIMS should be 2 or 3." # Convert periodicity to a Tuple of a Bool for every dimension if all(periodicity) @@ -286,7 +286,7 @@ conforming mesh from a `t8_cmesh` data structure. function T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh}; mapping = nothing, polydeg = 1, RealT = Float64, initial_refinement_level = 0) where {NDIMS} - @assert (NDIMS == 2 || NDIMS == 3) "NDIMS should be 2 or 3." + @assert (NDIMS == 2||NDIMS == 3) "NDIMS should be 2 or 3." scheme = t8_scheme_new_default_cxx() forest = t8_forest_new_uniform(cmesh, scheme, initial_refinement_level, 0, mpi_comm()) From 8f8d2ad928e877c7837480df593375c2a1420514 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Mon, 15 Jan 2024 14:16:35 +0100 Subject: [PATCH 104/128] Added comments to help interpreting the source code. --- src/meshes/t8code_mesh.jl | 2 +- src/solvers/dgsem_t8code/containers_2d.jl | 3 +++ src/solvers/dgsem_t8code/containers_3d.jl | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 115ddb259f..446f1c95ba 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -195,7 +195,7 @@ function T8codeMesh(trees_per_dimension; polydeg, ntuple(_ -> length(nodes), NDIMS)..., prod(trees_per_dimension)) - # Get cell length in reference mesh: Omega_ref = [-1,1]^2. + # Get cell length in reference mesh: Omega_ref = [-1,1]^NDIMS. dx = [2 / n for n in trees_per_dimension] num_local_trees = t8_cmesh_get_num_local_trees(cmesh) diff --git a/src/solvers/dgsem_t8code/containers_2d.jl b/src/solvers/dgsem_t8code/containers_2d.jl index 360c6a2384..bf77826a34 100644 --- a/src/solvers/dgsem_t8code/containers_2d.jl +++ b/src/solvers/dgsem_t8code/containers_2d.jl @@ -31,6 +31,9 @@ function calc_node_coordinates!(node_coordinates, element = t8_forest_get_element_in_tree(mesh.forest, itree, ielement) element_level = t8_element_level(eclass_scheme, element) + # Note, `t8_quad_len` is encoded as an integer (Morton encoding) in + # relation to `t8_quad_root_len`. This line transforms the + # "integer" length to a float in relation to the unit interval [0,1]. element_length = t8_quad_len(element_level) / t8_quad_root_len element_coords = Array{Float64}(undef, 3) diff --git a/src/solvers/dgsem_t8code/containers_3d.jl b/src/solvers/dgsem_t8code/containers_3d.jl index b5d7648f46..70c1d2edd2 100644 --- a/src/solvers/dgsem_t8code/containers_3d.jl +++ b/src/solvers/dgsem_t8code/containers_3d.jl @@ -33,6 +33,9 @@ function calc_node_coordinates!(node_coordinates, element = t8_forest_get_element_in_tree(mesh.forest, itree, ielement) element_level = t8_element_level(eclass_scheme, element) + # Note, `t8_hex_len` is encoded as an integer (Morton encoding) in + # relation to `t8_hex_root_len`. This line transforms the + # "integer" length to a float in relation to the unit interval [0,1]. element_length = t8_hex_len(element_level) / t8_hex_root_len element_coords = Vector{Cdouble}(undef, 3) From c9aad1bcfbe1c45137fc1aa7d5ba8e4eb3248408 Mon Sep 17 00:00:00 2001 From: Johannes Markert <10619309+jmark@users.noreply.github.com> Date: Mon, 15 Jan 2024 14:19:37 +0100 Subject: [PATCH 105/128] Update src/callbacks_step/amr_dg3d.jl Co-authored-by: Benedict <135045760+bgeihe@users.noreply.github.com> --- src/callbacks_step/amr_dg3d.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/callbacks_step/amr_dg3d.jl b/src/callbacks_step/amr_dg3d.jl index de5030e2ad..392cbba9e2 100644 --- a/src/callbacks_step/amr_dg3d.jl +++ b/src/callbacks_step/amr_dg3d.jl @@ -339,7 +339,7 @@ function adapt!(u_ode::AbstractVector, adaptor, mesh::T8codeMesh{3}, equations, reinitialize_containers!(mesh, equations, dg, cache) resize!(u_ode, - nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) + nvariables(equations) * ndofs(mesh, dg, cache)) u = wrap_array(u_ode, mesh, equations, dg, cache) u_tmp1 = Array{eltype(u), 4}(undef, nvariables(equations), nnodes(dg), From 61160d9e1c2a46037802446d27d8a7890b915565 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Mon, 15 Jan 2024 17:20:01 +0100 Subject: [PATCH 106/128] Adhered to unified mesh constructor calling scheme. --- src/meshes/t8code_mesh.jl | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 446f1c95ba..e81ff7c6d5 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -281,10 +281,16 @@ conforming mesh from a `t8_cmesh` data structure. - `RealT::Type`: the type that should be used for coordinates. - `initial_refinement_level::Integer`: refine the mesh uniformly to this level before the simulation starts. """ -function T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh}; - mapping = nothing, polydeg = 1, RealT = Float64, - initial_refinement_level = 0) where {NDIMS} - @assert (NDIMS == 2||NDIMS == 3) "NDIMS should be 2 or 3." +function T8codeMesh(cmesh::Ptr{t8_cmesh}; + mapping = nothing, polydeg = 1, RealT = Float64, + initial_refinement_level = 0) + + @assert (t8_cmesh_get_num_trees(cmesh) > 0) "Given `cmesh` does not contain any trees." + + # Infer NDIMS from the geometry of the first tree. + NDIMS = Int(t8_geom_get_dimension(t8_cmesh_get_tree_geometry(cmesh, 0))) + + @assert (NDIMS == 2 || NDIMS == 3) "NDIMS should be 2 or 3." scheme = t8_scheme_new_default_cxx() forest = t8_forest_new_uniform(cmesh, scheme, initial_refinement_level, 0, mpi_comm()) @@ -313,10 +319,8 @@ function T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh}; v = verts[:, 3] - verts[:, 1] w = [0.0, 0.0, 1.0] - # triple product gives volume of spanned parallelepiped - vol = dot(cross(u, v), w) - - if vol < 0.0 + # Triple product gives signed volume of spanned parallelepiped. + if dot(cross(u, v), w) < 0.0 @warn "Discovered negative volumes in `cmesh`: vol = $vol" end @@ -390,7 +394,7 @@ conforming mesh from a `p4est_connectivity` data structure. function T8codeMesh(conn::Ptr{p4est_connectivity}; kwargs...) cmesh = t8_cmesh_new_from_p4est(conn, mpi_comm(), 0) - return T8codeMesh{2}(cmesh; kwargs...) + return T8codeMesh(cmesh; kwargs...) end """ @@ -414,7 +418,7 @@ conforming mesh from a `p4est_connectivity` data structure. function T8codeMesh(conn::Ptr{p8est_connectivity}; kwargs...) cmesh = t8_cmesh_new_from_p8est(conn, mpi_comm(), 0) - return T8codeMesh{3}(cmesh; kwargs...) + return T8codeMesh(cmesh; kwargs...) end """ @@ -435,16 +439,16 @@ mesh from a Gmsh mesh file (`.msh`). - `RealT::Type`: the type that should be used for coordinates. - `initial_refinement_level::Integer`: refine the mesh uniformly to this level before the simulation starts. """ -function T8codeMesh{NDIMS}(meshfile::String; kwargs...) where {NDIMS} +function T8codeMesh(meshfile::String, ndims; kwargs...) # Prevent `t8code` from crashing Julia if the file doesn't exist. @assert isfile(meshfile) meshfile_prefix, meshfile_suffix = splitext(meshfile) - cmesh = t8_cmesh_from_msh_file(meshfile_prefix, 0, mpi_comm(), NDIMS, 0, 0) + cmesh = t8_cmesh_from_msh_file(meshfile_prefix, 0, mpi_comm(), ndims, 0, 0) - return T8codeMesh{NDIMS}(cmesh; kwargs...) + return T8codeMesh(cmesh; kwargs...) end # TODO: Just a placeholder. Will be implemented later when MPI is supported. From 36e41a6ef174907111224e05d52277536a24e585 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Mon, 15 Jan 2024 17:23:35 +0100 Subject: [PATCH 107/128] Applied formatter. --- src/meshes/t8code_mesh.jl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index e81ff7c6d5..53fe7c53b0 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -284,13 +284,12 @@ conforming mesh from a `t8_cmesh` data structure. function T8codeMesh(cmesh::Ptr{t8_cmesh}; mapping = nothing, polydeg = 1, RealT = Float64, initial_refinement_level = 0) - - @assert (t8_cmesh_get_num_trees(cmesh) > 0) "Given `cmesh` does not contain any trees." + @assert (t8_cmesh_get_num_trees(cmesh)>0) "Given `cmesh` does not contain any trees." # Infer NDIMS from the geometry of the first tree. NDIMS = Int(t8_geom_get_dimension(t8_cmesh_get_tree_geometry(cmesh, 0))) - @assert (NDIMS == 2 || NDIMS == 3) "NDIMS should be 2 or 3." + @assert (NDIMS == 2||NDIMS == 3) "NDIMS should be 2 or 3." scheme = t8_scheme_new_default_cxx() forest = t8_forest_new_uniform(cmesh, scheme, initial_refinement_level, 0, mpi_comm()) From deb02bc4a5b5dbd3fab33dbf129870fcb6c1dfb3 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Mon, 15 Jan 2024 17:25:22 +0100 Subject: [PATCH 108/128] Switched to Float64 instead of Cdouble. --- src/solvers/dgsem_t8code/containers_3d.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solvers/dgsem_t8code/containers_3d.jl b/src/solvers/dgsem_t8code/containers_3d.jl index 70c1d2edd2..f2d54ff07d 100644 --- a/src/solvers/dgsem_t8code/containers_3d.jl +++ b/src/solvers/dgsem_t8code/containers_3d.jl @@ -38,7 +38,7 @@ function calc_node_coordinates!(node_coordinates, # "integer" length to a float in relation to the unit interval [0,1]. element_length = t8_hex_len(element_level) / t8_hex_root_len - element_coords = Vector{Cdouble}(undef, 3) + element_coords = Vector{Float64}(undef, 3) t8_element_vertex_reference_coords(eclass_scheme, element, 0, pointer(element_coords)) From 7ba7b9ec063a420b8a3206e4ba79430057fa7a20 Mon Sep 17 00:00:00 2001 From: Benedict <135045760+bgeihe@users.noreply.github.com> Date: Mon, 15 Jan 2024 22:05:39 +0100 Subject: [PATCH 109/128] Update src/meshes/t8code_mesh.jl Co-authored-by: Daniel Doehring --- src/meshes/t8code_mesh.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 53fe7c53b0..5c0af90f0c 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -171,16 +171,15 @@ function T8codeMesh(trees_per_dimension; polydeg, periodicity = Tuple(periodicity) end + do_partition = 0 if NDIMS == 2 conn = T8code.Libt8.p4est_connectivity_new_brick(trees_per_dimension..., periodicity...) - do_partition = 0 cmesh = t8_cmesh_new_from_p4est(conn, mpi_comm(), do_partition) T8code.Libt8.p4est_connectivity_destroy(conn) elseif NDIMS == 3 conn = T8code.Libt8.p8est_connectivity_new_brick(trees_per_dimension..., periodicity...) - do_partition = 0 cmesh = t8_cmesh_new_from_p8est(conn, mpi_comm(), do_partition) T8code.Libt8.p8est_connectivity_destroy(conn) end From d7bf2effacdaf4708c14677f19bf243c685feee9 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 16 Jan 2024 11:52:07 +0100 Subject: [PATCH 110/128] Refactored negative volume check. --- src/meshes/t8code_mesh.jl | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 53fe7c53b0..97da0422ba 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -314,13 +314,18 @@ function T8codeMesh(cmesh::Ptr{t8_cmesh}; veptr = t8_cmesh_get_tree_vertices(cmesh, itree) verts = unsafe_wrap(Array, veptr, (3, 1 << NDIMS)) - u = verts[:, 2] - verts[:, 1] - v = verts[:, 3] - verts[:, 1] - w = [0.0, 0.0, 1.0] - - # Triple product gives signed volume of spanned parallelepiped. - if dot(cross(u, v), w) < 0.0 - @warn "Discovered negative volumes in `cmesh`: vol = $vol" + # Check if tree's node ordering is right-handed or print a warning. + let z = zero(eltype(verts)) + u = verts[:, 2] - verts[:, 1] + v = verts[:, 3] - verts[:, 1] + w = [z, z, z] + + # Triple product gives signed volume of spanned parallelepiped. + vol = dot(cross(u, v), w) + + if vol < z + @warn "Discovered negative volumes in `cmesh`: vol = $vol" + end end # Tree vertices are stored in z-order. From 7cb90b80284640d07d65c3fada57f416b37522fa Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 16 Jan 2024 11:55:15 +0100 Subject: [PATCH 111/128] Applied formatter. --- src/meshes/t8code_mesh.jl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 20ab83859b..6adc8edd52 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -315,16 +315,16 @@ function T8codeMesh(cmesh::Ptr{t8_cmesh}; # Check if tree's node ordering is right-handed or print a warning. let z = zero(eltype(verts)) - u = verts[:, 2] - verts[:, 1] - v = verts[:, 3] - verts[:, 1] - w = [z, z, z] + u = verts[:, 2] - verts[:, 1] + v = verts[:, 3] - verts[:, 1] + w = [z, z, z] - # Triple product gives signed volume of spanned parallelepiped. - vol = dot(cross(u, v), w) + # Triple product gives signed volume of spanned parallelepiped. + vol = dot(cross(u, v), w) - if vol < z - @warn "Discovered negative volumes in `cmesh`: vol = $vol" - end + if vol < z + @warn "Discovered negative volumes in `cmesh`: vol = $vol" + end end # Tree vertices are stored in z-order. From ba442fff4570ecb671daa1d0e2e701762813b79a Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Tue, 16 Jan 2024 16:40:57 +0100 Subject: [PATCH 112/128] Fixed typo resp. bug. --- src/meshes/t8code_mesh.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 6adc8edd52..0c2b70652d 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -314,10 +314,10 @@ function T8codeMesh(cmesh::Ptr{t8_cmesh}; verts = unsafe_wrap(Array, veptr, (3, 1 << NDIMS)) # Check if tree's node ordering is right-handed or print a warning. - let z = zero(eltype(verts)) + let z = zero(eltype(verts)), o = one(eltype(verts)) u = verts[:, 2] - verts[:, 1] v = verts[:, 3] - verts[:, 1] - w = [z, z, z] + w = [z, z, o] # Triple product gives signed volume of spanned parallelepiped. vol = dot(cross(u, v), w) From 40624afea672efd506e484b4bc6b50082b3fab3d Mon Sep 17 00:00:00 2001 From: Benedict <135045760+bgeihe@users.noreply.github.com> Date: Wed, 17 Jan 2024 12:38:15 +0100 Subject: [PATCH 113/128] Apply suggestions from code review Co-authored-by: Hendrik Ranocha --- examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl | 2 +- examples/t8code_3d_dgsem/elixir_euler_free_stream.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl b/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl index 04f8467c6d..3c858bf8fb 100644 --- a/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl +++ b/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl @@ -53,7 +53,7 @@ function adapt_callback(forest, end end -@assert(Trixi.t8_forest_is_committed(mesh.forest)!=0); +@assert(Trixi.t8_forest_is_committed(mesh.forest)!=0) # Init new forest. new_forest_ref = Ref{Trixi.t8_forest_t}() diff --git a/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl b/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl index fba5d73af2..a8aa31472e 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl @@ -92,7 +92,7 @@ function adapt_callback(forest, end end -@assert(Trixi.t8_forest_is_committed(mesh.forest)!=0); +@assert(Trixi.t8_forest_is_committed(mesh.forest)!=0) # Init new forest. new_forest_ref = Ref{Trixi.t8_forest_t}() From 5c7cfbc28ae4edc1fd8a069fcb895cd9247bfb0c Mon Sep 17 00:00:00 2001 From: Benedict Geihe Date: Wed, 17 Jan 2024 12:42:39 +0100 Subject: [PATCH 114/128] add missing allocation checks --- test/test_t8code_3d.jl | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/test_t8code_3d.jl b/test/test_t8code_3d.jl index 1318b3777f..d959f18c38 100644 --- a/test/test_t8code_3d.jl +++ b/test/test_t8code_3d.jl @@ -18,6 +18,14 @@ mkdir(outdir) # Expected errors are exactly the same as with TreeMesh! l2=[0.00016263963870641478], linf=[0.0014537194925779984]) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000 + end end @trixi_testset "elixir_advection_unstructured_curved.jl" begin @@ -25,6 +33,14 @@ mkdir(outdir) "elixir_advection_unstructured_curved.jl"), l2=[0.0004750004258546538], linf=[0.026527551737137167]) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000 + end end @trixi_testset "elixir_advection_nonconforming.jl" begin From 30c6e2923fbc687edbac172b1913cbbfa99a0363 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Thu, 18 Jan 2024 16:37:41 +0100 Subject: [PATCH 115/128] Some refactoring. --- .../elixir_advection_nonconforming_flag.jl | 36 +------ .../elixir_euler_free_stream.jl | 37 +------ ...e_terms_nonconforming_unstructured_flag.jl | 37 +------ .../rectangle_with_negative_volumes.msh | 79 ++++++++++++++ .../elixir_advection_nonconforming.jl | 36 +------ .../elixir_euler_free_stream.jl | 36 +------ .../elixir_euler_free_stream_extruded.jl | 36 +------ ...terms_nonconforming_unstructured_curved.jl | 34 +----- src/Trixi.jl | 2 + src/auxiliary/t8code.jl | 5 + src/meshes/t8code_mesh.jl | 100 +++++++++++++++++- test/test_t8code_2d.jl | 24 +++++ test/test_t8code_3d.jl | 14 +++ 13 files changed, 252 insertions(+), 224 deletions(-) create mode 100644 examples/t8code_2d_dgsem/rectangle_with_negative_volumes.msh diff --git a/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl b/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl index 4aadc0c963..61f78bf683 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl @@ -29,7 +29,7 @@ mesh = T8codeMesh(trees_per_dimension, polydeg = 3, # Note: This is actually a `p4est_quadrant_t` which is much bigger than the # following struct. But we only need the first three fields for our purpose. -struct t8_dhex_t +struct t8_dquad_t x::Int32 y::Int32 level::Int8 @@ -37,16 +37,9 @@ struct t8_dhex_t end # Refine quadrants of each tree at lower left edge to level 4. -function adapt_callback(forest, - forest_from, - which_tree, - lelement_id, - ts, - is_family, - num_elements, - elements_ptr)::Cint - elements = unsafe_wrap(Array, elements_ptr, num_elements) - el = unsafe_load(Ptr{t8_dhex_t}(elements[1])) +function adapt_callback(forest, ltreeid, eclass_scheme, lelemntid, elements, is_family, + user_data) + el = unsafe_load(Ptr{t8_dquad_t}(elements[1])) if el.x == 0 && el.y == 0 && el.level < 4 # return true (refine) @@ -57,26 +50,7 @@ function adapt_callback(forest, end end -@assert(Trixi.t8_forest_is_committed(mesh.forest)!=0); - -# Init new forest. -new_forest_ref = Ref{Trixi.t8_forest_t}() -Trixi.t8_forest_init(new_forest_ref); -new_forest = new_forest_ref[] - -# Check out `examples/t8_step4_partition_balance_ghost.jl` in -# https://github.com/DLR-AMR/T8code.jl for detailed explanations. -let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0 - Trixi.t8_forest_set_user_data(new_forest, C_NULL) - Trixi.t8_forest_set_adapt(new_forest, mesh.forest, - Trixi.@t8_adapt_callback(adapt_callback), recursive) - Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) - Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - Trixi.t8_forest_set_ghost(new_forest, 1, Trixi.T8_GHOST_FACES) - Trixi.t8_forest_commit(new_forest) -end - -mesh.forest = new_forest +adapt!(mesh, adapt_callback) # A semidiscretization collects data structures and functions for the spatial discretization semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_convergence_test, diff --git a/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl b/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl index 46d6d8a7ed..5ed36d34a9 100644 --- a/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl +++ b/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl @@ -44,21 +44,13 @@ mesh = T8codeMesh(conn, polydeg = 3, mapping = mapping, initial_refinement_level = 1) -function adapt_callback(forest, - forest_from, - which_tree, - lelement_id, - ts, - is_family, - num_elements, - elements_ptr)::Cint +function adapt_callback(forest, ltreeid, eclass_scheme, lelemntid, elements, is_family, + user_data) vertex = Vector{Cdouble}(undef, 3) - elements = unsafe_wrap(Array, elements_ptr, num_elements) + Trixi.t8_element_vertex_reference_coords(eclass_scheme, elements[1], 0, vertex) - Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) - - level = Trixi.t8_element_level(ts, elements[1]) + level = Trixi.t8_element_level(eclass_scheme, elements[1]) # TODO: Make this condition more general. if vertex[1] < 1e-8 && vertex[2] < 1e-8 && level < 3 @@ -70,26 +62,7 @@ function adapt_callback(forest, end end -Trixi.@T8_ASSERT(Trixi.t8_forest_is_committed(mesh.forest)!=0); - -# Init new forest. -new_forest_ref = Ref{Trixi.t8_forest_t}() -Trixi.t8_forest_init(new_forest_ref); -new_forest = new_forest_ref[] - -# Check out `examples/t8_step4_partition_balance_ghost.jl` in -# https://github.com/DLR-AMR/T8code.jl for detailed explanations. -let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0 - Trixi.t8_forest_set_user_data(new_forest, C_NULL) - Trixi.t8_forest_set_adapt(new_forest, mesh.forest, - Trixi.@t8_adapt_callback(adapt_callback), recursive) - Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) - Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - Trixi.t8_forest_set_ghost(new_forest, 1, Trixi.T8_GHOST_FACES) - Trixi.t8_forest_commit(new_forest) -end - -mesh.forest = new_forest +adapt!(mesh, adapt_callback) semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, boundary_conditions = Dict(:all => BoundaryConditionDirichlet(initial_condition))) diff --git a/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl b/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl index 83da26c745..552b7d2500 100644 --- a/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl @@ -44,21 +44,13 @@ mesh = T8codeMesh(conn, polydeg = 3, mapping = mapping_flag, initial_refinement_level = 1) -function adapt_callback(forest, - forest_from, - which_tree, - lelement_id, - ts, - is_family, - num_elements, - elements_ptr)::Cint +function adapt_callback(forest, ltreeid, eclass_scheme, lelemntid, elements, is_family, + user_data) vertex = Vector{Cdouble}(undef, 3) - elements = unsafe_wrap(Array, elements_ptr, num_elements) + Trixi.t8_element_vertex_reference_coords(eclass_scheme, elements[1], 0, pointer(vertex)) - Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex)) - - level = Trixi.t8_element_level(ts, elements[1]) + level = Trixi.t8_element_level(eclass_scheme, elements[1]) # TODO: Make this condition more general. if vertex[1] < 1e-8 && vertex[2] < 1e-8 && level < 2 @@ -70,26 +62,7 @@ function adapt_callback(forest, end end -@assert(Trixi.t8_forest_is_committed(mesh.forest)!=0); - -# Init new forest. -new_forest_ref = Ref{Trixi.t8_forest_t}() -Trixi.t8_forest_init(new_forest_ref); -new_forest = new_forest_ref[] - -# Check out `examples/t8_step4_partition_balance_ghost.jl` in -# https://github.com/DLR-AMR/T8code.jl for detailed explanations. -let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0 - Trixi.t8_forest_set_user_data(new_forest, C_NULL) - Trixi.t8_forest_set_adapt(new_forest, mesh.forest, - Trixi.@t8_adapt_callback(adapt_callback), recursive) - Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) - Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - Trixi.t8_forest_set_ghost(new_forest, 1, Trixi.T8_GHOST_FACES) - Trixi.t8_forest_commit(new_forest) -end - -mesh.forest = new_forest +adapt!(mesh, adapt_callback) semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, source_terms = source_terms, diff --git a/examples/t8code_2d_dgsem/rectangle_with_negative_volumes.msh b/examples/t8code_2d_dgsem/rectangle_with_negative_volumes.msh new file mode 100644 index 0000000000..64fcdcd25d --- /dev/null +++ b/examples/t8code_2d_dgsem/rectangle_with_negative_volumes.msh @@ -0,0 +1,79 @@ +$MeshFormat +4.1 0 8 +$EndMeshFormat +$Entities +4 4 1 0 +1000 -3 -3 0 0 +1001 -3 3 0 0 +1002 3 3 0 0 +1003 3 -3 0 0 +101 -3 -3 0 -3 3 0 0 2 1000 -1001 +102 -3 3 0 3 3 0 0 2 1001 -1002 +103 3 -3 0 3 3 0 0 2 1002 -1003 +104 -3 -3 0 3 -3 0 0 2 1003 -1000 +1 -3 -3 0 3 3 0 0 4 101 102 103 104 +$EndEntities +$Nodes +9 11 1 11 +0 1000 0 1 +1 +-3 -3 0 +0 1001 0 1 +2 +-3 3 0 +0 1002 0 1 +3 +3 3 0 +0 1003 0 1 +4 +3 -3 0 +1 101 1 1 +5 +-3 -1.101696511796035e-11 0 0.4999999999981639 +1 102 1 1 +6 +-1.101696511796035e-11 3 0 0.4999999999981639 +1 103 1 1 +7 +3 1.101696511796035e-11 0 0.4999999999981639 +1 104 1 1 +8 +1.101696511796035e-11 -3 0 0.4999999999981639 +2 1 1 3 +9 +10 +11 +-0.0002444350403711321 0.005208961077741881 -0 -0.005208961077741881 -0.0002444350403711321 +0.8787876029934786 -0.8769758575617213 0 0.8769758575617213 0.8787876029934786 +-0.8731987035236274 0.8965716725861542 -0 -0.8965716725861542 -0.8731987035236274 +$EndNodes +$Elements +9 18 1 18 +0 1000 15 1 +1 1 +0 1001 15 1 +2 2 +0 1002 15 1 +3 3 +0 1003 15 1 +4 4 +1 101 1 2 +5 1 5 +6 5 2 +1 102 1 2 +7 2 6 +8 6 3 +1 103 1 2 +9 3 7 +10 7 4 +1 104 1 2 +11 4 8 +12 8 1 +2 1 3 6 +13 7 4 8 10 +14 6 11 5 2 +15 3 9 11 6 +16 1 9 10 8 +17 3 7 10 9 +18 1 5 11 9 +$EndElements diff --git a/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl b/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl index 3c858bf8fb..c1a49a8100 100644 --- a/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl +++ b/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl @@ -32,18 +32,11 @@ struct t8_dhex_t end # Refine bottom left quadrant of each second tree to level 2 -function adapt_callback(forest, - forest_from, - which_tree, - lelement_id, - ts, - is_family, - num_elements, - elements_ptr)::Cint - elements = unsafe_wrap(Array, elements_ptr, num_elements) +function adapt_callback(forest, ltreeid, eclass_scheme, lelemntid, elements, is_family, + user_data) el = unsafe_load(Ptr{t8_dhex_t}(elements[1])) - if iseven(convert(Int, which_tree)) && el.x == 0 && el.y == 0 && el.z == 0 && + if iseven(convert(Int, ltreeid)) && el.x == 0 && el.y == 0 && el.z == 0 && el.level < 3 # return true (refine) return 1 @@ -53,28 +46,7 @@ function adapt_callback(forest, end end -@assert(Trixi.t8_forest_is_committed(mesh.forest)!=0) - -# Init new forest. -new_forest_ref = Ref{Trixi.t8_forest_t}() -Trixi.t8_forest_init(new_forest_ref); -new_forest = new_forest_ref[] - -# Check out `examples/t8_step4_partition_balance_ghost.jl` in -# https://github.com/DLR-AMR/T8code.jl for detailed explanations. -let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0, - do_ghost = 1 - - Trixi.t8_forest_set_user_data(new_forest, C_NULL) - Trixi.t8_forest_set_adapt(new_forest, mesh.forest, - Trixi.@t8_adapt_callback(adapt_callback), recursive) - Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) - Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - Trixi.t8_forest_set_ghost(new_forest, do_ghost, Trixi.T8_GHOST_FACES) - Trixi.t8_forest_commit(new_forest) -end - -mesh.forest = new_forest +adapt!(mesh, adapt_callback) # A semidiscretization collects data structures and functions for the spatial discretization semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_convergence_test, diff --git a/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl b/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl index a8aa31472e..f4799b0b88 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl @@ -71,18 +71,11 @@ struct t8_dhex_t end # Refine bottom left quadrant of each second tree to level 2 -function adapt_callback(forest, - forest_from, - which_tree, - lelement_id, - ts, - is_family, - num_elements, - elements_ptr)::Cint - elements = unsafe_wrap(Array, elements_ptr, num_elements) +function adapt_callback(forest, ltreeid, eclass_scheme, lelemntid, elements, is_family, + user_data) el = unsafe_load(Ptr{t8_dhex_t}(elements[1])) - if iseven(convert(Int, which_tree)) && el.x == 0 && el.y == 0 && el.z == 0 && + if iseven(convert(Int, ltreeid)) && el.x == 0 && el.y == 0 && el.z == 0 && el.level < 2 # return true (refine) return 1 @@ -92,28 +85,7 @@ function adapt_callback(forest, end end -@assert(Trixi.t8_forest_is_committed(mesh.forest)!=0) - -# Init new forest. -new_forest_ref = Ref{Trixi.t8_forest_t}() -Trixi.t8_forest_init(new_forest_ref); -new_forest = new_forest_ref[] - -# Check out `examples/t8_step4_partition_balance_ghost.jl` in -# https://github.com/DLR-AMR/T8code.jl for detailed explanations. -let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0, - do_ghost = 1 - - Trixi.t8_forest_set_user_data(new_forest, C_NULL) - Trixi.t8_forest_set_adapt(new_forest, mesh.forest, - Trixi.@t8_adapt_callback(adapt_callback), recursive) - Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) - Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - Trixi.t8_forest_set_ghost(new_forest, do_ghost, Trixi.T8_GHOST_FACES) - Trixi.t8_forest_commit(new_forest) -end - -mesh.forest = new_forest +adapt!(mesh, adapt_callback) semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, boundary_conditions = boundary_conditions) diff --git a/examples/t8code_3d_dgsem/elixir_euler_free_stream_extruded.jl b/examples/t8code_3d_dgsem/elixir_euler_free_stream_extruded.jl index a357ec5ddc..a4b0b282c4 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_free_stream_extruded.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_free_stream_extruded.jl @@ -60,18 +60,11 @@ struct t8_dhex_t end # Refine quadrants in y-direction of each tree at one edge to level 2 -function adapt_callback(forest, - forest_from, - which_tree, - lelement_id, - ts, - is_family, - num_elements, - elements_ptr)::Cint - elements = unsafe_wrap(Array, elements_ptr, num_elements) +function adapt_callback(forest, ltreeid, eclass_scheme, lelemntid, elements, is_family, + user_data) el = unsafe_load(Ptr{t8_dhex_t}(elements[1])) - if convert(Int, which_tree) < 4 && el.x == 0 && el.y == 0 && el.level < 2 + if convert(Int, ltreeid) < 4 && el.x == 0 && el.y == 0 && el.level < 2 # return true (refine) return 1 else @@ -80,28 +73,7 @@ function adapt_callback(forest, end end -@assert(Trixi.t8_forest_is_committed(mesh.forest)!=0); - -# Init new forest. -new_forest_ref = Ref{Trixi.t8_forest_t}() -Trixi.t8_forest_init(new_forest_ref); -new_forest = new_forest_ref[] - -# Check out `examples/t8_step4_partition_balance_ghost.jl` in -# https://github.com/DLR-AMR/T8code.jl for detailed explanations. -let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0, - do_ghost = 1 - - Trixi.t8_forest_set_user_data(new_forest, C_NULL) - Trixi.t8_forest_set_adapt(new_forest, mesh.forest, - Trixi.@t8_adapt_callback(adapt_callback), recursive) - Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) - Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - Trixi.t8_forest_set_ghost(new_forest, do_ghost, Trixi.T8_GHOST_FACES) - Trixi.t8_forest_commit(new_forest) -end - -mesh.forest = new_forest +adapt!(mesh, adapt_callback) semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, boundary_conditions = boundary_conditions) diff --git a/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl b/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl index 13ec6789d9..bd467035f2 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl @@ -73,15 +73,8 @@ struct t8_dhex_t # [...] # See `p8est.h` in `p4est` for more info. end -function adapt_callback(forest, - forest_from, - which_tree, - lelement_id, - ts, - is_family, - num_elements, - elements_ptr)::Cint - elements = unsafe_wrap(Array, elements_ptr, num_elements) +function adapt_callback(forest, ltreeid, eclass_scheme, lelemntid, elements, is_family, + user_data) el = unsafe_load(Ptr{t8_dhex_t}(elements[1])) if el.x == 0 && el.y == 0 && el.z == 0 && el.level < 2 @@ -93,28 +86,7 @@ function adapt_callback(forest, end end -@assert(Trixi.t8_forest_is_committed(mesh.forest)!=0); - -# Init new forest. -new_forest_ref = Ref{Trixi.t8_forest_t}() -Trixi.t8_forest_init(new_forest_ref); -new_forest = new_forest_ref[] - -# Check out `examples/t8_step4_partition_balance_ghost.jl` in -# https://github.com/DLR-AMR/T8code.jl for detailed explanations. -let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0, - do_ghost = 1 - - Trixi.t8_forest_set_user_data(new_forest, C_NULL) - Trixi.t8_forest_set_adapt(new_forest, mesh.forest, - Trixi.@t8_adapt_callback(adapt_callback), recursive) - Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition) - Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening) - Trixi.t8_forest_set_ghost(new_forest, do_ghost, Trixi.T8_GHOST_FACES) - Trixi.t8_forest_commit(new_forest) -end - -mesh.forest = new_forest +adapt!(mesh, adapt_callback) semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, source_terms = source_terms_convergence_test, diff --git a/src/Trixi.jl b/src/Trixi.jl index e18b2f6415..288c2ad398 100644 --- a/src/Trixi.jl +++ b/src/Trixi.jl @@ -222,6 +222,8 @@ export ncomponents, eachcomponent export TreeMesh, StructuredMesh, UnstructuredMesh2D, P4estMesh, T8codeMesh +export adapt! + export DG, DGSEM, LobattoLegendreBasis, FDSBP, diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index 83e3dd20d5..ecdca62776 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -421,3 +421,8 @@ function trixi_t8_adapt!(mesh, indicators) return differences end + +trixi_t8_msh_file_negative_volume = """ + + +""" diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 0c2b70652d..bc19a338e3 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -308,10 +308,18 @@ function T8codeMesh(cmesh::Ptr{t8_cmesh}; if NDIMS == 2 data_in = Array{RealT, 3}(undef, 2, 2, 2) tmp1 = zeros(RealT, 2, length(nodes), length(nodes_in)) + verts = zeros(3, 4) for itree in 0:(num_local_trees - 1) veptr = t8_cmesh_get_tree_vertices(cmesh, itree) - verts = unsafe_wrap(Array, veptr, (3, 1 << NDIMS)) + + # Note, `verts = unsafe_wrap(Array, veptr, (3, 1 << NDIMS))` + # sometimes does not work since `veptr` is not necessarily properly + # aligned to 8 bytes. + for icorner in 1:4 + verts[1, icorner] = unsafe_load(veptr, (icorner - 1) * 3 + 1) + verts[2, icorner] = unsafe_load(veptr, (icorner - 1) * 3 + 2) + end # Check if tree's node ordering is right-handed or print a warning. let z = zero(eltype(verts)), o = one(eltype(verts)) @@ -343,10 +351,19 @@ function T8codeMesh(cmesh::Ptr{t8_cmesh}; elseif NDIMS == 3 data_in = Array{RealT, 4}(undef, 3, 2, 2, 2) tmp1 = zeros(RealT, 3, length(nodes), length(nodes_in), length(nodes_in)) + verts = zeros(3, 4) for itree in 0:(num_local_trees - 1) veptr = t8_cmesh_get_tree_vertices(cmesh, itree) - verts = unsafe_wrap(Array, veptr, (3, 1 << NDIMS)) + + # Note, `verts = unsafe_wrap(Array, veptr, (3, 1 << NDIMS))` + # sometimes does not work since `veptr` is not necessarily properly + # aligned to 8 bytes. + for icorner in 1:8 + verts[1, icorner] = unsafe_load(veptr, (icorner - 1) * 3 + 1) + verts[2, icorner] = unsafe_load(veptr, (icorner - 1) * 3 + 2) + verts[3, icorner] = unsafe_load(veptr, (icorner - 1) * 3 + 3) + end # Tree vertices are stored in z-order. @views data_in[:, 1, 1, 1] .= verts[1:3, 1] @@ -454,6 +471,85 @@ function T8codeMesh(meshfile::String, ndims; kwargs...) return T8codeMesh(cmesh; kwargs...) end +struct adapt_callback_passthrough + adapt_callback::Function + user_data::Any +end + +# Callback function prototype to decide for refining and coarsening. +# If `is_family` equals 1, the first `num_elements` in elements +# form a family and we decide whether this family should be coarsened +# or only the first element should be refined. +# Otherwise `is_family` must equal zero and we consider the first entry +# of the element array for refinement. +# Entries of the element array beyond the first `num_elements` are undefined. +# \param [in] forest the forest to which the new elements belong +# \param [in] forest_from the forest that is adapted. +# \param [in] which_tree the local tree containing `elements` +# \param [in] lelement_id the local element id in `forest_old` in the tree of the current element +# \param [in] ts the eclass scheme of the tree +# \param [in] is_family if 1, the first `num_elements` entries in `elements` form a family. If 0, they do not. +# \param [in] num_elements the number of entries in `elements` that are defined +# \param [in] elements Pointers to a family or, if `is_family` is zero, +# pointer to one element. +# \return greater zero if the first entry in `elements` should be refined, +# smaller zero if the family `elements` shall be coarsened, +# zero else. +function adapt_callback_wrapper(forest, + forest_from, + which_tree, + lelement_id, + ts, + is_family, + num_elements, + elements_ptr)::Cint + passthrough = unsafe_pointer_to_objref(t8_forest_get_user_data(forest))[] + + elements = unsafe_wrap(Array, elements_ptr, num_elements) + + return passthrough.adapt_callback(forest_from, which_tree, ts, lelement_id, elements, + Bool(is_family), passthrough.user_data) +end + +function adapt!(mesh::T8codeMesh, adapt_callback; recursive = true, balance = true, + partition = true, ghost = true, user_data = C_NULL) + # Check that forest is a committed, that is valid and usable, forest. + @assert t8_forest_is_committed(mesh.forest) != 0 + + # Init new forest. + new_forest_ref = Ref{t8_forest_t}() + t8_forest_init(new_forest_ref) + new_forest = new_forest_ref[] + + # Check out `examples/t8_step4_partition_balance_ghost.jl` in + # https://github.com/DLR-AMR/T8code.jl for detailed explanations. + let set_from = C_NULL, set_for_coarsening = 0, no_repartition = !partition + t8_forest_set_user_data(new_forest, + pointer_from_objref(Ref(adapt_callback_passthrough(adapt_callback, + user_data)))) + t8_forest_set_adapt(new_forest, mesh.forest, + @t8_adapt_callback(adapt_callback_wrapper), + recursive) + if balance + t8_forest_set_balance(new_forest, set_from, no_repartition) + end + + if partition + t8_forest_set_partition(new_forest, set_from, set_for_coarsening) + end + + t8_forest_set_ghost(new_forest, ghost, T8_GHOST_FACES) # Note: MPI support not available yet so it is a dummy call. + + # The old forest is destroyed here. + # Call `t8_forest_ref(Ref(mesh.forest))` to keep it. + t8_forest_commit(new_forest) + end + + mesh.forest = new_forest + + return nothing +end + # TODO: Just a placeholder. Will be implemented later when MPI is supported. function balance!(mesh::T8codeMesh, init_fn = C_NULL) return nothing diff --git a/test/test_t8code_2d.jl b/test/test_t8code_2d.jl index b3e1947132..7d66f537a0 100644 --- a/test/test_t8code_2d.jl +++ b/test/test_t8code_2d.jl @@ -30,7 +30,19 @@ mkdir(outdir) end end +@trixi_testset "test check_for_negative_volumes" begin + # Unstructured mesh with six cells which have left-handed node ordering. + mesh_file = joinpath(EXAMPLES_DIR, "rectangle_with_negative_volumes.msh") + isfile(mesh_file) || + download("https://gist.githubusercontent.com/jmark/bfe0d45f8e369298d6cc637733819013/raw/cecf86edecc736e8b3e06e354c494b2052d41f7a/rectangle_with_negative_volumes.msh", + mesh_file) + + # This call should throw a warning about negative volumes detected. + mesh = T8codeMesh(mesh_file, 2) +end + @trixi_testset "elixir_advection_basic.jl" begin + # This test is identical to the one in `test_p4est_2d.jl`. @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_basic.jl"), # Expected errors are exactly the same as with TreeMesh! l2=[8.311947673061856e-6], @@ -46,6 +58,7 @@ end end @trixi_testset "elixir_advection_nonconforming_flag.jl" begin + # This test is identical to the one in `test_p4est_2d.jl`. @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_nonconforming_flag.jl"), l2=[3.198940059144588e-5], @@ -61,6 +74,7 @@ end end @trixi_testset "elixir_advection_unstructured_flag.jl" begin + # This test is identical to the one in `test_p4est_2d.jl`. @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_unstructured_flag.jl"), l2=[0.0005379687442422346], linf=[0.007438525029884735]) @@ -91,6 +105,7 @@ end end @trixi_testset "elixir_advection_amr_solution_independent.jl" begin + # This test is identical to the one in `test_p4est_2d.jl`. @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_amr_solution_independent.jl"), # Expected errors are exactly the same as with StructuredMesh! @@ -108,6 +123,7 @@ end end @trixi_testset "elixir_euler_source_terms_nonconforming_unstructured_flag.jl" begin + # This test is identical to the one in `test_p4est_2d.jl`. @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_source_terms_nonconforming_unstructured_flag.jl"), l2=[ @@ -133,6 +149,7 @@ end end @trixi_testset "elixir_euler_free_stream.jl" begin + # This test is identical to the one in `test_p4est_2d.jl`. @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_free_stream.jl"), l2=[ 2.063350241405049e-15, @@ -153,6 +170,7 @@ end end @trixi_testset "elixir_euler_shockcapturing_ec.jl" begin + # This test is identical to the one in `test_p4est_2d.jl`. @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_shockcapturing_ec.jl"), l2=[ 9.53984675e-02, @@ -178,6 +196,8 @@ end end @trixi_testset "elixir_euler_sedov.jl" begin + # This test is identical to the one in `test_p4est_2d.jl` besides minor + # deviations in the expected error norms. @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_sedov.jl"), l2=[ 3.76149952e-01, @@ -203,6 +223,7 @@ end end @trixi_testset "elixir_shallowwater_source_terms.jl" begin + # This test is identical to the one in `test_p4est_2d.jl`. @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_source_terms.jl"), l2=[ 9.168126407325352e-5, @@ -228,6 +249,7 @@ end end @trixi_testset "elixir_mhd_alfven_wave.jl" begin + # This test is identical to the one in `test_p4est_2d.jl`. @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_mhd_alfven_wave.jl"), l2=[1.0513414461545583e-5, 1.0517900957166411e-6, 1.0517900957304043e-6, 1.511816606372376e-6, @@ -250,6 +272,8 @@ end end @trixi_testset "elixir_mhd_rotor.jl" begin + # This test is identical to the one in `test_p4est_2d.jl` besides minor + # deviations in the expected error norms. @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_mhd_rotor.jl"), l2=[0.44211360369891683, 0.8805178316216257, 0.8262710688468049, 0.0, diff --git a/test/test_t8code_3d.jl b/test/test_t8code_3d.jl index d959f18c38..4232cf0409 100644 --- a/test/test_t8code_3d.jl +++ b/test/test_t8code_3d.jl @@ -13,6 +13,7 @@ isdir(outdir) && rm(outdir, recursive = true) mkdir(outdir) @testset "T8codeMesh3D" begin + # This test is identical to the one in `test_p4est_3d.jl`. @trixi_testset "elixir_advection_basic.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_basic.jl"), # Expected errors are exactly the same as with TreeMesh! @@ -28,6 +29,7 @@ mkdir(outdir) end end + # This test is identical to the one in `test_p4est_3d.jl`. @trixi_testset "elixir_advection_unstructured_curved.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_unstructured_curved.jl"), @@ -43,6 +45,7 @@ mkdir(outdir) end end + # This test is identical to the one in `test_p4est_3d.jl`. @trixi_testset "elixir_advection_nonconforming.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_nonconforming.jl"), l2=[0.00253595715323843], @@ -57,6 +60,8 @@ mkdir(outdir) end end + # This test is identical to the one in `test_p4est_3d.jl` besides minor + # deviations from the expected error norms. @trixi_testset "elixir_advection_amr.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_amr.jl"), # Expected errors are exactly the same as with TreeMesh! @@ -74,6 +79,8 @@ mkdir(outdir) end end + # This test is identical to the one in `test_p4est_3d.jl` besides minor + # deviations from the expected error norms. @trixi_testset "elixir_advection_amr_unstructured_curved.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_amr_unstructured_curved.jl"), @@ -92,6 +99,7 @@ mkdir(outdir) end end + # This test is identical to the one in `test_p4est_3d.jl`. @trixi_testset "elixir_euler_source_terms_nonconforming_unstructured_curved.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_source_terms_nonconforming_unstructured_curved.jl"), @@ -120,6 +128,7 @@ mkdir(outdir) end end + # This test is identical to the one in `test_p4est_3d.jl`. @trixi_testset "elixir_euler_source_terms_nonperiodic.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_source_terms_nonperiodic.jl"), @@ -148,6 +157,7 @@ mkdir(outdir) end end + # This test is identical to the one in `test_p4est_3d.jl`. @trixi_testset "elixir_euler_free_stream.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_free_stream.jl"), l2=[ @@ -175,6 +185,7 @@ mkdir(outdir) end end + # This test is identical to the one in `test_p4est_3d.jl`. @trixi_testset "elixir_euler_free_stream_extruded.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_free_stream_extruded.jl"), l2=[ @@ -202,6 +213,7 @@ mkdir(outdir) end end + # This test is identical to the one in `test_p4est_3d.jl`. @trixi_testset "elixir_euler_ec.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_ec.jl"), l2=[ @@ -230,6 +242,8 @@ mkdir(outdir) end end + # This test is identical to the one in `test_p4est_3d.jl` besides minor + # deviations in the expected error norms. @trixi_testset "elixir_euler_sedov.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_sedov.jl"), l2=[ From cd7acc5fce5ba86ccbb857f7bc526b7a242bd8ff Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Thu, 18 Jan 2024 16:40:11 +0100 Subject: [PATCH 116/128] Deleted msh file. --- .../rectangle_with_negative_volumes.msh | 79 ------------------- 1 file changed, 79 deletions(-) delete mode 100644 examples/t8code_2d_dgsem/rectangle_with_negative_volumes.msh diff --git a/examples/t8code_2d_dgsem/rectangle_with_negative_volumes.msh b/examples/t8code_2d_dgsem/rectangle_with_negative_volumes.msh deleted file mode 100644 index 64fcdcd25d..0000000000 --- a/examples/t8code_2d_dgsem/rectangle_with_negative_volumes.msh +++ /dev/null @@ -1,79 +0,0 @@ -$MeshFormat -4.1 0 8 -$EndMeshFormat -$Entities -4 4 1 0 -1000 -3 -3 0 0 -1001 -3 3 0 0 -1002 3 3 0 0 -1003 3 -3 0 0 -101 -3 -3 0 -3 3 0 0 2 1000 -1001 -102 -3 3 0 3 3 0 0 2 1001 -1002 -103 3 -3 0 3 3 0 0 2 1002 -1003 -104 -3 -3 0 3 -3 0 0 2 1003 -1000 -1 -3 -3 0 3 3 0 0 4 101 102 103 104 -$EndEntities -$Nodes -9 11 1 11 -0 1000 0 1 -1 --3 -3 0 -0 1001 0 1 -2 --3 3 0 -0 1002 0 1 -3 -3 3 0 -0 1003 0 1 -4 -3 -3 0 -1 101 1 1 -5 --3 -1.101696511796035e-11 0 0.4999999999981639 -1 102 1 1 -6 --1.101696511796035e-11 3 0 0.4999999999981639 -1 103 1 1 -7 -3 1.101696511796035e-11 0 0.4999999999981639 -1 104 1 1 -8 -1.101696511796035e-11 -3 0 0.4999999999981639 -2 1 1 3 -9 -10 -11 --0.0002444350403711321 0.005208961077741881 -0 -0.005208961077741881 -0.0002444350403711321 -0.8787876029934786 -0.8769758575617213 0 0.8769758575617213 0.8787876029934786 --0.8731987035236274 0.8965716725861542 -0 -0.8965716725861542 -0.8731987035236274 -$EndNodes -$Elements -9 18 1 18 -0 1000 15 1 -1 1 -0 1001 15 1 -2 2 -0 1002 15 1 -3 3 -0 1003 15 1 -4 4 -1 101 1 2 -5 1 5 -6 5 2 -1 102 1 2 -7 2 6 -8 6 3 -1 103 1 2 -9 3 7 -10 7 4 -1 104 1 2 -11 4 8 -12 8 1 -2 1 3 6 -13 7 4 8 10 -14 6 11 5 2 -15 3 9 11 6 -16 1 9 10 8 -17 3 7 10 9 -18 1 5 11 9 -$EndElements From 52f917ee67711357b140a2a39520714420129d0c Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Thu, 18 Jan 2024 16:57:36 +0100 Subject: [PATCH 117/128] Fixed a bug. --- src/meshes/t8code_mesh.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index bc19a338e3..fde566bffe 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -351,7 +351,7 @@ function T8codeMesh(cmesh::Ptr{t8_cmesh}; elseif NDIMS == 3 data_in = Array{RealT, 4}(undef, 3, 2, 2, 2) tmp1 = zeros(RealT, 3, length(nodes), length(nodes_in), length(nodes_in)) - verts = zeros(3, 4) + verts = zeros(3, 8) for itree in 0:(num_local_trees - 1) veptr = t8_cmesh_get_tree_vertices(cmesh, itree) From c67800ae4d2e2d2de62083c60bf0a78d4013234a Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Thu, 18 Jan 2024 17:15:39 +0100 Subject: [PATCH 118/128] Code cleanup. --- src/auxiliary/t8code.jl | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index ecdca62776..83e3dd20d5 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -421,8 +421,3 @@ function trixi_t8_adapt!(mesh, indicators) return differences end - -trixi_t8_msh_file_negative_volume = """ - - -""" From d7afb24646e41b10398205561a6ea387633cfe2f Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Fri, 19 Jan 2024 11:15:32 +0100 Subject: [PATCH 119/128] Ignore gmsh files. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 3132b9af38..b4f1cf6bb4 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ *.mesh *.bson *.inp +*.msh **/Manifest.toml out*/ docs/build From 8f80b7672c9d014ac2a357b2b4a4a0592cadcbd7 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Fri, 19 Jan 2024 11:15:53 +0100 Subject: [PATCH 120/128] Removed adapt! from global namespace. --- examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl | 2 +- examples/t8code_2d_dgsem/elixir_euler_free_stream.jl | 2 +- ...elixir_euler_source_terms_nonconforming_unstructured_flag.jl | 2 +- examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl | 2 +- examples/t8code_3d_dgsem/elixir_euler_free_stream.jl | 2 +- examples/t8code_3d_dgsem/elixir_euler_free_stream_extruded.jl | 2 +- ...ixir_euler_source_terms_nonconforming_unstructured_curved.jl | 2 +- src/Trixi.jl | 2 -- 8 files changed, 7 insertions(+), 9 deletions(-) diff --git a/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl b/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl index 61f78bf683..a39f3a7e19 100644 --- a/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl @@ -50,7 +50,7 @@ function adapt_callback(forest, ltreeid, eclass_scheme, lelemntid, elements, is_ end end -adapt!(mesh, adapt_callback) +Trixi.adapt!(mesh, adapt_callback) # A semidiscretization collects data structures and functions for the spatial discretization semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_convergence_test, diff --git a/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl b/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl index 5ed36d34a9..37d15f3856 100644 --- a/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl +++ b/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl @@ -62,7 +62,7 @@ function adapt_callback(forest, ltreeid, eclass_scheme, lelemntid, elements, is_ end end -adapt!(mesh, adapt_callback) +Trixi.adapt!(mesh, adapt_callback) semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, boundary_conditions = Dict(:all => BoundaryConditionDirichlet(initial_condition))) diff --git a/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl b/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl index 552b7d2500..bcc1abc560 100644 --- a/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl +++ b/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl @@ -62,7 +62,7 @@ function adapt_callback(forest, ltreeid, eclass_scheme, lelemntid, elements, is_ end end -adapt!(mesh, adapt_callback) +Trixi.adapt!(mesh, adapt_callback) semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, source_terms = source_terms, diff --git a/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl b/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl index c1a49a8100..8d7a48370f 100644 --- a/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl +++ b/examples/t8code_3d_dgsem/elixir_advection_nonconforming.jl @@ -46,7 +46,7 @@ function adapt_callback(forest, ltreeid, eclass_scheme, lelemntid, elements, is_ end end -adapt!(mesh, adapt_callback) +Trixi.adapt!(mesh, adapt_callback) # A semidiscretization collects data structures and functions for the spatial discretization semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_convergence_test, diff --git a/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl b/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl index f4799b0b88..e135d46481 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_free_stream.jl @@ -85,7 +85,7 @@ function adapt_callback(forest, ltreeid, eclass_scheme, lelemntid, elements, is_ end end -adapt!(mesh, adapt_callback) +Trixi.adapt!(mesh, adapt_callback) semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, boundary_conditions = boundary_conditions) diff --git a/examples/t8code_3d_dgsem/elixir_euler_free_stream_extruded.jl b/examples/t8code_3d_dgsem/elixir_euler_free_stream_extruded.jl index a4b0b282c4..d129b59826 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_free_stream_extruded.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_free_stream_extruded.jl @@ -73,7 +73,7 @@ function adapt_callback(forest, ltreeid, eclass_scheme, lelemntid, elements, is_ end end -adapt!(mesh, adapt_callback) +Trixi.adapt!(mesh, adapt_callback) semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, boundary_conditions = boundary_conditions) diff --git a/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl b/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl index bd467035f2..d4664522be 100644 --- a/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl +++ b/examples/t8code_3d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_curved.jl @@ -86,7 +86,7 @@ function adapt_callback(forest, ltreeid, eclass_scheme, lelemntid, elements, is_ end end -adapt!(mesh, adapt_callback) +Trixi.adapt!(mesh, adapt_callback) semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, source_terms = source_terms_convergence_test, diff --git a/src/Trixi.jl b/src/Trixi.jl index 288c2ad398..e18b2f6415 100644 --- a/src/Trixi.jl +++ b/src/Trixi.jl @@ -222,8 +222,6 @@ export ncomponents, eachcomponent export TreeMesh, StructuredMesh, UnstructuredMesh2D, P4estMesh, T8codeMesh -export adapt! - export DG, DGSEM, LobattoLegendreBasis, FDSBP, From c6204f1c17d62dba6a6f44c03e9a2080735aca08 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Fri, 19 Jan 2024 12:00:55 +0100 Subject: [PATCH 121/128] Added documentation. --- src/meshes/t8code_mesh.jl | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index fde566bffe..e3293786cf 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -511,6 +511,41 @@ function adapt_callback_wrapper(forest, Bool(is_family), passthrough.user_data) end +""" + adapt!(mesh::T8codeMesh, adapt_callback; kwargs...) + +Adapt a `T8codeMesh` according to a user-defined `adapt_callback`. + +# Arguments +- `mesh::T8codeMesh`: Initialized mesh object. +- `adapt_callback`: A user-defined callback which tells the adaption routines + if an element should be refined, coarsend or stay unchanged. + + The expected callback signature is as follows: + + `adapt_callback(forest, ltreeid, eclass_scheme, lelemntid, elements, is_family, user_data)` + # Arguments + - `forest`: Pointer to the analyzed forest. + - `ltreeid`: Local index of the current tree where the analyzed elements are part of. + - `eclass_scheme`: Element class of `elements`. + - `lelemntid`: Local index of the first element in `elements`. + - `elements`: Array of elements. If consecutive elements form a family + they are passed together, otherwise `elements` consists of just one element. + - `is_family`: Boolean signifying if `elements` represents a family or not. + - `user_data`: Void pointer to some arbitrary user data. Default value is `C_NULL`. + # Returns + -1 : Coarsen family of elements. + 0 : Stay unchanged. + 1 : Refine element. + +- `kwargs`: + - `recursive = true`: Adapt the forest recursively. If true the caller must ensure that the callback + returns 0 for every analyzed element at some point to stop the recursion. + - `balance = true`: Make sure the adapted forest is 2^(NDIMS-1):1 balanced. + - `partition = true`: Partition the forest to redistribute elements evenly among MPI ranks. + - `ghost = true`: Create a ghost layer for MPI data exchange. + - `user_data = C_NULL`: Pointer to some arbitrary user-defined data. +""" function adapt!(mesh::T8codeMesh, adapt_callback; recursive = true, balance = true, partition = true, ghost = true, user_data = C_NULL) # Check that forest is a committed, that is valid and usable, forest. From 8100cd834535dcdd2bad16c22202021c8fb14d7e Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Fri, 19 Jan 2024 13:23:13 +0100 Subject: [PATCH 122/128] Added @test_warn to test. --- test/test_t8code_2d.jl | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/test/test_t8code_2d.jl b/test/test_t8code_2d.jl index 7d66f537a0..baa52ae2e6 100644 --- a/test/test_t8code_2d.jl +++ b/test/test_t8code_2d.jl @@ -31,14 +31,16 @@ mkdir(outdir) end @trixi_testset "test check_for_negative_volumes" begin - # Unstructured mesh with six cells which have left-handed node ordering. - mesh_file = joinpath(EXAMPLES_DIR, "rectangle_with_negative_volumes.msh") - isfile(mesh_file) || - download("https://gist.githubusercontent.com/jmark/bfe0d45f8e369298d6cc637733819013/raw/cecf86edecc736e8b3e06e354c494b2052d41f7a/rectangle_with_negative_volumes.msh", - mesh_file) + @test_warn "Discovered negative volumes" begin + # Unstructured mesh with six cells which have left-handed node ordering. + mesh_file = joinpath(EXAMPLES_DIR, "rectangle_with_negative_volumes.msh") + isfile(mesh_file) || + download("https://gist.githubusercontent.com/jmark/bfe0d45f8e369298d6cc637733819013/raw/cecf86edecc736e8b3e06e354c494b2052d41f7a/rectangle_with_negative_volumes.msh", + mesh_file) - # This call should throw a warning about negative volumes detected. - mesh = T8codeMesh(mesh_file, 2) + # This call should throw a warning about negative volumes detected. + mesh = T8codeMesh(mesh_file, 2) + end end @trixi_testset "elixir_advection_basic.jl" begin From 65c442442eb8db1abaae884753bc59fc9d853328 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Fri, 19 Jan 2024 13:25:00 +0100 Subject: [PATCH 123/128] Applied formatter. --- test/test_t8code_2d.jl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/test_t8code_2d.jl b/test/test_t8code_2d.jl index baa52ae2e6..ab95e068d0 100644 --- a/test/test_t8code_2d.jl +++ b/test/test_t8code_2d.jl @@ -31,16 +31,16 @@ mkdir(outdir) end @trixi_testset "test check_for_negative_volumes" begin - @test_warn "Discovered negative volumes" begin - # Unstructured mesh with six cells which have left-handed node ordering. - mesh_file = joinpath(EXAMPLES_DIR, "rectangle_with_negative_volumes.msh") - isfile(mesh_file) || - download("https://gist.githubusercontent.com/jmark/bfe0d45f8e369298d6cc637733819013/raw/cecf86edecc736e8b3e06e354c494b2052d41f7a/rectangle_with_negative_volumes.msh", - mesh_file) + @test_warn "Discovered negative volumes" begin + # Unstructured mesh with six cells which have left-handed node ordering. + mesh_file = joinpath(EXAMPLES_DIR, "rectangle_with_negative_volumes.msh") + isfile(mesh_file) || + download("https://gist.githubusercontent.com/jmark/bfe0d45f8e369298d6cc637733819013/raw/cecf86edecc736e8b3e06e354c494b2052d41f7a/rectangle_with_negative_volumes.msh", + mesh_file) - # This call should throw a warning about negative volumes detected. - mesh = T8codeMesh(mesh_file, 2) - end + # This call should throw a warning about negative volumes detected. + mesh = T8codeMesh(mesh_file, 2) + end end @trixi_testset "elixir_advection_basic.jl" begin From 28b1244822ac282d22dac389b83afc242172da68 Mon Sep 17 00:00:00 2001 From: Benedict <135045760+bgeihe@users.noreply.github.com> Date: Fri, 19 Jan 2024 14:06:36 +0100 Subject: [PATCH 124/128] Apply suggestions from code review Co-authored-by: Hendrik Ranocha Co-authored-by: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com> --- src/meshes/t8code_mesh.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index e3293786cf..3343010e84 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -512,14 +512,14 @@ function adapt_callback_wrapper(forest, end """ - adapt!(mesh::T8codeMesh, adapt_callback; kwargs...) + Trixi.adapt!(mesh::T8codeMesh, adapt_callback; kwargs...) Adapt a `T8codeMesh` according to a user-defined `adapt_callback`. # Arguments - `mesh::T8codeMesh`: Initialized mesh object. - `adapt_callback`: A user-defined callback which tells the adaption routines - if an element should be refined, coarsend or stay unchanged. + if an element should be refined, coarsened or stay unchanged. The expected callback signature is as follows: From cae1cbbcdeb434e2bf72d0a4f0ef5a0b50d48b66 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Fri, 19 Jan 2024 14:30:00 +0100 Subject: [PATCH 125/128] Turned @warn to @info. --- src/auxiliary/t8code.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl index 83e3dd20d5..db01476bb8 100644 --- a/src/auxiliary/t8code.jl +++ b/src/auxiliary/t8code.jl @@ -35,7 +35,7 @@ function init_t8code() # production runs this is not mandatory, but is helpful during # development. Hence, this option is only activated when environment # variable TRIXI_T8CODE_SC_FINALIZE exists. - @warn "T8code.jl: sc_finalize will be called during shutdown of Trixi.jl." + @info "T8code.jl: `sc_finalize` will be called during shutdown of Trixi.jl." MPI.add_finalize_hook!(T8code.Libt8.sc_finalize) end else From e055f5a97041759ecff55273f802b3b87fccbae7 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Fri, 19 Jan 2024 14:30:34 +0100 Subject: [PATCH 126/128] Code cleanup and added @deprecated routines in order to avoid breaking release. --- src/meshes/t8code_mesh.jl | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index e3293786cf..026e6cf5db 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -138,7 +138,7 @@ Non-periodic boundaries will be called ':x_neg', ':x_pos', ':y_neg', ':y_pos', ' - 'periodicity': either a 'Bool' deciding if all of the boundaries are periodic or an 'NTuple{NDIMS, Bool}' deciding for each dimension if the boundaries in this dimension are periodic. """ -function T8codeMesh(trees_per_dimension; polydeg, +function T8codeMesh(trees_per_dimension; polydeg = 1, mapping = nothing, faces = nothing, coordinates_min = nothing, coordinates_max = nothing, RealT = Float64, initial_refinement_level = 0, @@ -261,9 +261,9 @@ function T8codeMesh(trees_per_dimension; polydeg, end """ - T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh}, - mapping=nothing, polydeg=1, RealT=Float64, - initial_refinement_level=0) + T8codeMesh(cmesh::Ptr{t8_cmesh}, + mapping=nothing, polydeg=1, RealT=Float64, + initial_refinement_level=0) Main mesh constructor for the `T8codeMesh` that imports an unstructured, conforming mesh from a `t8_cmesh` data structure. @@ -449,6 +449,7 @@ mesh from a Gmsh mesh file (`.msh`). # Arguments - `meshfile::String`: path to a Gmsh mesh file. +- `ndims`: Mesh file dimension: `2` or `3`. - `mapping`: a function of `NDIMS` variables to describe the mapping that transforms the imported mesh to the physical domain. Use `nothing` for the identity map. - `polydeg::Integer`: polynomial degree used to store the geometry of the mesh. @@ -594,3 +595,8 @@ end function partition!(mesh::T8codeMesh; allow_coarsening = true, weight_fn = C_NULL) return nothing end + +@deprecate T8codeMesh{2}(conn::Ptr{p4est_connectivity}; kwargs...) T8codeMesh(conn::Ptr{p4est_connectivity}; kwargs...) +@deprecate T8codeMesh{3}(conn::Ptr{p8est_connectivity}; kwargs...) T8codeMesh(conn::Ptr{p8est_connectivity}; kwargs...) +@deprecate T8codeMesh{2}(meshfile::String; kwargs...) T8codeMesh(meshfile::String, 2; kwargs...) +@deprecate T8codeMesh{3}(meshfile::String; kwargs...) T8codeMesh(meshfile::String, 3; kwargs...) From 4a1b775613a5d97768c0adcf292fc07ab0e3f986 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Fri, 19 Jan 2024 14:36:23 +0100 Subject: [PATCH 127/128] Applied formatter. --- src/meshes/t8code_mesh.jl | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 053ecdc7f3..1016c2cdb3 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -596,7 +596,11 @@ function partition!(mesh::T8codeMesh; allow_coarsening = true, weight_fn = C_NUL return nothing end -@deprecate T8codeMesh{2}(conn::Ptr{p4est_connectivity}; kwargs...) T8codeMesh(conn::Ptr{p4est_connectivity}; kwargs...) -@deprecate T8codeMesh{3}(conn::Ptr{p8est_connectivity}; kwargs...) T8codeMesh(conn::Ptr{p8est_connectivity}; kwargs...) -@deprecate T8codeMesh{2}(meshfile::String; kwargs...) T8codeMesh(meshfile::String, 2; kwargs...) -@deprecate T8codeMesh{3}(meshfile::String; kwargs...) T8codeMesh(meshfile::String, 3; kwargs...) +@deprecate T8codeMesh{2}(conn::Ptr{p4est_connectivity}; kwargs...) T8codeMesh(conn::Ptr{p4est_connectivity}; + kwargs...) +@deprecate T8codeMesh{3}(conn::Ptr{p8est_connectivity}; kwargs...) T8codeMesh(conn::Ptr{p8est_connectivity}; + kwargs...) +@deprecate T8codeMesh{2}(meshfile::String; kwargs...) T8codeMesh(meshfile::String, 2; + kwargs...) +@deprecate T8codeMesh{3}(meshfile::String; kwargs...) T8codeMesh(meshfile::String, 3; + kwargs...) From 6e79e6a3ead64f830f253f92d10719d8ce877ab1 Mon Sep 17 00:00:00 2001 From: Johannes Markert Date: Fri, 19 Jan 2024 14:43:06 +0100 Subject: [PATCH 128/128] Added formatter pragmas to avoid ugly formatting. --- src/meshes/t8code_mesh.jl | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl index 1016c2cdb3..c9665a22af 100644 --- a/src/meshes/t8code_mesh.jl +++ b/src/meshes/t8code_mesh.jl @@ -596,11 +596,9 @@ function partition!(mesh::T8codeMesh; allow_coarsening = true, weight_fn = C_NUL return nothing end -@deprecate T8codeMesh{2}(conn::Ptr{p4est_connectivity}; kwargs...) T8codeMesh(conn::Ptr{p4est_connectivity}; - kwargs...) -@deprecate T8codeMesh{3}(conn::Ptr{p8est_connectivity}; kwargs...) T8codeMesh(conn::Ptr{p8est_connectivity}; - kwargs...) -@deprecate T8codeMesh{2}(meshfile::String; kwargs...) T8codeMesh(meshfile::String, 2; - kwargs...) -@deprecate T8codeMesh{3}(meshfile::String; kwargs...) T8codeMesh(meshfile::String, 3; - kwargs...) +#! format: off +@deprecate T8codeMesh{2}(conn::Ptr{p4est_connectivity}; kwargs...) T8codeMesh(conn::Ptr{p4est_connectivity}; kwargs...) +@deprecate T8codeMesh{3}(conn::Ptr{p8est_connectivity}; kwargs...) T8codeMesh(conn::Ptr{p8est_connectivity}; kwargs...) +@deprecate T8codeMesh{2}(meshfile::String; kwargs...) T8codeMesh(meshfile::String, 2; kwargs...) +@deprecate T8codeMesh{3}(meshfile::String; kwargs...) T8codeMesh(meshfile::String, 3; kwargs...) +#! format: on