Path: blob/main/docs/literate/src/files/structured_mesh_mapping.jl
5591 views
#src # Structured mesh with curvilinear mapping12# Here, we want to introduce another mesh type of [Trixi.jl](https://github.com/trixi-framework/Trixi.jl).3# More precisely, this tutorial is about the curved mesh type [`StructuredMesh`](@ref) supporting4# curved meshes.56# # Creating a curved mesh7# There are two basic options to define a curved [`StructuredMesh`](@ref) in Trixi.jl. You can8# implement curves for the domain boundaries, or alternatively, set up directly the complete9# transformation mapping. We now present one short example each.1011# ## Mesh defined by domain boundary curves12# Both examples are based on a semdiscretization of the 2D compressible Euler equations.1314using OrdinaryDiffEqLowStorageRK15using Trixi1617equations = CompressibleEulerEquations2D(1.4)1819# We start with a pressure perturbation at `(xs, 0.0)` as initial condition.20function initial_condition_pressure_perturbation(x, t,21equations::CompressibleEulerEquations2D)22xs = 1.5 # location of the initial disturbance on the x axis23w = 1 / 8 # half width24p = exp(-log(2) * ((x[1] - xs)^2 + x[2]^2) / w^2) + 1.025v1 = 0.026v2 = 0.027rho = 1.02829return prim2cons(SVector(rho, v1, v2, p), equations)30end31initial_condition = initial_condition_pressure_perturbation3233# Initialize every boundary as a [`boundary_condition_slip_wall`](@ref).34boundary_conditions = boundary_condition_slip_wall3536# The approximation setup is an entropy-stable split-form DG method with `polydeg=4`. We are using37# the two fluxes [`flux_ranocha`](@ref) and [`flux_lax_friedrichs`](@ref).38solver = DGSEM(polydeg = 4, surface_flux = flux_lax_friedrichs,39volume_integral = VolumeIntegralFluxDifferencing(flux_ranocha))4041# We want to define a circular cylinder as physical domain. It contains an inner semicircle with42# radius `r0` and an outer semicircle of radius `r1`.4344# 4546#src # \documentclass[border=0.2cm]{standalone}47#src # \usepackage{tikz}48#src # \begin{document}49#src # \begin{tikzpicture}50#src # % Circular cylinder51#src # \draw[<-] (-5,0) -- node[above] {$\textbf{f}_2$} (-0.5,0);52#src # \draw[->] (0.5,0) -- node[above] {$\textbf{f}_1$} (5,0);53#src # \draw[->] (0.5,0) arc (0:180:0.5); \node at (0, 0.8) {$\textbf{f}_3$};54#src # \draw[->] (5,0) arc (0:180:5); \node at (0, 4.7) {$\textbf{f}_4$};55#src # \draw[dashed] (-5, -0.5) -- node[below] {$\textbf{r}_1$} (0, -0.5);56#src # \draw[dashed] (-0.5, -0.7) -- node[below] {$\textbf{r}_0$} (0, -0.7);57#src # % Arrow58#src # \draw[line width=0.1cm, ->, bend angle=30, bend left] (4, 2) to (8,3);59#src # % Right Square60#src # \draw[->] (7,0) -- node[below] {$\textbf{f}_1$} (12,0);61#src # \draw[->] (7,5) -- node[above] {$\textbf{f}_2$} (12,5);62#src # \draw[->] (7,0) -- node[left] {$\textbf{f}_3$} (7,5);63#src # \draw[->] (12,0) -- node[right] {$\textbf{f}_4$} (12,5);64#src # % Pressure perturbation65#src # \draw[fill] (1.6, 0) arc (0:180:0.1);66#src # \draw (1.7, 0) arc (0:180:0.2);67#src # \draw (1.8, 0) arc (0:180:0.3);68#src # \end{tikzpicture}69#src # \end{document}7071# The domain boundary curves with curve parameter in $[-1,1]$ are sorted as shown in the sketch.72# They always are orientated from negative to positive coordinate, such that the corners have to73# fit like this $f_1(+1) = f_4(-1)$, $f_3(+1) = f_2(-1)$, etc.7475# In our case we can define the domain boundary curves as follows:76r0 = 0.5 # inner radius77r1 = 5.0 # outer radius78f1(xi) = SVector(r0 + 0.5 * (r1 - r0) * (xi + 1), 0.0) # right line79f2(xi) = SVector(-r0 - 0.5 * (r1 - r0) * (xi + 1), 0.0) # left line80f3(eta) = SVector(r0 * cos(0.5 * pi * (eta + 1)), r0 * sin(0.5 * pi * (eta + 1))) # inner circle81f4(eta) = SVector(r1 * cos(0.5 * pi * (eta + 1)), r1 * sin(0.5 * pi * (eta + 1))) # outer circle8283# We create a curved mesh with 16 x 16 elements. The defined domain boundary curves are passed as a tuple.84cells_per_dimension = (16, 16)85mesh = StructuredMesh(cells_per_dimension, (f1, f2, f3, f4), periodicity = false)8687# Then, we define the simulation with endtime `T=3` with `semi`, `ode` and `callbacks`.88semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,89boundary_conditions = boundary_conditions)9091tspan = (0.0, 3.0)92ode = semidiscretize(semi, tspan)9394analysis_interval = 10095analysis_callback = AnalysisCallback(semi, interval = analysis_interval)9697alive_callback = AliveCallback(analysis_interval = analysis_interval)9899stepsize_callback = StepsizeCallback(cfl = 0.9)100101callbacks = CallbackSet(analysis_callback,102alive_callback,103stepsize_callback);104105# Running the simulation106sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false);107dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback108ode_default_options()..., callback = callbacks);109110using Plots111plot(sol)112#-113pd = PlotData2D(sol)114plot(pd["p"])115plot!(getmesh(pd))116117# ## Mesh directly defined by the transformation mapping118# As mentioned before, you can also define the domain for a `StructuredMesh` by directly setting up119# a transformation mapping. Here, we want to present a nice mapping, which is often used to test120# free-stream preservation. Exact free-stream preservation is a crucial property of any numerical121# method on curvilinear grids. The mapping is a reduced 2D version of the mapping described in122# [Rueda-Ramírez et al. (2021), p.18](https://arxiv.org/abs/2012.12040).123124using OrdinaryDiffEqLowStorageRK125using Trixi126127equations = CompressibleEulerEquations2D(1.4)128129# As mentioned, this mapping is used for testing free-stream preservation. So, we use a constant130# initial condition.131initial_condition = initial_condition_constant132133solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs)134135# We define the transformation mapping with variables in $[-1, 1]$ as described in136# Rueda-Ramírez et al. (2021), p.18 (reduced to 2D):137function mapping(xi_, eta_)138## Transform input variables between -1 and 1 onto [0,3]139xi = 1.5 * xi_ + 1.5140eta = 1.5 * eta_ + 1.5141142y = eta + 3 / 8 * (cos(1.5 * pi * (2 * xi - 3) / 3) *143cos(0.5 * pi * (2 * eta - 3) / 3))144145x = xi + 3 / 8 * (cos(0.5 * pi * (2 * xi - 3) / 3) *146cos(2 * pi * (2 * y - 3) / 3))147148return SVector(x, y)149end150151# Instead of a tuple of boundary functions, the `mesh` now has the mapping as its parameter.152cells_per_dimension = (16, 16)153mesh = StructuredMesh(cells_per_dimension, mapping, periodicity = true)154155semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver;156boundary_conditions = boundary_condition_periodic)157158tspan = (0.0, 1.0)159ode = semidiscretize(semi, tspan)160161analysis_callback = AnalysisCallback(semi, interval = 250)162163stepsize_callback = StepsizeCallback(cfl = 0.8)164165callbacks = CallbackSet(analysis_callback,166stepsize_callback)167168sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false);169dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback170ode_default_options()..., callback = callbacks);171172# Now, we want to verify the free-stream preservation property and plot the mesh. For the verification,173# we calculate the absolute difference of the first conservation variable density `u[1]` and `1.0`.174# To plot this error and the mesh, we are using the visualization feature `ScalarPlotData2D`,175# explained in [visualization](@ref visualization).176error_density = let u = Trixi.wrap_array(sol.u[end], semi)177abs.(u[1, :, :, :] .- 1.0) # density, x, y, elements178end179pd = ScalarPlotData2D(error_density, semi)180181using Plots182plot(pd, title = "Error in density")183plot!(getmesh(pd))184185# We observe that the errors in the variable `density` are at the level of machine accuracy.186# Moreover, the plot shows the mesh structure resulting from our transformation mapping.187188# Of course, you can also use other mappings as for instance shifts by $(x, y)$189# ````julia190# mapping(xi, eta) = SVector(xi + x, eta + y)191# ````192# or rotations with a rotation matrix $T$193# ````julia194# mapping(xi, eta) = T * SVector(xi, eta).195# ````196197# For more curved mesh mappings, please have a look at some198# [elixirs for `StructuredMesh`](https://github.com/trixi-framework/Trixi.jl/tree/main/examples).199# For another curved mesh type, there is a [tutorial](@ref hohqmesh_tutorial) about Trixi.jl's200# unstructured mesh type [`UnstructuredMesh2D`] and its use of the201# [High-Order Hex-Quad Mesh (HOHQMesh) generator](https://github.com/trixi-framework/HOHQMesh),202# created and developed by David Kopriva.203204# ## Package versions205206# These results were obtained using the following versions.207208using InteractiveUtils209versioninfo()210211using Pkg212Pkg.status(["Trixi", "OrdinaryDiffEqLowStorageRK", "Plots"],213mode = PKGMODE_MANIFEST)214215216