-
Notifications
You must be signed in to change notification settings - Fork 113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom Advection/diffusion-like equation with limiter, unstructured grid and Robin Boundary condition #2262
Comments
Hi @cgadal - forgive me if I'm misunderstanding something, but you're imposing boundary conditions of |
In terms of instabilities - what do you observe specifically? Are you talking about the oscillations, or does the ODE solver crash? If it's oscillations, these are common for high order methods. Shock capturing will help suppress them, but they shouldn't crash a solver by themselves. |
Hi @jlchan, Thanks for your quick answer, and sorry I should have been more explicit and uploaded a better image. Here is the same at longer time steps: Hi @cgadal - forgive me if I'm misunderstanding something, but you're imposing boundary conditions of 0 and 1 but your initial condition is 0.8 here. Wouldn't you expect shocks/discontinuities to form since the BCs aren't consistent with the initial condition? Yes, two shocks should form at the boundaries (at the left, a shock between 1 and 0.8, and at the right a shock between 0 and 0.8) and meet somewhere in the centre of the domain. In terms of instabilities - what do you observe specifically? Are you talking about the oscillations, or does the ODE solver crash? If it's oscillations, these are common for high order methods. Shock capturing will help suppress them, but they shouldn't crash a solver by themselves. The right-moving shock forms at the left and moves to the right with reasonable shape and amplitude. However, oscillations develop and grow on the right side until the solver returns nans.
But the initial condition is not symmetric, so oscillations grow and get crazy when the shock gets too big. If I use 0.2 as a starting condition, then oscillations develop in at the other end. So I guess it is linked to the size of the shock, which would point towards the use of a limiter? Making the grid coarser indeed help as it makes the shock less sharp. |
The first thing to try is to set |
Setting
This is getting me confused, as I thought that central fluxes ( F(u_L, u_R) = 0.5 * (f(u_L) + f(u_R) ) ) or Lax-Friedrichs fluxes were also two-point fluxes. Could you clarify this for me? |
Yes. When you have smooth (parts of the) solutions, high-order methods will perform better since they give you much better results on coarse meshes.
You can use |
Thanks, it works amazingly (code below). I'll think about a more efficient and robust non-dissipative flux later in the process if needed. I'm now moving to adding the parabolic part :) Currently, the positivity limiter works in prevent phi from going negative. I suspect I could also use it to prevent phi from going above 1. Can I define another variable to pass to the limiter as phi_in = 1- phi ? Should I expand my system of equations to two variables? import Trixi as TR
import OrdinaryDiffEq as ODE
import Plots
# #### parameter values
F = -1
phi_0 = 0.8
D = 0.01
#############################
# ##### Set-up equation #####
#############################
# ###### hyperbolic part ######
struct SegregationEquation <: TR.AbstractEquations{1, # number of spatial dimensions
1} # number of primary variables, i.e. scalar
end
TR.flux(u, orientation, equation::SegregationEquation) = F .* u .* (TR.SVector(1) - u)
TR.varnames(_, ::SegregationEquation) = ("phi",)
equation_hyperbolic = SegregationEquation()
# characteristics
function TR.max_abs_speed_naive(u_ll, u_rr, orientation::Integer,
::SegregationEquation)
advection_velocity_ll = F .* (1 - 2 * u_ll[1])
advection_velocity_rr = F .* (1 - 2 * u_rr[1])
return max(TR.abs(advection_velocity_ll), TR.abs(advection_velocity_rr))
end
TR.max_abs_speeds(u, equations::SegregationEquation) = TR.abs(F .* (1 - 2 .* u[1]))
#################################
# ##### Boundary conditions #####
#################################
boundary_conditions_hyperbolic = (x_neg=TR.BoundaryConditionDirichlet((x, t, equation) -> TR.SVector(1.0)),
x_pos=TR.BoundaryConditionDirichlet((x, t, equation) -> TR.SVector(0.0)))
###################################
# ##### Create discretization #####
###################################
mesh = TR.TreeMesh(-1.0, 1.0, # min/max coordinates
initial_refinement_level=4,
n_cells_max=10^4,
periodicity=false)
volume_flux = TR.flux_central
surface_flux = TR.flux_lax_friedrichs
basis = TR.LobattoLegendreBasis(3)
#_______ part specific to shock capturing method
indicator_sc = TR.IndicatorHennemannGassner(equation, basis,
alpha_max=0.5,
alpha_min=0.001,
alpha_smooth=true,
variable=TR.first)
volume_integral = TR.VolumeIntegralShockCapturingHG(indicator_sc;
volume_flux_dg=volume_flux,
volume_flux_fv=surface_flux)
#_______
solver = TR.DGSEM(basis, surface_flux, volume_integral)
# initial conditions
initial_condition_plain(x, t, equation::SegregationEquation) = phi_0
# Create discretization
semi = TR.SemidiscretizationHyperbolic(mesh,
equation_hyperbolic,
initial_condition_plain,
solver,
boundary_conditions=boundary_conditions_hyperbolic)
#################################
# ##### Use temporal solver #####
#################################
# Create ODE problem with given time span
tspan = (0.0, 2)
ode = TR.semidiscretize(semi, tspan)
summary_callback = TR.SummaryCallback()
visualization_callback = TR.VisualizationCallback(solution_variables=TR.cons2cons, interval=5; clims=(0, 1), plot_data_creator=TR.PlotData1D)
stepsize_callback = TR.StepsizeCallback(cfl=0.5)
callbacks = TR.CallbackSet(summary_callback, visualization_callback, stepsize_callback)
stage_limiter! = TR.PositivityPreservingLimiterZhangShu(thresholds=(5e-6,), variables=(TR.first,))
# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks
sol = ODE.solve(ode, ODE.SSPRK33(stage_limiter!); dt=1.0, callback=callbacks);
Solution for polydeg=3 and initial_refinement_level=4: |
I have an issue with the definition of boundary conditions when adding the parabolic term. I tried to inspire myself from the struct BoundaryConditionConstantNeumann{T<:Real}
boundary_value::T
end
@inline function (boundary_condition::BoundaryConditionConstantNeumann)(flux_inner, u_inner,
normal::TR.AbstractVector,
x, t,
operator_type::TR.Divergence,
equations_parabolic::Diffusion1D)
return boundary_condition.boundary_value
end
@inline function (boundary_condition::BoundaryConditionConstantNeumann)(flux_inner, u_inner,
normal::TR.AbstractVector,
x, t,
operator_type::TR.Gradient,
equations_parabolic::Diffusion1D)
return flux_inner
end
boundary_conditions_parabolic = (x_neg=BoundaryConditionConstantNeumann(0.0),
x_pos=BoundaryConditionConstantNeumann(0.0))
but this returns the following error:
Full code (click to expand)import Trixi as TR
import OrdinaryDiffEq as ODE
import Plots
# #### parameter values
F = -1
phi_0 = 0.8
D = 0.01
#############################
# ##### Set-up equation #####
#############################
# ###### hyperbolic part ######
struct SegregationEquation <: TR.AbstractEquations{1, # number of spatial dimensions
1} # number of primary variables, i.e. scalar
end
TR.flux(u, orientation, equation::SegregationEquation) = F .* u .* (TR.SVector(1) - u)
TR.varnames(_, ::SegregationEquation) = ("phi",)
equation_hyperbolic = SegregationEquation()
# characteristics
function TR.max_abs_speed_naive(u_ll, u_rr, orientation::Integer,
::SegregationEquation)
advection_velocity_ll = F .* (1 - 2 * u_ll[1])
advection_velocity_rr = F .* (1 - 2 * u_rr[1])
return max(TR.abs(advection_velocity_ll), TR.abs(advection_velocity_rr))
end
TR.max_abs_speeds(u, equations::SegregationEquation) = TR.abs(F .* (1 - 2 .* u[1]))
# ###### parabolic part
struct Diffusion1D{E,T} <: TR.AbstractEquationsParabolic{1,1,TR.GradientVariablesConservative}
diffusivity::T
equation_hyperbolic::E
end
function TR.varnames(variable_mapping, equation_parabolic::Diffusion1D)
TR.varnames(variable_mapping, equation_parabolic.equation_hyperbolic)
end
function TR.flux(u, gradients, orientation::Integer,
equation_parabolic::Diffusion1D)
TR.@unpack diffusivity = equation_parabolic
return TR.SVector(diffusivity * gradients[1])
end
equation_parabolic = Diffusion1D(D, equation_hyperbolic)
#################################
# ##### Boundary conditions #####
#################################
# hyperbolic
boundary_conditions_hyperbolic = (x_neg=TR.BoundaryConditionDirichlet((x, t, equation) -> TR.SVector(1.0)),
x_pos=TR.BoundaryConditionDirichlet((x, t, equation) -> TR.SVector(0.0)))
# parabolic
struct BoundaryConditionConstantNeumann{T<:Real}
boundary_value::T
end
@inline function (boundary_condition::BoundaryConditionConstantNeumann)(flux_inner, u_inner,
normal::TR.AbstractVector,
x, t,
operator_type::TR.Divergence,
equations_parabolic::Diffusion1D)
return boundary_condition.boundary_value
end
@inline function (boundary_condition::BoundaryConditionConstantNeumann)(flux_inner, u_inner,
normal::TR.AbstractVector,
x, t,
operator_type::TR.Gradient,
equations_parabolic::Diffusion1D)
return flux_inner
end
boundary_conditions_parabolic = (x_neg=BoundaryConditionConstantNeumann(0.0),
x_pos=BoundaryConditionConstantNeumann(0.0))
###################################
# ##### Create discretization #####
###################################
mesh = TR.TreeMesh(-1.0, 1.0, # min/max coordinates
initial_refinement_level=6,
n_cells_max=10^4,
periodicity=false)
volume_flux = TR.flux_central
surface_flux = TR.flux_lax_friedrichs
basis = TR.LobattoLegendreBasis(3)
#_______ part specific to shock capturing method
indicator_sc = TR.IndicatorHennemannGassner(equation, basis,
alpha_max=0.5,
alpha_min=0.001,
alpha_smooth=true,
variable=TR.first)
volume_integral = TR.VolumeIntegralShockCapturingHG(indicator_sc;
volume_flux_dg=volume_flux,
volume_flux_fv=surface_flux)
#_______
solver = TR.DGSEM(basis, surface_flux, volume_integral)
# #### initial coniditions
initial_condition_plain(x, t, equation) = phi_0
# #### boundary condition
semi = TR.SemidiscretizationHyperbolicParabolic(mesh,
(equation_hyperbolic, equation_parabolic),
initial_condition_plain,
solver;
boundary_conditions=(boundary_conditions_hyperbolic, boundary_conditions_parabolic))
# ### Use temporal solver
# Create ODE problem with given time span
tspan = (0.0, 2)
ode = TR.semidiscretize(semi, tspan)
summary_callback = TR.SummaryCallback()
visualization_callback = TR.VisualizationCallback(solution_variables=TR.cons2cons, interval=5; clims=(0, 1), plot_data_creator=TR.PlotData1D)
stepsize_callback = TR.StepsizeCallback(cfl=0.5)
callbacks = TR.CallbackSet(summary_callback, visualization_callback, stepsize_callback)
stage_limiter! = TR.PositivityPreservingLimiterZhangShu(thresholds=(5e-6,), variables=(TR.first,))
# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks
sol = ODE.solve(ode, ODE.SSPRK33(stage_limiter!); dt=1.0, callback=callbacks);
|
I think you have @inline function (boundary_condition::BoundaryConditionConstantNeumann)(flux_inner, u_inner,
normal::TR.AbstractVector,
x, t,
operator_type::TR.Divergence,
equations_parabolic::Diffusion1D)
return boundary_condition.boundary_value
end
@inline function (boundary_condition::BoundaryConditionConstantNeumann)(flux_inner, u_inner,
normal::TR.AbstractVector,
x, t,
operator_type::TR.Gradient,
equations_parabolic::Diffusion1D)
return flux_inner
end |
Changing to @inline function (boundary_condition::BoundaryConditionConstantNeumann)(flux_inner, u_inner,
normal::AbstractVector,
x, t,
operator_type::TR.Divergence,
equations_parabolic::Diffusion1D)
return boundary_condition.boundary_value
end
@inline function (boundary_condition::BoundaryConditionConstantNeumann)(flux_inner, u_inner,
normal::AbstractVector,
x, t,
operator_type::TR.Gradient,
equations_parabolic::Diffusion1D)
return flux_inner
end results in the same error |
You're using a Cartesian |
The tutorial https://trixi-framework.github.io/Trixi.jl/stable/tutorials/adding_new_parabolic_terms/ also uses a What is the difference in my case? Edit: Oh, I noticed by looking at https://github.com/trixi-framework/Trixi.jl/blob/f586b0dc837cea99c1eff805065c3a1c5c39f550/src/solvers/dgsem_tree/dg_2d_parabolic.jl that
is valid just for the 1D case of cartesian I'm leaving the working 1D code here in case it can help someone in the future, and moving towards the 2D part :) 1D working Code (click to expand)import Trixi as TR
import OrdinaryDiffEq as ODE
import Plots
# #### parameter values
F = -1
phi_0 = 0.8
D = 0.1
#############################
# ##### Set-up equation #####
#############################
# ###### hyperbolic part ######
struct SegregationEquation <: TR.AbstractEquations{1, # number of spatial dimensions
1} # number of primary variables, i.e. scalar
end
TR.flux(u, orientation, equation::SegregationEquation) = F .* u .* (TR.SVector(1) - u)
TR.varnames(_, ::SegregationEquation) = ("phi",)
equation_hyperbolic = SegregationEquation()
# characteristics
function TR.max_abs_speed_naive(u_ll, u_rr, orientation::Integer,
::SegregationEquation)
advection_velocity_ll = F .* (1 - 2 * u_ll[1])
advection_velocity_rr = F .* (1 - 2 * u_rr[1])
return max(TR.abs(advection_velocity_ll), TR.abs(advection_velocity_rr))
end
TR.max_abs_speeds(u, equations::SegregationEquation) = TR.abs(F .* (1 - 2 .* u[1]))
# ###### parabolic part
struct Diffusion1D{E,T} <: TR.AbstractEquationsParabolic{1,1,TR.GradientVariablesConservative}
diffusivity::T
equation_hyperbolic::E
end
function TR.varnames(variable_mapping, equation_parabolic::Diffusion1D)
TR.varnames(variable_mapping, equation_parabolic.equation_hyperbolic)
end
function TR.flux(u, gradients, orientation::Integer,
equation_parabolic::Diffusion1D)
dudx = gradients
return equation_parabolic.diffusivity * dudx
end
equation_parabolic = Diffusion1D(D, equation_hyperbolic)
#################################
# ##### Boundary conditions #####
#################################
# hyperbolic
boundary_conditions_hyperbolic = (x_neg=TR.BoundaryConditionDirichlet((x, t, equation) -> TR.SVector(1.0)),
x_pos=TR.BoundaryConditionDirichlet((x, t, equation) -> TR.SVector(0.0)))
# parabolic
struct BoundaryConditionConstantNeumann{T<:Real}
boundary_value::T
end
@inline function (boundary_condition::BoundaryConditionConstantNeumann)(flux_inner, u_inner,
orientation::Integer,
direction::Integer,
x, t,
operator_type::TR.Divergence,
equation_parabolic::Diffusion1D)
return boundary_condition.boundary_value
end
@inline function (boundary_condition::BoundaryConditionConstantNeumann)(flux_inner, u_inner,
orientation::Integer,
direction::Integer,
x, t,
operator_type::TR.Gradient,
equation_parabolic::Diffusion1D)
return flux_inner
end
boundary_conditions_parabolic = (x_neg=BoundaryConditionConstantNeumann(0.0),
x_pos=BoundaryConditionConstantNeumann(0.0))
###################################
# ##### Create discretization #####
###################################
mesh = TR.TreeMesh(-1.0, 1.0, # min/max coordinates
initial_refinement_level=6,
n_cells_max=10^4,
periodicity=false)
volume_flux = TR.flux_central
surface_flux = TR.flux_lax_friedrichs
basis = TR.LobattoLegendreBasis(3)
#_______ part specific to shock capturing method
indicator_sc = TR.IndicatorHennemannGassner(equation_hyperbolic, basis,
alpha_max=0.5,
alpha_min=0.001,
alpha_smooth=true,
variable=TR.first)
volume_integral = TR.VolumeIntegralShockCapturingHG(indicator_sc;
volume_flux_dg=volume_flux,
volume_flux_fv=surface_flux)
#_______
solver = TR.DGSEM(basis, surface_flux, volume_integral)
# #### initial coniditions
initial_condition_plain(x, t, equation) = phi_0
# #### boundary condition
semi = TR.SemidiscretizationHyperbolicParabolic(mesh,
(equation_hyperbolic, equation_parabolic),
initial_condition_plain,
solver;
boundary_conditions=(boundary_conditions_hyperbolic, boundary_conditions_parabolic))
# ### Use temporal solver
# Create ODE problem with given time span
tspan = (0.0, 2)
ode = TR.semidiscretize(semi, tspan)
# summary_callback = TR.SummaryCallback()
# visualization_callback = TR.VisualizationCallback(solution_variables=TR.cons2cons, interval=5; clims=(0, 1), plot_data_creator=TR.PlotData1D)
# stepsize_callback = TR.StepsizeCallback(cfl=0.1)
# callbacks = TR.CallbackSet(summary_callback, visualization_callback, stepsize_callback)
# stage_limiter! = TR.PositivityPreservingLimiterZhangShu(thresholds=(5e-6,), variables=(TR.first,))
# # OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks
# sol = ODE.solve(ode, ODE.SSPRK33(stage_limiter!); dt=1.0, callback=callbacks);
summary_callback = TR.SummaryCallback()
visualization_callback = TR.VisualizationCallback(solution_variables=TR.cons2cons, interval=5; clims=(0, 1), plot_data_creator=TR.PlotData1D)
stepsize_callback = TR.StepsizeCallback(cfl=0.5)
callbacks = TR.CallbackSet(summary_callback, visualization_callback, stepsize_callback)
stage_limiter! = TR.PositivityPreservingLimiterZhangShu(thresholds=(5e-6,), variables=(TR.first,))
# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks
time_int_tol = 1.0e-6
sol = ODE.solve(ode, ODE.RDPK3SpFSAL49(stage_limiter!); callback=callbacks, abstol=time_int_tol, TR.ode_default_options()..., reltol=time_int_tol); |
Yeah... We should probably fix/improve this in the internals 👍 |
Okay, so I have moved to 2D and everything is going very well on cartesian meshes, created from For simplicity, I have removed the parabolic part and kept only the hyperbolic part. 2D hyperbolic code on cartesian P4estMesh (click to expand)import Trixi as TR
import OrdinaryDiffEq as ODE
import Plots
# #### parameter values
S = TR.SVector{2}([0.0, -1.0])
phi_0 = 0.8
#############################
# ##### Set-up equation #####
#############################
# ###### hyperbolic part ######
struct SegregationEquation{S} <: TR.AbstractEquations{2,1}
segregation::S
end
function TR.flux(u, normal_direction::AbstractVector, equation::SegregationEquation)
TR.@unpack segregation = equation
a = TR.dot(segregation, normal_direction) # segregation component in normal direction
# a = segregation[1] * normal_direction[1] + segregation[2] * normal_direction[2]
return a .* u .* (TR.SVector(1.0) - u)
end
# characteristics
function characteristics(u, S)
return S .* (1 - 2 .* u)
end
function TR.max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,
equation::SegregationEquation)
TR.@unpack segregation = equation
advection_velocity_ll = TR.dot(characteristics(u_ll[1], segregation), normal_direction)
advection_velocity_rr = TR.dot(characteristics(u_rr[1], segregation), normal_direction)
return max(TR.abs(advection_velocity_ll), TR.abs(advection_velocity_rr))
end
function TR.max_abs_speeds(u, equation::SegregationEquation)
TR.@unpack segregation = equation
return TR.abs.(characteristics(u[1], segregation))
end
TR.varnames(_, ::SegregationEquation) = ("phi",)
equation_hyperbolic = SegregationEquation(S)
#################################
# ##### Boundary conditions #####
#################################
# hyperbolic
function zeroflux_boundarycondition(u_inner, normal_direction::AbstractVector, x, t, surface_flux_function, equation::SegregationEquation)
return 0.0
end
boundary_conditions_hyperbolic = Dict(
:x_neg => zeroflux_boundarycondition,
:y_neg => zeroflux_boundarycondition,
:x_pos => zeroflux_boundarycondition,
:y_pos => zeroflux_boundarycondition,)
###################################
# ##### Create discretization #####
###################################
# #### create mesh
coordinates_min = (-1.0, -1.0) # minimum coordinates (min(x), min(y))
coordinates_max = (1.0, 1.0) # maximum coordinates (max(x), max(y))
trees_per_dimension = (4, 4)
mesh = TR.P4estMesh(trees_per_dimension,
polydeg=3, initial_refinement_level=5,
coordinates_min=coordinates_min, coordinates_max=coordinates_max,
periodicity=false)
# ### set-up discretization
volume_flux = TR.flux_central
surface_flux = TR.flux_lax_friedrichs
basis = TR.LobattoLegendreBasis(3)
#_______ part specific to shock capturing method
indicator_sc = TR.IndicatorHennemannGassner(equation_hyperbolic, basis,
alpha_max=0.5,
alpha_min=0.001,
alpha_smooth=true,
variable=TR.first)
volume_integral = TR.VolumeIntegralShockCapturingHG(indicator_sc;
volume_flux_dg=volume_flux,
volume_flux_fv=surface_flux)
#_______
solver = TR.DGSEM(basis, surface_flux, volume_integral)
# #### initial conditions
initial_condition_plain(x, t, equation) = phi_0
semi = TR.SemidiscretizationHyperbolic(mesh,
equation_hyperbolic,
initial_condition_plain,
solver;
boundary_conditions=boundary_conditions_hyperbolic)
# ### Use temporal solver
# Create ODE problem with given time span
tspan = (0.0, 2)
ode = TR.semidiscretize(semi, tspan)
summary_callback = TR.SummaryCallback()
visualization_callback = TR.VisualizationCallback(solution_variables=TR.cons2cons, interval=5; clims=(0, 1))
alive_callback = TR.AliveCallback(alive_interval=20)
stepsize_callback = TR.StepsizeCallback(cfl=0.5)
callbacks = TR.CallbackSet(summary_callback, visualization_callback, alive_callback, stepsize_callback)
stage_limiter! = TR.PositivityPreservingLimiterZhangShu(thresholds=(5e-6,), variables=(TR.first,))
# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks
time_int_tol = 1.0e-6
sol = ODE.solve(ode, ODE.RDPK3SpFSAL49(stage_limiter!); callback=callbacks, abstol=time_int_tol, TR.ode_default_options()..., reltol=time_int_tol)
# sol = ODE.solve(ode, ODE.SSPRK83(stage_limiter!); dt=1.0, callback=callbacks, abstol=time_int_tol, TR.ode_default_options()..., reltol=time_int_tol);
Two shocks are generated at the bottom and at the top and they travel to meet somewhere in the center. Code on P4estMesh imported from .inp file coming from gmsh (click to expand)import Trixi as TR
import OrdinaryDiffEq as ODE
import Plots
# #### parameter values
S = TR.SVector{2}([0.0, -1.0])
phi_0 = 0.8
#############################
# ##### Set-up equation #####
#############################
# ###### hyperbolic part ######
struct SegregationEquation{S} <: TR.AbstractEquations{2,1}
segregation::S
end
function TR.flux(u, normal_direction::AbstractVector, equation::SegregationEquation)
TR.@unpack segregation = equation
a = TR.dot(segregation, normal_direction) # segregation component in normal direction
# a = segregation[1] * normal_direction[1] + segregation[2] * normal_direction[2]
return a .* u .* (TR.SVector(1.0) - u)
end
# characteristics
function characteristics(u, S)
return S .* (1 - 2 .* u)
end
function TR.max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,
equation::SegregationEquation)
TR.@unpack segregation = equation
advection_velocity_ll = TR.dot(characteristics(u_ll[1], segregation), normal_direction)
advection_velocity_rr = TR.dot(characteristics(u_rr[1], segregation), normal_direction)
return max(TR.abs(advection_velocity_ll), TR.abs(advection_velocity_rr))
end
function TR.max_abs_speeds(u, equation::SegregationEquation)
TR.@unpack segregation = equation
return TR.abs.(characteristics(u[1], segregation))
end
TR.varnames(_, ::SegregationEquation) = ("phi",)
equation_hyperbolic = SegregationEquation(S)
#################################
# ##### Boundary conditions #####
#################################
# hyperbolic
function zeroflux_boundarycondition(u_inner, normal_direction::AbstractVector, x, t, surface_flux_function, equation::SegregationEquation)
return 0.0
end
boundary_conditions_hyperbolic = Dict(
:ExtBoundary => zeroflux_boundarycondition,
)
###################################
# ##### Create discretization #####
###################################
# #### load mesh
mesh_file = "numerical/test_trixi.jl/mesh_test.inp"
boundary_symbols = [:ExtBoundary]
mesh = TR.P4estMesh{2}(mesh_file, boundary_symbols=boundary_symbols, polydeg=3)
# ### set-up discretization
volume_flux = TR.flux_central
surface_flux = TR.flux_lax_friedrichs
basis = TR.LobattoLegendreBasis(3)
#_______ part specific to shock capturing method
indicator_sc = TR.IndicatorHennemannGassner(equation_hyperbolic, basis,
alpha_max=0.5,
alpha_min=0.001,
alpha_smooth=true,
variable=TR.first)
volume_integral = TR.VolumeIntegralShockCapturingHG(indicator_sc;
volume_flux_dg=volume_flux,
volume_flux_fv=surface_flux)
#_______
solver = TR.DGSEM(basis, surface_flux, volume_integral)
# #### initial coniditions
initial_condition_plain(x, t, equation) = phi_0
semi = TR.SemidiscretizationHyperbolic(mesh,
equation_hyperbolic,
initial_condition_plain,
solver;
boundary_conditions=boundary_conditions_hyperbolic)
# ### Use temporal solver
# Create ODE problem with given time span
tspan = (0.0, 2)
ode = TR.semidiscretize(semi, tspan)
summary_callback = TR.SummaryCallback()
visualization_callback = TR.VisualizationCallback(solution_variables=TR.cons2cons, interval=5; clims=(0, 1))
alive_callback = TR.AliveCallback(alive_interval=20)
stepsize_callback = TR.StepsizeCallback(cfl=0.5)
callbacks = TR.CallbackSet(summary_callback, visualization_callback, alive_callback, stepsize_callback)
stage_limiter! = TR.PositivityPreservingLimiterZhangShu(thresholds=(5e-6,), variables=(TR.first,))
# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks
time_int_tol = 1.0e-6
sol = ODE.solve(ode, ODE.RDPK3SpFSAL49(stage_limiter!); callback=callbacks, abstol=time_int_tol, TR.ode_default_options()..., reltol=time_int_tol)
# sol = ODE.solve(ode, ODE.SSPRK83(stage_limiter!); dt=1.0, callback=callbacks, abstol=time_int_tol, TR.ode_default_options()..., reltol=time_int_tol); The result that I do not know how to interpret is: The mesh is created using julia API from gmsh: Code to create .inp file from gmsh julia API (click to expand)import Gmsh: gmshxmin, xmax = -1, 1 gmsh.initialize() bot_left = gmsh.model.geo.addPoint(xmin, ymin, 0, typical_meshsize) left_line = gmsh.model.geo.addSpline([bot_left, top_left]) gmsh.model.geo.removeAllDuplicates() gmsh.model.geo.synchronize() gmsh.model.addPhysicalGroup(1, [left_line, right_line, bot_line, top_line], 1, "ExtBoundary") gmsh.option.setNumber("Mesh.SaveAll", 1) gmsh.model.mesh.generate(2) gmsh.fltk.run() Any suggestions on where to start looking? |
Can you just evaluate an initial condition and check what it looks like? |
I was not sure how to check the initial condition so I ran the code with Things seem to be a bit better when I decrease It suggests a wavy behaviour similar to what I had at the beginning in 1D without the shock-capturing method developing where the shape shock is (at the top, 0 to 1). This is confirmed by changing where the oscillating behaviour is now at the bottom. |
Since this behavior is near the boundary, what happens if you use the Gmsh BCs with the Cartesian P4estMesh? |
Sorry, I do not understand what you mean by this. The boundary condition function is the same for both, and also the same everywhere around the domain. The only things that changes from this point is how this function is passed to the discretization: Cartesian P4estMesh: boundary_conditions_hyperbolic = Dict(
:x_neg => zeroflux_boundarycondition,
:y_neg => zeroflux_boundarycondition,
:x_pos => zeroflux_boundarycondition,
:y_pos => zeroflux_boundarycondition) GMSH: boundary_conditions_hyperbolic = Dict(
:ExtBoundary => zeroflux_boundarycondition,
) |
Sorry, I read your code incorrectly. I am trying to see if this is a mathematical issue or a bug. Is it possible to generate a uniform mesh using Gmsh to see if it recovers the Cartesian P4estMesh case? |
No worries. It is indeed possible ! Computing the structured cartesian grid using Code for generating structured grid with gmsh (click to expand)import Gmsh: gmsh
xmin, xmax = -1, 1
ymin, ymax = -1, 1
n_width = 60
typical_meshsize = (xmax - xmin) / n_width
gmsh.initialize()
gmsh.model.add("domain")
bot_left = gmsh.model.geo.addPoint(xmin, ymin, 0, typical_meshsize)
bot_right = gmsh.model.geo.addPoint(xmax, ymin, 0, typical_meshsize)
top_right = gmsh.model.geo.addPoint(xmax, ymax, 0, typical_meshsize)
top_left = gmsh.model.geo.addPoint(xmin, ymax, 0, typical_meshsize)
left_line = gmsh.model.geo.addLine(bot_left, top_left)
top_line = gmsh.model.geo.addLine(top_left, top_right)
right_line = gmsh.model.geo.addLine(top_right, bot_right)
bot_line = gmsh.model.geo.addLine(bot_right, bot_left)
gmsh.model.geo.removeAllDuplicates()
gmsh.model.geo.addCurveLoop([left_line, top_line, right_line, bot_line], 1)
surface = gmsh.model.geo.addPlaneSurface([1], 1)
# #### for structured mesh
gmsh.model.geo.mesh.setTransfiniteCurve(left_line, n_width)
gmsh.model.geo.mesh.setTransfiniteCurve(right_line, n_width)
gmsh.model.geo.mesh.setTransfiniteCurve(top_line, n_width)
gmsh.model.geo.mesh.setTransfiniteCurve(bot_line, n_width)
# gmsh.model.geo.mesh.setTransfiniteSurface(surface, "Left", [1, 2, 3, 4])
gmsh.model.geo.mesh.setTransfiniteSurface(surface, "Left")
gmsh.model.geo.synchronize()
gmsh.model.mesh.setRecombine(2, 1) # Recombine triangles into quadrilaterals
gmsh.model.addPhysicalGroup(1, [left_line, right_line, bot_line, top_line], 1, "ExtBoundary")
gmsh.option.setNumber("Mesh.SaveAll", 1)
gmsh.option.setNumber("Mesh.SaveGroupsOfElements", 1)
gmsh.option.setNumber("Mesh.SaveGroupsOfNodes", 1) # Important for p4est and Trixi
gmsh.model.mesh.generate(2)
gmsh.fltk.run()
gmsh.write("mesh_test.inp")
gmsh.finalize()
For comparison, the result when building the cartesian mesh directly from p4est is the following, as expected: EDIT:
|
That's quite odd; you'd expect them to be the same if things were processed correctly. I'm not sure why this would be the case, however. Naive question - is the BC actually being used in both cases? E.g., if you put a print statement out inside is it being called? |
Agreed ... See my edit on my previous answer, I might have done something wrong with the meshes ...
Yes, I just checked adding a print statement and it's called in both cases at each timestep. |
Your edits on your previous answer do look a bit odd. I can't say for sure, but it almost looks like |
I just loaded the foil mesh from https://github.com/trixi-framework/Trixi.jl/blob/main/examples/p4est_2d_dgsem/elixir_euler_NACA6412airfoil_mach2.jl and it gives pretty similar results to the mesh I generated using
The only difference is that the actual boundary names |
@DanielDoehring You have worked with this type of setup, haven't you? |
Are you supplying the boundary symbols to the mesh constructor, i.e.,
? |
Yes, as boundary_symbols = [:ExtBoundary]
mesh = TR.P4estMesh{2}(mesh_file, boundary_symbols=boundary_symbols, polydeg=polydeg) I just tested to actually run my code using the .inp file from the foil from the example you linked, and the code runs as expected: So the problem comes from my .inp files or how I load them. I compared the .inp files I generated myself to the one of the foil in the example, and they look very similar ... |
So the mesh you generate should be fine. One thing I can imagine is that your BCs assume something on the mesh which is true for the p4est generated one but not for the imported one. You could check this by using one of the tested, known to work BCs. |
Okay, so I figured it out. I was basically defining the outer domain by following the corners clockwise instead of anticlockwise/trigo. It looks like if you start from
Then this would be right:
but this would be wrong:
It does not prevent the generation of the mesh by gmsh, and the nodes are simply ordered differently in the file. |
Ah, that makes sense. I'm not sure of the |
Yeah that makes sense, |
Can we detect this when reading in the mesh? |
Hi all,
I'm moving here the discussion started on the Julia forum as it's easier to write here.
Problem Summary
I am trying to solve the following equation:
where:
The domain is bounded by two parabolas (red and brown) :
and the black lines represent streamlines of the advection field
The boundary conditions are non-flux through the boundaries. As the vector field also respects this condition, it becomes:
where$\boldsymbol{n} = (n_{x}, n_{z})$ is a vector normal to the boundary.
Workflow
As I am new to Trixi.jl, I will try to follow the current plan:
Current state
I am currently trying to implement the 1D hyperbolic equation$\frac{\partial \phi}{\partial t} + \frac{\partial}{\partial z} (F \phi(1-\phi) = 0$ .
Here is the code I've come up with based on tutorials and examples:
Expected results: Two shocks should be generated at the top and bottom boundaries, travel and meet somewhere inside the domain.
Current issues:
Bonus question: In the past, I have successfully used Central upwind semi-discrete schemes to solve this kind of hyperbolic equations, such as described in Kurganov and Tadmor (2000), New High-Resolution Central Schemes
for Nonlinear Conservation Laws and Convection–Diffusion Equations. However, while I am far from being a specialist of numerical methods to solve PDE, I feel like their method could be used in Trixi by using the way they compute the flux as surface flux. Is that true ? More generally, could you briefly explain how their scheme relates to the methods used in Trixi?
The text was updated successfully, but these errors were encountered: