-
Notifications
You must be signed in to change notification settings - Fork 173
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
Bugfix for parent_index_range
#3573
Conversation
parent_index_range
There are a couple issues dedicated to index issues with the free surface that might be related. @siddharthabishnu can you try to track these down? |
Might be good to add a test (or more than just one) for windowed fields. Even a simple one like windowed_field = CenterField(grid, indices=(:, :, 1:1))
@test view(windowed_field, :, :, 1:1) isa AbstractArray or something. @siddharthabishnu can you add that? Pretty basic test so probably belongs in https://github.com/CliMA/Oceananigans.jl/blob/sb/extend-parent-indices/test/test_field.jl |
A lot of tests seem to fail... |
This is troubling |
Ok, I think it's because It's not the nicest code either Oceananigans.jl/src/OutputWriters/netcdf_output_writer.jl Lines 52 to 57 in ed77780
|
Ok I think I understand the issue. In In I think we just need two different functions. I'll fix it. |
Ok here's a little more insight into the problem. The issue is actually with I'm extending This is probably the issue with outputting the free surface too. I can't remember exactly what went wrong there though. The output issue could also hint that things are more convoluted than they should be. Still though, we should be able to create views into fields that are already views. It should be fine. |
nice one! |
OY. Ok I worked on it a bit. This works: julia> grid = RectilinearGrid(size=(1, 1, 3), x=(0, 1), y=(0, 1), z=(0, 1));
julia> c = CenterField(grid);
julia> set!(c, (x, y, z) -> rand())
1×1×3 Field{Center, Center, Center} on RectilinearGrid on CPU
├── grid: 1×1×3 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 1×1×3 halo
├── boundary conditions: FieldBoundaryConditions
│ └── west: Periodic, east: Periodic, south: Periodic, north: Periodic, bottom: ZeroFlux, top: ZeroFlux, immersed: ZeroFlux
└── data: 3×3×9 OffsetArray(::Array{Float64, 3}, 0:2, 0:2, -2:6) with eltype Float64 with indices 0:2×0:2×-2:6
└── max=0.92472, min=0.40508, mean=0.67047
julia> cv = view(c, :, :, 2:3)
1×1×2 Field{Center, Center, Center} on RectilinearGrid on CPU
├── grid: 1×1×3 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 1×1×3 halo
├── boundary conditions: FieldBoundaryConditions
│ └── west: Periodic, east: Periodic, south: Periodic, north: Periodic, bottom: Nothing, top: Nothing, immersed: ZeroFlux
├── indices: (:, :, 2:3)
└── data: 3×3×2 OffsetArray(view(::Array{Float64, 3}, :, :, 5:6), 0:2, 0:2, 2:3) with eltype Float64 with indices 0:2×0:2×2:3
└── max=0.92472, min=0.681611, mean=0.803165
julia> cvv = view(cv, :, :, 3)
1×1×1 Field{Center, Center, Center} on RectilinearGrid on CPU
├── grid: 1×1×3 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 1×1×3 halo
├── boundary conditions: FieldBoundaryConditions
│ └── west: Periodic, east: Periodic, south: Periodic, north: Periodic, bottom: Nothing, top: Nothing, immersed: ZeroFlux
├── indices: (:, :, 3:3)
└── data: 3×3×1 OffsetArray(view(::Array{Float64, 3}, :, :, 6:6), 0:2, 0:2, 3:3) with eltype Float64 with indices 0:2×0:2×3:3
└── max=0.681611, min=0.681611, mean=0.681611
julia> cvvv = view(cvv, :, :, 3)
1×1×1 Field{Center, Center, Center} on RectilinearGrid on CPU
├── grid: 1×1×3 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 1×1×3 halo
├── boundary conditions: FieldBoundaryConditions
│ └── west: Periodic, east: Periodic, south: Periodic, north: Periodic, bottom: Nothing, top: Nothing, immersed: ZeroFlux
├── indices: (:, :, 3:3)
└── data: 3×3×1 OffsetArray(view(::Array{Float64, 3}, :, :, 6:6), 0:2, 0:2, 3:3) with eltype Float64 with indices 0:2×0:2×3:3
└── max=0.681611, min=0.681611, mean=0.681611
julia> interior(c, :, :, 3) .= 0
1×1 view(::Array{Float64, 3}, 2:2, 2:2, 6) with eltype Float64:
0.0
julia> cvv
1×1×1 Field{Center, Center, Center} on RectilinearGrid on CPU
├── grid: 1×1×3 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 1×1×3 halo
├── boundary conditions: FieldBoundaryConditions
│ └── west: Periodic, east: Periodic, south: Periodic, north: Periodic, bottom: Nothing, top: Nothing, immersed: ZeroFlux
├── indices: (:, :, 3:3)
└── data: 3×3×1 OffsetArray(view(::Array{Float64, 3}, :, :, 6:6), 0:2, 0:2, 3:3) with eltype Float64 with indices 0:2×0:2×3:3
└── max=0.0, min=0.0, mean=0.0
julia> cv
1×1×2 Field{Center, Center, Center} on RectilinearGrid on CPU
├── grid: 1×1×3 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 1×1×3 halo
├── boundary conditions: FieldBoundaryConditions
│ └── west: Periodic, east: Periodic, south: Periodic, north: Periodic, bottom: Nothing, top: Nothing, immersed: ZeroFlux
├── indices: (:, :, 2:3)
└── data: 3×3×2 OffsetArray(view(::Array{Float64, 3}, :, :, 5:6), 0:2, 0:2, 2:3) with eltype Float64 with indices 0:2×0:2×2:3
└── max=0.92472, min=0.0, mean=0.46236 Note that one of the quirks of fields that come from views, is that julia> parent(cv)
3×3×2 view(::Array{Float64, 3}, :, :, 5:6) with eltype Float64:
[:, :, 1] =
0.0 0.0 0.0
0.0 0.92472 0.0
0.0 0.0 0.0
[:, :, 2] =
0.0 0.0 0.0
0.0 0.0 0.0
0.0 0.0 0.0
julia> parent(cvv)
3×3×1 view(::Array{Float64, 3}, :, :, 6:6) with eltype Float64:
[:, :, 1] =
0.0 0.0 0.0
0.0 0.0 0.0
0.0 0.0 0.0 That's different than if you create a windowed field from scratch: julia> b = CenterField(grid, indices=(:, :, 3))
1×1×1 Field{Center, Center, Center} on RectilinearGrid on CPU
├── grid: 1×1×3 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 1×1×3 halo
├── boundary conditions: FieldBoundaryConditions
│ └── west: Periodic, east: Periodic, south: Periodic, north: Periodic, bottom: Nothing, top: Nothing, immersed: ZeroFlux
├── indices: (:, :, 3:3)
└── data: 3×3×1 OffsetArray(::Array{Float64, 3}, 0:2, 0:2, 3:3) with eltype Float64 with indices 0:2×0:2×3:3
└── max=0.0, min=0.0, mean=0.0
julia> parent(b)
3×3×1 Array{Float64, 3}:
[:, :, 1] =
0.0 0.0 0.0
0.0 0.0 0.0
0.0 0.0 0.0 This is how output works --- we use |
@siddharthabishnu could you help by adding some simple unit tests for views of views? It'd be nice to test correctness, I think something like this would work: grid = RectilinearGrid(size=(1, 1, 3), x=(0, 1), y=(0, 1), z=(0, 1))
c = CenterField(grid)
set!(c, (x, y, z) -> rand())
# First test that regular view is right
cv = view(c, :, :, 2:3)
@test c[1, 1, 2] == cv[1, 1, 2]
@test c[1, 1, 3] == cv[1, 1, 3]
# Now test views of views
cvv = view(cv, :, :, 3)
@test cv[1, 1, 3] == cvv[1, 1, 3] We may also want to test that things error correctly like julia> view(cv, :, :, 1)
ERROR: ArgumentError: view indices (Colon(), Colon(), 1) do not intersect field indices (Colon(), Colon(), 2:3)
Stacktrace:
[1] view(f::Field{…}, i::Function, j::Function, k::Int64)
@ Oceananigans.Fields ~/Projects/dev/Oceananigans.jl/src/Fields/field.jl:315
[2] top-level scope
@ REPL[25]:1
Some type information was truncated. Use `show(err)` to see complete types. Maybe @test_throws ArgumentError view(cv, :, :, 1) Not sure what else we might want maybe @navidcy has ideas. |
Could do @test_throws BoundsError cv[1, 1, 1] |
And to do those tests on the GPU we need |
Thanks for this @glwagner! |
I ran the doctests locally and this one: Oceananigans.jl/src/Fields/field.jl Lines 270 to 298 in f217b80
fails. I need to head out now but I wanted to document this. |
julia> using Oceananigans
julia> grid = RectilinearGrid(size=(2, 3, 4), x=(0, 1), y=(0, 1), z=(0, 1));
julia> c = CenterField(grid)
2×3×4 Field{Center, Center, Center} on RectilinearGrid on CPU
├── grid: 2×3×4 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 2×3×3 halo
├── boundary conditions: FieldBoundaryConditions
│ └── west: Periodic, east: Periodic, south: Periodic, north: Periodic, bottom: ZeroFlux, top: ZeroFlux, immersed: ZeroFlux
└── data: 6×9×10 OffsetArray(::Array{Float64, 3}, -1:4, -2:6, -2:7) with eltype Float64 with indices -1:4×-2:6×-2:7
└── max=0.0, min=0.0, mean=0.0
julia> c .= rand(size(c)...);
julia> v = view(c, :, 2:3, 1:2)
2×2×2 Field{Center, Center, Center} on RectilinearGrid on CPU
├── grid: 2×3×4 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 2×3×3 halo
├── boundary conditions: FieldBoundaryConditions
│ └── west: Periodic, east: Periodic, south: Nothing, north: Nothing, bottom: Nothing, top: Nothing, immersed: ZeroFlux
├── indices: (:, 2:3, 1:2)
└── data: 6×2×2 OffsetArray(view(::Array{Float64, 3}, :, 1:2, 1:2), -1:4, 2:3, 1:2) with eltype Float64 with indices -1:4×2:3×1:2
└── max=0.0, min=0.0, mean=0.0
julia> size(v)
(2, 2, 2)
julia> v[2, 2, 2] == c[2, 2, 2]
false seems like julia> v
2×2×2 Field{Center, Center, Center} on RectilinearGrid on CPU
├── grid: 2×3×4 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 2×3×3 halo
├── boundary conditions: FieldBoundaryConditions
│ └── west: Periodic, east: Periodic, south: Nothing, north: Nothing, bottom: Nothing, top: Nothing, immersed: ZeroFlux
├── indices: (:, 2:3, 1:2)
└── data: 6×2×2 OffsetArray(view(::Array{Float64, 3}, :, 1:2, 1:2), -1:4, 2:3, 1:2) with eltype Float64 with indices -1:4×2:3×1:2
└── max=0.0, min=0.0, mean=0.0 |
Can't reproduce that @navidcy julia> using Oceananigans
Precompiling Oceananigans
1 dependency successfully precompiled in 11 seconds. 160 already precompiled.
julia> grid = RectilinearGrid(size=(2, 3, 4), x=(0, 1), y=(0, 1), z=(0, 1));
julia> c = CenterField(grid)
2×3×4 Field{Center, Center, Center} on RectilinearGrid on CPU
├── grid: 2×3×4 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 2×3×3 halo
├── boundary conditions: FieldBoundaryConditions
│ └── west: Periodic, east: Periodic, south: Periodic, north: Periodic, bottom: ZeroFlux, top: ZeroFlux, immersed: ZeroFlux
└── data: 6×9×10 OffsetArray(::Array{Float64, 3}, -1:4, -2:6, -2:7) with eltype Float64 with indices -1:4×-2:6×-2:7
└── max=0.0, min=0.0, mean=0.0
julia> c .= rand(size(c)...);
julia> v = view(c, :, 2:3, 1:2)
2×2×2 Field{Center, Center, Center} on RectilinearGrid on CPU
├── grid: 2×3×4 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 2×3×3 halo
├── boundary conditions: FieldBoundaryConditions
│ └── west: Periodic, east: Periodic, south: Nothing, north: Nothing, bottom: Nothing, top: Nothing, immersed: ZeroFlux
├── indices: (:, 2:3, 1:2)
└── data: 6×2×2 OffsetArray(view(::Array{Float64, 3}, :, 5:6, 4:5), -1:4, 2:3, 1:2) with eltype Float64 with indices -1:4×2:3×1:2
└── max=0.943733, min=0.11545, mean=0.571896
julia> size(v)
(2, 2, 2)
julia> v[2, 2, 2] == c[2, 2, 2]
true |
And all tests pass! Great! Ignore my messages! |
test/test_field.jl
Outdated
@test all(cv[1, 1, i] == c[1, 1, i] for i in firstindex(interior(c), 3)+1:lastindex(interior(c), 3)-1) | ||
|
||
# Now test the views of views | ||
cvv = view(cv, :, :, firstindex(interior(c), 3)+2:lastindex(interior(c), 3)-2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe define k_tops = [Nz, Nz+1]
and include that in the loop above
The tests use a lot of scalar indexing that's why they fail on the GPU: https://buildkite.com/clima/oceananigans/builds/15604#018f40ed-787d-4e74-a5f4-ae1656fa3043/18-724 I think if we are comparing single numbers it makes sense to use If we are comparing vectors it could be nice to figure out how to get the tests to run without |
@siddharthabishnu could you deal with this? |
Given that we're comparing elements of vectors with a maximum length of 6, I opted to use |
Done. I think it's ready to merge once the tests pass. |
test/test_field.jl
Outdated
# @test_throws BoundsError cvv[:, :, 7:8] for a ZFaceField with bounded topology in z else @test_throws BoundsError cvv[:, :, 6:7]. | ||
@test_throws BoundsError cvvv[:, :, 1:1+2] | ||
@test_throws BoundsError cvvv[:, :, k_top-2:k_top] | ||
# @test_throws BoundsError cvvv[:, :, 6:8] for a ZFaceField with bounded topology in z else @test_throws BoundsError cvvv[:, :, 5:7]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@siddharthabishnu can you put @allowscalar
around only the specific lines where it is needed? I thought by using k_top
we eliminated the need for the comments. Not sure what the comments mean
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test/test_field.jl
Outdated
@test_throws BoundsError cvv[:, :, k_top-1:k_top] | ||
# @test_throws BoundsError cvv[:, :, 7:8] for a ZFaceField with bounded topology in z else @test_throws BoundsError cvv[:, :, 6:7]. | ||
@test_throws BoundsError cvvv[:, :, 1:1+2] | ||
@test_throws BoundsError cvvv[:, :, k_top-2:k_top] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this use interior
? We don't ever directly index fields in the user interface
@test_throws BoundsError cvvv[:, :, k_top-2:k_top] | |
@test_throws BoundsError cvvv[:, :, k_top-2:k_top] | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, not, unless we also extend the interior index range like we did with the parent index range. Consider the following MWE:
julia> using Oceananigans
julia> Nx, Ny, Nz = 1, 1, 7;
julia> grid = RectilinearGrid(size=(Nx, Ny, Nz), x=(0, 1), y=(0, 1), z=(0, 1), topology = (Periodic, Periodic, Bounded));
julia> k_top = Nz + 1;
julia> c = ZFaceField(grid)
1×1×8 Field{Center, Center, Face} on RectilinearGrid on CPU
├── grid: 1×1×7 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 1×1×3 halo
├── boundary conditions: FieldBoundaryConditions
│ └── west: Periodic, east: Periodic, south: Periodic, north: Periodic, bottom: Nothing, top: Nothing, immersed: ZeroFlux
└── data: 3×3×14 OffsetArray(::Array{Float64, 3}, 0:2, 0:2, -2:11) with eltype Float64 with indices 0:2×0:2×-2:11
└── max=0.0, min=0.0, mean=0.0
julia> set!(c, (x, y, z) -> rand())
1×1×8 Field{Center, Center, Face} on RectilinearGrid on CPU
├── grid: 1×1×7 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 1×1×3 halo
├── boundary conditions: FieldBoundaryConditions
│ └── west: Periodic, east: Periodic, south: Periodic, north: Periodic, bottom: Nothing, top: Nothing, immersed: ZeroFlux
└── data: 3×3×14 OffsetArray(::Array{Float64, 3}, 0:2, 0:2, -2:11) with eltype Float64 with indices 0:2×0:2×-2:11
└── max=0.72276, min=0.0939695, mean=0.428961
julia> cv = view(c, :, :, 1+1:k_top-1)
1×1×6 Field{Center, Center, Face} on RectilinearGrid on CPU
├── grid: 1×1×7 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 1×1×3 halo
├── boundary conditions: FieldBoundaryConditions
│ └── west: Periodic, east: Periodic, south: Periodic, north: Periodic, bottom: Nothing, top: Nothing, immersed: ZeroFlux
├── indices: (:, :, 2:7)
└── data: 3×3×6 OffsetArray(view(::Array{Float64, 3}, :, :, 5:10), 0:2, 0:2, 2:7) with eltype Float64 with indices 0:2×0:2×2:7
└── max=0.72276, min=0.0939695, mean=0.503409
julia> interior(cv)
1×1×6 view(::Array{Float64, 3}, 2:2, 2:2, 5:10) with eltype Float64:
[:, :, 1] =
0.6491566506900277
[:, :, 2] =
0.5118574586205709
[:, :, 3] =
0.6214555441666034
[:, :, 4] =
0.09396949649555086
[:, :, 5] =
0.7227599953730516
[:, :, 6] =
0.4212556767008805
julia> interior(cv, :, :, k_top-1)
ERROR: BoundsError: attempt to access 1×1×6 view(::Array{Float64, 3}, 2:2, 2:2, 5:10) with eltype Float64 at index [1:1, 1:1, 7]
Stacktrace:
[1] throw_boundserror(A::SubArray{Float64, 3, Array{Float64, 3}, Tuple{UnitRange{Int64}, UnitRange{Int64}, UnitRange{Int64}}, false}, I::Tuple{Base.Slice{Base.OneTo{Int64}}, Base.Slice{Base.OneTo{Int64}}, Int64})
@ Base ./abstractarray.jl:737
[2] checkbounds
@ ./abstractarray.jl:702 [inlined]
[3] view(::SubArray{Float64, 3, Array{Float64, 3}, Tuple{UnitRange{Int64}, UnitRange{Int64}, UnitRange{Int64}}, false}, ::Function, ::Function, ::Int64)
@ Base ./subarray.jl:184
[4] interior(::Field{Center, Center, Face, Nothing, RectilinearGrid{…}, Tuple{…}, OffsetArrays.OffsetArray{…}, Float64, FieldBoundaryConditions{…}, Nothing, Oceananigans.Fields.FieldBoundaryBuffers{…}}, ::Function, ::Function, ::Int64)
@ Oceananigans.Fields /Users/Sid/Library/CloudStorage/Dropbox/StudyFolder/PostDocMITDesktop/Codes/Oceananigans/extend-parent-indices/src/Fields/field.jl:395
[5] top-level scope
@ REPL[20]:1
Some type information was truncated. Use `show(err)` to see complete types.
Currently, the interior indices start from 1 and extend up to the size of the interior of the field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I'm not totally sure what the test is contributing
test/test_field.jl
Outdated
grid = RectilinearGrid(arch, FT, size=(Nx, Ny, Nz), x=(0, 1), y=(0, 1), z=(0, 1), topology = (Periodic, Periodic, ZTopology)) | ||
Hx, Hy, Hz = halo_size(grid) | ||
|
||
k_top = (FieldType == ZFaceField && ZTopology == Bounded) ? Nz+1 : Nz |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is a function for this; you need to do using Oceananigans.Grids: total_length
and then do
k_top = (FieldType == ZFaceField && ZTopology == Bounded) ? Nz+1 : Nz | |
k_top = total_length(location(c, 3)(), topology(c, 3)(), size(grid, 3)) |
but after you create the field
test/test_field.jl
Outdated
|
||
# First test that the regular view is correct | ||
cv = view(c, :, :, 1+1:k_top-1) | ||
@test size(cv) == (1, 1, k_top-2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@test size(cv) == (1, 1, k_top-2) | |
@test size(cv) == (Nx, Ny, k_top-2) | |
test/test_field.jl
Outdated
# First test that the regular view is correct | ||
cv = view(c, :, :, 1+1:k_top-1) | ||
@test size(cv) == (1, 1, k_top-2) | ||
@test size(parent(cv)) == (1+2Hx, 1+2Hy, k_top-2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@test size(parent(cv)) == (1+2Hx, 1+2Hy, k_top-2) | |
@test size(parent(cv)) == (Nx+2Hx, Ny+2Hy, k_top-2) | |
test/test_field.jl
Outdated
cv = view(c, :, :, 1+1:k_top-1) | ||
@test size(cv) == (1, 1, k_top-2) | ||
@test size(parent(cv)) == (1+2Hx, 1+2Hy, k_top-2) | ||
CUDA.@allowscalar @test all(cv[1, 1, i] == c[1, 1, i] for i in 1+1:k_top-1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CUDA.@allowscalar @test all(cv[1, 1, i] == c[1, 1, i] for i in 1+1:k_top-1) | |
CUDA.@allowscalar @test all(cv[i, j, k] == c[i, j, k] for k in 1+1:k_top-1, j in 1:Ny, i in 1:Nx) | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@siddharthabishnu could you remove all the rest hardcoded 1's below in the tests? (unless they are not hard coded and I missed it...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
made few suggestions
Incorporated in commit d71e14a. |
good to merge by me |
I'm merging |
Closes #3572.