|
1 | 1 | using Oceananigans.Operators: Δxᶠᶜᶜ, Δyᶜᶠᶜ, Δzᶜᶜᶠ, Ax_qᶠᶜᶜ, Ay_qᶜᶠᶜ, Az_qᶜᶜᶠ
|
| 2 | +using Oceananigans: defaults |
2 | 3 |
|
3 | 4 | """
|
4 | 5 | PerturbationAdvection
|
@@ -30,24 +31,41 @@ where Ũ = U Δt / Δx, then uⁿ⁺¹ is:
|
30 | 31 | where τ̃ = Δt/τ.
|
31 | 32 |
|
32 | 33 | The same operation can be repeated for left boundaries.
|
| 34 | +
|
| 35 | +The relaxation timescale ``τ`` can be set to different values depending on whether the |
| 36 | +``U`` points in or out of the domain (`inflow_timescale`/`outflow_timescale`). Since the |
| 37 | +scheme is only valid when the flow is directed out of the domain the boundary condition |
| 38 | +falls back to relaxation to the prescribed value. By default this happens instantly but |
| 39 | +if the direction varies this may not be preferable. It is beneficial to relax the outflow |
| 40 | +(i.e. non-zero `outflow_timescale`) to reduce the shock when the flow changes direction |
| 41 | +to point into the domain. |
| 42 | +
|
| 43 | +The ideal value of the timescales probably depend on the grid spacing and details of the |
| 44 | +boundary flow. |
33 | 45 | """
|
34 |
| -struct PerturbationAdvection{VT, FT} |
35 |
| - backward_step :: VT |
| 46 | +struct PerturbationAdvection{FT} |
36 | 47 | inflow_timescale :: FT
|
37 | 48 | outflow_timescale :: FT
|
38 | 49 | end
|
39 | 50 |
|
40 | 51 | Adapt.adapt_structure(to, pe::PerturbationAdvection) =
|
41 |
| - PerturbationAdvection(adapt(to, pe.backward_step), |
42 |
| - adapt(to, pe.inflow_timescale), |
| 52 | + PerturbationAdvection(adapt(to, pe.inflow_timescale), |
43 | 53 | adapt(to, pe.outflow_timescale))
|
44 | 54 |
|
45 |
| -function PerturbationAdvectionOpenBoundaryCondition(val, FT = Float64; |
46 |
| - backward_step = true, |
| 55 | +""" |
| 56 | + PerturbationAdvectionOpenBoundaryCondition(val, FT = defaults.FloatType; |
| 57 | + outflow_timescale = Inf, |
| 58 | + inflow_timescale = 0, kwargs...) |
| 59 | +
|
| 60 | +Creates a `PerturbationAdvectionOpenBoundaryCondition` with a given exterior value `val`, to which |
| 61 | +the flow is forced with an `outflow_timescale` for outflow and `inflow_timescale` for inflow. For |
| 62 | +details about this method, refer to the docstring for `PerturbationAdvection`. |
| 63 | +""" |
| 64 | +function PerturbationAdvectionOpenBoundaryCondition(val, FT = defaults.FloatType; |
47 | 65 | outflow_timescale = Inf,
|
48 |
| - inflow_timescale = 300.0, kwargs...) |
| 66 | + inflow_timescale = 0, kwargs...) |
49 | 67 |
|
50 |
| - classification = Open(PerturbationAdvection(Val(backward_step), inflow_timescale, outflow_timescale)) |
| 68 | + classification = Open(PerturbationAdvection(inflow_timescale, outflow_timescale)) |
51 | 69 |
|
52 | 70 | @warn "`PerturbationAdvection` open boundaries matching scheme is experimental and un-tested/validated"
|
53 | 71 |
|
|
56 | 74 |
|
57 | 75 | const PAOBC = BoundaryCondition{<:Open{<:PerturbationAdvection}}
|
58 | 76 |
|
59 |
| -const BPAOBC = BoundaryCondition{<:Open{<:PerturbationAdvection{Val{true}}}} |
60 |
| -const FPAOBC = BoundaryCondition{<:Open{<:PerturbationAdvection{Val{false}}}} |
61 |
| - |
62 |
| -@inline function step_right_boundary!(bc::BPAOBC, l, m, boundary_indices, boundary_adjacent_indices, |
| 77 | +@inline function step_right_boundary!(bc::PAOBC, l, m, boundary_indices, boundary_adjacent_indices, |
63 | 78 | grid, u, clock, model_fields, ΔX)
|
64 | 79 | Δt = clock.last_stage_Δt
|
65 |
| - |
66 | 80 | Δt = ifelse(isinf(Δt), 0, Δt)
|
67 | 81 |
|
68 | 82 | ūⁿ⁺¹ = getbc(bc, l, m, grid, clock, model_fields)
|
69 |
| - |
70 | 83 | uᵢⁿ = @inbounds getindex(u, boundary_indices...)
|
71 | 84 | uᵢ₋₁ⁿ⁺¹ = @inbounds getindex(u, boundary_adjacent_indices...)
|
72 |
| - |
73 | 85 | U = max(0, min(1, Δt / ΔX * ūⁿ⁺¹))
|
74 | 86 |
|
75 | 87 | pa = bc.classification.matching_scheme
|
76 |
| - |
77 | 88 | τ = ifelse(ūⁿ⁺¹ >= 0, pa.outflow_timescale, pa.inflow_timescale)
|
78 |
| - |
79 | 89 | τ̃ = Δt / τ
|
80 | 90 |
|
81 |
| - uᵢⁿ⁺¹ = (uᵢⁿ + U * uᵢ₋₁ⁿ⁺¹ + ūⁿ⁺¹ * τ̃) / (1 + τ̃ + U) |
| 91 | + relaxed_uᵢⁿ⁺¹ = (uᵢⁿ + U * uᵢ₋₁ⁿ⁺¹ + ūⁿ⁺¹ * τ̃) / (1 + τ̃ + U) |
| 92 | + uᵢⁿ⁺¹ = ifelse(τ == 0, ūⁿ⁺¹, relaxed_uᵢⁿ⁺¹) |
82 | 93 |
|
83 | 94 | @inbounds setindex!(u, uᵢⁿ⁺¹, boundary_indices...)
|
84 | 95 |
|
85 | 96 | return nothing
|
86 | 97 | end
|
87 | 98 |
|
88 |
| -@inline function step_left_boundary!(bc::BPAOBC, l, m, boundary_indices, boundary_adjacent_indices, boundary_secret_storage_indices, |
| 99 | +@inline function step_left_boundary!(bc::PAOBC, l, m, boundary_indices, boundary_adjacent_indices, boundary_secret_storage_indices, |
89 | 100 | grid, u, clock, model_fields, ΔX)
|
90 | 101 | Δt = clock.last_stage_Δt
|
91 |
| - |
92 | 102 | Δt = ifelse(isinf(Δt), 0, Δt)
|
93 | 103 |
|
94 | 104 | ūⁿ⁺¹ = getbc(bc, l, m, grid, clock, model_fields)
|
95 |
| - |
96 | 105 | uᵢⁿ = @inbounds getindex(u, boundary_secret_storage_indices...)
|
97 | 106 | uᵢ₋₁ⁿ⁺¹ = @inbounds getindex(u, boundary_adjacent_indices...)
|
98 |
| - |
99 | 107 | U = min(0, max(-1, Δt / ΔX * ūⁿ⁺¹))
|
100 | 108 |
|
101 | 109 | pa = bc.classification.matching_scheme
|
102 |
| - |
103 | 110 | τ = ifelse(ūⁿ⁺¹ <= 0, pa.outflow_timescale, pa.inflow_timescale)
|
104 |
| - |
105 |
| - τ̃ = Δt / τ |
106 |
| - |
107 |
| - u₁ⁿ⁺¹ = (uᵢⁿ - U * uᵢ₋₁ⁿ⁺¹ + ūⁿ⁺¹ * τ̃) / (1 + τ̃ - U) |
108 |
| - |
109 |
| - @inbounds setindex!(u, u₁ⁿ⁺¹, boundary_indices...) |
110 |
| - @inbounds setindex!(u, u₁ⁿ⁺¹, boundary_secret_storage_indices...) |
111 |
| - |
112 |
| - return nothing |
113 |
| -end |
114 |
| - |
115 |
| - |
116 |
| -@inline function step_right_boundary!(bc::FPAOBC, l, m, boundary_indices, boundary_adjacent_indices, |
117 |
| - grid, u, clock, model_fields, ΔX) |
118 |
| - Δt = clock.last_stage_Δt |
119 |
| - Δt = ifelse(isinf(Δt), 0, Δt) |
120 |
| - |
121 |
| - ūⁿ⁺¹ = getbc(bc, l, m, grid, clock, model_fields) |
122 |
| - uᵢⁿ = @inbounds getindex(u, boundary_indices...) |
123 |
| - uᵢ₋₁ⁿ⁺¹ = @inbounds getindex(u, boundary_adjacent_indices...) |
124 |
| - |
125 |
| - U = max(0, min(1, Δt / ΔX * ūⁿ⁺¹)) |
126 |
| - pa = bc.classification.matching_scheme |
127 |
| - |
128 |
| - τ = ifelse(ūⁿ⁺¹ >= 0, pa.outflow_timescale, pa.inflow_timescale) |
129 | 111 | τ̃ = Δt / τ
|
130 | 112 |
|
131 |
| - uᵢⁿ⁺¹ = uᵢⁿ + U * (uᵢ₋₁ⁿ⁺¹ - ūⁿ⁺¹) + (ūⁿ⁺¹ - uᵢⁿ) * τ̃ |
132 |
| - @inbounds setindex!(u, uᵢⁿ⁺¹, boundary_indices...) |
133 |
| - |
134 |
| - return nothing |
135 |
| -end |
136 |
| - |
137 |
| -@inline function step_left_boundary!(bc::FPAOBC, l, m, boundary_indices, boundary_adjacent_indices, boundary_secret_storage_indices, |
138 |
| - grid, u, clock, model_fields, ΔX) |
139 |
| - Δt = clock.last_stage_Δt |
140 |
| - Δt = ifelse(isinf(Δt), 0, Δt) |
141 |
| - |
142 |
| - ūⁿ⁺¹ = getbc(bc, l, m, grid, clock, model_fields) |
143 |
| - uᵢⁿ = @inbounds getindex(u, boundary_secret_storage_indices...) |
144 |
| - uᵢ₋₁ⁿ⁺¹ = @inbounds getindex(u, boundary_adjacent_indices...) |
145 |
| - |
146 |
| - U = min(0, max(-1, Δt / ΔX * ūⁿ⁺¹)) |
147 |
| - pa = bc.classification.matching_scheme |
148 |
| - |
149 |
| - τ = ifelse(ūⁿ⁺¹ <= 0, pa.outflow_timescale, pa.inflow_timescale) |
150 |
| - τ̃ = Δt / τ |
| 113 | + relaxed_u₁ⁿ⁺¹ = (uᵢⁿ - U * uᵢ₋₁ⁿ⁺¹ + ūⁿ⁺¹ * τ̃) / (1 + τ̃ - U) |
| 114 | + u₁ⁿ⁺¹ = ifelse(τ == 0, ūⁿ⁺¹, relaxed_u₁ⁿ⁺¹) |
151 | 115 |
|
152 |
| - u₁ⁿ⁺¹ = uᵢⁿ - U * (uᵢ₋₁ⁿ⁺¹ - ūⁿ⁺¹) + (ūⁿ⁺¹ - uᵢⁿ) * τ̃ |
153 | 116 | @inbounds setindex!(u, u₁ⁿ⁺¹, boundary_indices...)
|
154 | 117 | @inbounds setindex!(u, u₁ⁿ⁺¹, boundary_secret_storage_indices...)
|
155 | 118 |
|
|
0 commit comments