|
89 | 89 | end |
90 | 90 |
|
91 | 91 | @testset "compute_strain_rate (c.f. analytical function)" begin |
92 | | - # Test compute_strain_rate_face |
| 92 | + # Test compute_strain_rate_face_vertical |
93 | 93 | (; helem, cent_space, face_space) = get_cartesian_spaces() |
94 | 94 | ccoords, fcoords = get_coords(cent_space, face_space) |
95 | 95 | FT = eltype(ccoords.x) |
|
118 | 118 | ᶠu = @. UVW(Geometry.UVector(ᶠu)) + |
119 | 119 | UVW(Geometry.VVector(ᶠv)) + |
120 | 120 | UVW(Geometry.WVector(ᶠw)) |
121 | | - ᶜϵ .= CA.compute_strain_rate_center(Geometry.Covariant123Vector.(ᶠu)) |
122 | | - ᶠϵ .= CA.compute_strain_rate_face(Geometry.Covariant123Vector.(ᶜu)) |
| 121 | + ᶜϵ .= CA.compute_strain_rate_center_vertical(Geometry.Covariant123Vector.(ᶠu)) |
| 122 | + ᶠϵ .= CA.compute_strain_rate_face_vertical(Geometry.Covariant123Vector.(ᶜu)) |
123 | 123 |
|
124 | 124 | # Center valued strain rate |
125 | 125 | @test ᶜϵ.components.data.:1 == ᶜϵ.components.data.:1 .* FT(0) |
|
163 | 163 | end |
164 | 164 | end |
165 | 165 |
|
| 166 | +@testset "compute_full_strain_rate (consistency and symmetry)" begin |
| 167 | + (; helem, cent_space, face_space) = get_cartesian_spaces() |
| 168 | + ccoords, fcoords = get_coords(cent_space, face_space) |
| 169 | + FT = eltype(ccoords.x) |
| 170 | + UVW = Geometry.UVWVector |
| 171 | + C123 = Geometry.Covariant123Vector |
| 172 | + UVec = Geometry.UVector |
| 173 | + VVec = Geometry.VVector |
| 174 | + WVec = Geometry.WVector |
| 175 | + ᶜgradᵥ = Operators.GradientF2C() |
| 176 | + gradₕ = Operators.Gradient() |
| 177 | + |
| 178 | + |
| 179 | + # Alloc scratch space for 3x3 tensor fields |
| 180 | + u₀ = UVW(FT(0), FT(0), FT(0)) |
| 181 | + ᶜε = Fields.Field(typeof(u₀ * u₀'), cent_space) |
| 182 | + ᶠε = Fields.Field(typeof(u₀ * u₀'), face_space) |
| 183 | + ᶜε_ref = Fields.Field(typeof(u₀ * u₀'), cent_space) |
| 184 | + ᶠε_ref = Fields.Field(typeof(u₀ * u₀'), face_space) |
| 185 | + |
| 186 | + # Velocity fields |
| 187 | + u, v, w = taylor_green_ic(ccoords) |
| 188 | + ᶠu, ᶠv, ᶠw = taylor_green_ic(fcoords) |
| 189 | + ᶜu = @. UVW(UVec(u)) + UVW(VVec(v)) + UVW(WVec(w)) |
| 190 | + ᶠu_vec = @. UVW(UVec(ᶠu)) + UVW(VVec(ᶠv)) + UVW(WVec(ᶠw)) |
| 191 | + |
| 192 | + # Compute using API under test |
| 193 | + CA.compute_strain_rate_center_full!(ᶜε, ᶜu, ᶠu_vec) |
| 194 | + CA.compute_strain_rate_face_full!(ᶠε, ᶜu, ᶠu_vec) |
| 195 | + |
| 196 | + # Build reference tensors explicitly (projection + symmetrization) |
| 197 | + axis_uvw = (Geometry.UVWAxis(),) |
| 198 | + # Center reference |
| 199 | + @. ᶜε_ref = Geometry.project(axis_uvw, ᶜgradᵥ(ᶠu_vec)) |
| 200 | + @. ᶜε_ref += Geometry.project(axis_uvw, gradₕ(ᶜu)) |
| 201 | + @. ᶜε_ref = (ᶜε_ref + adjoint(ᶜε_ref)) / 2 |
| 202 | + |
| 203 | + # Face reference (construct vertical gradient with the same BCs) |
| 204 | + ∇ᵥuvw_boundary = Geometry.outer(WVec(0), UVW(0, 0, 0)) |
| 205 | + ∇bc = Operators.SetGradient(∇ᵥuvw_boundary) |
| 206 | + ᶠgradᵥ = Operators.GradientC2F(bottom = ∇bc, top = ∇bc) |
| 207 | + @. ᶠε_ref = Geometry.project(axis_uvw, ᶠgradᵥ(ᶜu)) |
| 208 | + @. ᶠε_ref += Geometry.project(axis_uvw, gradₕ(ᶠu_vec)) |
| 209 | + @. ᶠε_ref = (ᶠε_ref + adjoint(ᶠε_ref)) / 2 |
| 210 | + |
| 211 | + # Symmetry checks (independent of reference) |
| 212 | + @test ᶜε.components.data.:2 == ᶜε.components.data.:4 |
| 213 | + @test ᶜε.components.data.:3 == ᶜε.components.data.:7 |
| 214 | + @test ᶜε.components.data.:6 == ᶜε.components.data.:8 |
| 215 | + @test ᶠε.components.data.:2 == ᶠε.components.data.:4 |
| 216 | + @test ᶠε.components.data.:3 == ᶠε.components.data.:7 |
| 217 | + @test ᶠε.components.data.:6 == ᶠε.components.data.:8 |
| 218 | + |
| 219 | + # Consistency with reference (component-wise, tight tolerance) |
| 220 | + tol = sqrt(eps(FT)) |
| 221 | + @test maximum(abs, ᶜε.components.data.:1 .- ᶜε_ref.components.data.:1) < tol |
| 222 | + @test maximum(abs, ᶜε.components.data.:2 .- ᶜε_ref.components.data.:2) < tol |
| 223 | + @test maximum(abs, ᶜε.components.data.:3 .- ᶜε_ref.components.data.:3) < tol |
| 224 | + @test maximum(abs, ᶜε.components.data.:4 .- ᶜε_ref.components.data.:4) < tol |
| 225 | + @test maximum(abs, ᶜε.components.data.:5 .- ᶜε_ref.components.data.:5) < tol |
| 226 | + @test maximum(abs, ᶜε.components.data.:6 .- ᶜε_ref.components.data.:6) < tol |
| 227 | + @test maximum(abs, ᶜε.components.data.:7 .- ᶜε_ref.components.data.:7) < tol |
| 228 | + @test maximum(abs, ᶜε.components.data.:8 .- ᶜε_ref.components.data.:8) < tol |
| 229 | + @test maximum(abs, ᶜε.components.data.:9 .- ᶜε_ref.components.data.:9) < tol |
| 230 | + |
| 231 | + @test maximum(abs, ᶠε.components.data.:1 .- ᶠε_ref.components.data.:1) < tol |
| 232 | + @test maximum(abs, ᶠε.components.data.:2 .- ᶠε_ref.components.data.:2) < tol |
| 233 | + @test maximum(abs, ᶠε.components.data.:3 .- ᶠε_ref.components.data.:3) < tol |
| 234 | + @test maximum(abs, ᶠε.components.data.:4 .- ᶠε_ref.components.data.:4) < tol |
| 235 | + @test maximum(abs, ᶠε.components.data.:5 .- ᶠε_ref.components.data.:5) < tol |
| 236 | + @test maximum(abs, ᶠε.components.data.:6 .- ᶠε_ref.components.data.:6) < tol |
| 237 | + @test maximum(abs, ᶠε.components.data.:7 .- ᶠε_ref.components.data.:7) < tol |
| 238 | + @test maximum(abs, ᶠε.components.data.:8 .- ᶠε_ref.components.data.:8) < tol |
| 239 | + @test maximum(abs, ᶠε.components.data.:9 .- ᶠε_ref.components.data.:9) < tol |
| 240 | + |
| 241 | + # Boundary behavior (face): match reference at first and last vertical levels per element |
| 242 | + for elem_id in 1:helem |
| 243 | + @test maximum( |
| 244 | + abs, |
| 245 | + Fields.field_values(Fields.slab(ᶠε.components.data.:3, 1, elem_id)) .- |
| 246 | + Fields.field_values(Fields.slab(ᶠε_ref.components.data.:3, 1, elem_id)), |
| 247 | + ) < tol |
| 248 | + @test maximum( |
| 249 | + abs, |
| 250 | + Fields.field_values(Fields.slab(ᶠε.components.data.:3, 11, elem_id)) .- |
| 251 | + Fields.field_values(Fields.slab(ᶠε_ref.components.data.:3, 11, elem_id)), |
| 252 | + ) < tol |
| 253 | + end |
| 254 | +end |
| 255 | + |
166 | 256 | @testset "horizontal integral at boundary" begin |
167 | 257 | # Test both `horizontal_integral_at_boundary` methods |
168 | 258 | (; cent_space, face_space) = get_cartesian_spaces() |
|
0 commit comments