Skip to content

Commit d867d88

Browse files
committed
Add UpwindBiasedGradient operator
1 parent a7bb4ec commit d867d88

File tree

2 files changed

+144
-0
lines changed

2 files changed

+144
-0
lines changed

src/Operators/finitedifference.jl

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3088,6 +3088,107 @@ Base.@propagate_inbounds function stencil_right_boundary(
30883088
)
30893089
end
30903090

3091+
"""
3092+
UG = UpwindBiasedGradient()
3093+
UG.(v, θ)
3094+
3095+
Compute the vertical gradient of the field `θ` by upwinding it according to the
3096+
direction of a vector field `v` on the same space.
3097+
3098+
More precisely, the gradient is computed based on the sign of the 3rd
3099+
contravariant component of `v`:
3100+
```math
3101+
UG(\\boldsymbol{v}, θ)[i] = \\begin{cases}
3102+
G(LB(θ))[i] \\textrm{, if } v^3[i] > 0 \\\\
3103+
G(RB(θ))[i] \\textrm{, if } v^3[i] < 0
3104+
\\end{cases}
3105+
```
3106+
where `G` is a gradient operator and `LB`/`RB` are left/right-bias operators.
3107+
When `θ` and `v` are located on centers, `G = GradientF2C()`,
3108+
`LB = LeftBiasedC2F()`, and `RB = RightBiasedC2F()`. When they are located on
3109+
faces, `G = GradientC2F()`, `LB = LeftBiasedF2C()`, and `RB = RightBiasedF2C()`.
3110+
3111+
No boundary conditions are currently supported. The default behavior on the left
3112+
boundary is
3113+
```math
3114+
UG(\\boldsymbol{v}, θ)[i_min] = G(RB(θ))[i_min]
3115+
```
3116+
and the default behavior on the right boundary is
3117+
```math
3118+
UG(\\boldsymbol{v}, θ)[i_max] = G(LB(θ))[i_max]
3119+
```
3120+
"""
3121+
struct UpwindBiasedGradient{BCS} <: FiniteDifferenceOperator
3122+
bcs::BCS
3123+
end
3124+
function UpwindBiasedGradient(; kwargs...)
3125+
assert_no_bcs("UpwindBiasedGradient", kwargs)
3126+
return UpwindBiasedGradient(NamedTuple())
3127+
end
3128+
3129+
return_eltype(::UpwindBiasedGradient, velocity, arg) =
3130+
Geometry.gradient_result_type(Val((3,)), eltype(arg))
3131+
3132+
return_space(
3133+
::UpwindBiasedGradient,
3134+
velocity_space::AllCenterFiniteDifferenceSpace,
3135+
arg_space::AllCenterFiniteDifferenceSpace,
3136+
) = arg_space
3137+
return_space(
3138+
::UpwindBiasedGradient,
3139+
velocity_space::AllFaceFiniteDifferenceSpace,
3140+
arg_space::AllFaceFiniteDifferenceSpace,
3141+
) = arg_space
3142+
3143+
stencil_interior_width(::UpwindBiasedGradient, velocity, arg) =
3144+
((0, 0), (-1, 1))
3145+
Base.@propagate_inbounds function stencil_interior(
3146+
::UpwindBiasedGradient,
3147+
space,
3148+
idx,
3149+
hidx,
3150+
velocity,
3151+
arg,
3152+
)
3153+
FT = Spaces.undertype(space)
3154+
a⁺ = getidx(space, arg, idx + 1, hidx)
3155+
a = getidx(space, arg, idx, hidx)
3156+
a⁻ = getidx(space, arg, idx - 1, hidx)
3157+
v = Geometry.contravariant3(
3158+
getidx(space, velocity, idx, hidx),
3159+
Geometry.LocalGeometry(space, idx, hidx),
3160+
)
3161+
∂a∂ξ₃_times_2 = (1 - sign(v)) a⁺ + 2 * sign(v) a - (1 + sign(v)) a⁻
3162+
return Geometry.Covariant3Vector(FT(1) / 2) ∂a∂ξ₃_times_2
3163+
end
3164+
boundary_width(::UpwindBiasedGradient, ::AbstractBoundaryCondition) = 1
3165+
3166+
Base.@propagate_inbounds function stencil_left_boundary(
3167+
::UpwindBiasedGradient,
3168+
::NullBoundaryCondition,
3169+
space,
3170+
idx,
3171+
hidx,
3172+
arg,
3173+
)
3174+
@assert idx == left_face_boundary_idx(space)
3175+
a⁺ = getidx(space, arg, idx + 1, hidx)
3176+
a = getidx(space, arg, idx, hidx)
3177+
return Geometry.Covariant3Vector(1) (a⁺ a)
3178+
end
3179+
Base.@propagate_inbounds function stencil_right_boundary(
3180+
::UpwindBiasedGradient,
3181+
::NullBoundaryCondition,
3182+
space,
3183+
idx,
3184+
hidx,
3185+
arg,
3186+
)
3187+
@assert idx == right_face_boundary_idx(space)
3188+
a = getidx(space, arg, idx, hidx)
3189+
a⁻ = getidx(space, arg, idx - 1, hidx)
3190+
return Geometry.Covariant3Vector(1) (a a⁻)
3191+
end
30913192

30923193
abstract type DivergenceOperator <: FiniteDifferenceOperator end
30933194
return_eltype(::DivergenceOperator, arg) =

test/Operators/finitedifference/convergence_column.jl

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,49 @@ end
409409
@test conv_curl_sin_f[1] conv_curl_sin_f[2] conv_curl_sin_f[3]
410410
end
411411

412+
@testset "UpwindBiasedGradient on (uniform) periodic mesh, random w" begin
413+
FT = Float64
414+
device = ClimaComms.device()
415+
416+
n_elems_seq = 2 .^ (5, 6, 7, 8)
417+
center_errors = zeros(FT, length(n_elems_seq))
418+
face_errors = zeros(FT, length(n_elems_seq))
419+
Δh = zeros(FT, length(n_elems_seq))
420+
421+
for (k, n) in enumerate(n_elems_seq)
422+
domain = Domains.IntervalDomain(
423+
Geometry.ZPoint{FT}(-pi),
424+
Geometry.ZPoint{FT}(pi);
425+
periodic = true,
426+
)
427+
mesh = Meshes.IntervalMesh(domain; nelems = n)
428+
429+
center_space = Spaces.CenterFiniteDifferenceSpace(device, mesh)
430+
face_space = Spaces.FaceFiniteDifferenceSpace(center_space)
431+
432+
ᶜw = Geometry.WVector.(map(_ -> rand(), ones(center_space)))
433+
ᶠw = Geometry.WVector.(map(_ -> rand(), ones(face_space)))
434+
435+
ᶜz = Fields.coordinate_field(center_space).z
436+
ᶠz = Fields.coordinate_field(face_space).z
437+
438+
upwind_biased_grad = Operators.UpwindBiasedGradient()
439+
ᶜ∇sinz = Geometry.WVector.(upwind_biased_grad.(ᶜw, sin.(ᶜz)))
440+
ᶠ∇sinz = Geometry.WVector.(upwind_biased_grad.(ᶠw, sin.(ᶠz)))
441+
442+
center_errors[k] = norm(ᶜ∇sinz .- Geometry.WVector.(cos.(ᶜz)))
443+
face_errors[k] = norm(ᶠ∇sinz .- Geometry.WVector.(cos.(ᶠz)))
444+
Δh[k] = Spaces.local_geometry_data(face_space).J[vindex(1)]
445+
end
446+
447+
@test all(error -> error < 0.1, center_errors)
448+
@test all(error -> error < 0.1, face_errors)
449+
450+
center_convergence_rates = convergence_rate(center_errors, Δh)
451+
face_convergence_rates = convergence_rate(face_errors, Δh)
452+
@test all(rate -> isapprox(rate, 1; atol = 0.01), center_convergence_rates)
453+
@test all(rate -> isapprox(rate, 1; atol = 0.01), face_convergence_rates)
454+
end
412455

413456
@testset "Upwind3rdOrderBiasedProductC2F + DivergenceF2C on (uniform) periodic mesh, constant w" begin
414457
FT = Float64

0 commit comments

Comments
 (0)