From 4c0e8f19211a7322ffabbcd9f74edb40b2444ec1 Mon Sep 17 00:00:00 2001 From: gaelforget Date: Tue, 28 Jul 2026 09:01:02 +0200 Subject: [PATCH 1/8] attempt to save allocs in similar, getindex, and view --- src/types/gcmarray.jl | 167 +++++++++++++++++++++++++++++++++--------- 1 file changed, 132 insertions(+), 35 deletions(-) diff --git a/src/types/gcmarray.jl b/src/types/gcmarray.jl index 9cdb7a3..f2121f1 100644 --- a/src/types/gcmarray.jl +++ b/src/types/gcmarray.jl @@ -153,22 +153,40 @@ end Base.dataids(A::gcmarray) = (Base.dataids(A.f)..., Base.dataids(A.fSize)..., Base.dataids(A.fIndex)...) # + -function Base.getindex(A::AbstractMeshArray{T, N}, I::Vararg{Union{Int,Array{Int},AbstractUnitRange,Colon}, N}) where {T,N} - J=1:length(A.fIndex) - !isa(I[1],Colon) ? J=J[I[1]] : nothing - nFaces=length(J) - - tmpf=A.f[I...] - if isa(tmpf,Array{eltype(A),2}) - tmp=tmpf - else - n3=Int(length(tmpf)/nFaces) - K=(A.grid,eltype(A),A.fSize[J],A.fIndex[J]) - n3>1 ? tmp=gcmarray(K...,n3) : tmp=gcmarray(K...) - for I in eachindex(tmpf); tmp.f[I] = view(tmpf[I],:,:); end - end - return tmp +function Base.getindex(A::AbstractMeshArray{T, N}, I::Vararg{Union{Int,Array{Int},AbstractUnitRange,Colon}, N}) where {T,N} + J = 1:length(A.fIndex) + !isa(I[1], Colon) ? J = J[I[1]] : nothing + nFaces = length(J) + + tmpf = A.f[I...] + + if isa(tmpf, Array{eltype(A), 2}) + return tmpf # Scalar index → single face + else + fSize_sub = A.fSize[J] + fIndex_sub = A.fIndex[J] + n3 = Int(length(tmpf) / nFaces) + + if n3 == 1 + # 1D result + f_new = OuterArray{InnerArray{T,2},1}(undef, nFaces) + for I_iter in eachindex(tmpf) + f_new[I_iter] = view(tmpf[I_iter], :, :) + end + B = gcmarray{T, 1, InnerArray{T,2}}( + A.grid, A.meta, f_new, fSize_sub, fIndex_sub, thisversion) + else + # 2D result + f_new = OuterArray{InnerArray{T,2}, 2}(undef, nFaces, n3) + for I_iter in eachindex(tmpf) + f_new[I_iter] = view(tmpf[I_iter], :, :) + end + B = gcmarray{T, 2, InnerArray{T,2}}( + A.grid, A.meta, f_new, fSize_sub, fIndex_sub, thisversion) + end + return B + end end """ @@ -189,18 +207,34 @@ function Base.setindex!(A::AbstractMeshArray{T, N}, v, I::Vararg{Int, N}) where end function Base.view(A::AbstractMeshArray{T, N}, I::Vararg{Union{Int,AbstractUnitRange,Colon}, N}) where {T,N} - J=1:length(A.fIndex) - !isa(I[1],Colon) ? J=J[I[1]] : nothing - nFaces=length(J) - - tmpf=view(A.f,I...) - n3=Int(length(tmpf)/nFaces) #length(tmpf)>nFaces ? n3=Int(length(tmpf)/nFaces) : n3=1 - - K=(A.grid,eltype(A),A.fSize[J],A.fIndex[J]) - n3>1 ? tmp=gcmarray(K...,n3) : tmp=gcmarray(K...) - for I in eachindex(tmpf); tmp.f[I] = view(tmpf[I],:,:); end - - return tmp + J = 1:length(A.fIndex) + !isa(I[1], Colon) ? J = J[I[1]] : nothing + nFaces = length(J) + + tmpf = view(A.f, I...) + n3 = Int(length(tmpf) / nFaces) + + fSize_sub = A.fSize[J] + fIndex_sub = A.fIndex[J] + + if n3 == 1 + # 1D result + f_new = OuterArray{InnerArray{T,2},1}(undef, nFaces) + for I_iter in eachindex(tmpf) + f_new[I_iter] = view(tmpf[I_iter], :, :) + end + B = gcmarray{T, 1, InnerArray{T,2}}( + A.grid, A.meta, f_new, fSize_sub, fIndex_sub, thisversion) + else + # 2D result + f_new = OuterArray{InnerArray{T,2}, 2}(undef, nFaces, n3) + for I_iter in eachindex(tmpf) + f_new[I_iter] = view(tmpf[I_iter], :, :) + end + B = gcmarray{T, 2, InnerArray{T,2}}( + A.grid, A.meta, f_new, fSize_sub, fIndex_sub, thisversion) + end + return B end # ### Custom pretty-printing, similar, and broadcast @@ -242,10 +276,42 @@ end import Base: display; display(X::AbstractMeshArray)=show(X) function Base.similar(A::gcmarray; m::varmeta=defaultmeta) - if ndims(A)==1 - B = gcmarray(A.grid, eltype(A), A.fSize, A.fIndex; meta=m) - else - B = gcmarray(A.grid, eltype(A), A.fSize, A.fIndex, size(A)[2:end]...; meta=m) + ElType = eltype(A) + nFaces = length(A.fIndex) + + if ndims(A) == 1 + # 1D case: (nFaces,) + f = OuterArray{InnerArray{ElType,2},1}(undef, nFaces) + for a in 1:nFaces + f[a] = InnerArray{ElType}(undef, A.fSize[a]...) + end + B = gcmarray{ElType, 1, InnerArray{ElType,2}}( + A.grid, m, f, A.fSize, A.fIndex, thisversion) + elseif ndims(A) == 2 + # 2D case: (nFaces, n3) + n3 = size(A, 2) + f = OuterArray{InnerArray{ElType,2}, 2}(undef, nFaces, n3) + for a in 1:nFaces + for i3 in 1:n3 + f[a, i3] = InnerArray{ElType}(undef, A.fSize[a]...) + end + end + B = gcmarray{ElType, 2, InnerArray{ElType,2}}( + A.grid, m, f, A.fSize, A.fIndex, thisversion) + else # ndims(A) == 3 + # 3D case: (nFaces, n3, n4) + n3 = size(A, 2) + n4 = size(A, 3) + f = OuterArray{InnerArray{ElType,2}, 3}(undef, nFaces, n3, n4) + for a in 1:nFaces + for i4 in 1:n4 + for i3 in 1:n3 + f[a, i3, i4] = InnerArray{ElType}(undef, A.fSize[a]...) + end + end + end + B = gcmarray{ElType, 3, InnerArray{ElType,2}}( + A.grid, m, f, A.fSize, A.fIndex, thisversion) end return B end @@ -256,10 +322,41 @@ Base.BroadcastStyle(::Type{<:AbstractMeshArray}) = Broadcast.ArrayStyle{Abstract function Base.similar(bc::Broadcast.Broadcasted{Broadcast.ArrayStyle{AbstractMeshArray}}, ::Type{ElType}) where ElType A = find_gcmarray(bc) - if ndims(A)==1 - B = gcmarray(A.grid, ElType, A.fSize, A.fIndex) - else - B = gcmarray(A.grid, ElType, A.fSize, A.fIndex, size(A)[2:end]...) + nFaces = length(A.fIndex) + + if ndims(A) == 1 + # 1D case + f = OuterArray{InnerArray{ElType,2},1}(undef, nFaces) + for a in 1:nFaces + f[a] = InnerArray{ElType}(undef, A.fSize[a]...) + end + B = gcmarray{ElType, 1, InnerArray{ElType,2}}( + A.grid, defaultmeta, f, A.fSize, A.fIndex, thisversion) + elseif ndims(A) == 2 + # 2D case: (nFaces, n3) + n3 = size(A, 2) + f = OuterArray{InnerArray{ElType,2}, 2}(undef, nFaces, n3) + for a in 1:nFaces + for i3 in 1:n3 + f[a, i3] = InnerArray{ElType}(undef, A.fSize[a]...) + end + end + B = gcmarray{ElType, 2, InnerArray{ElType,2}}( + A.grid, defaultmeta, f, A.fSize, A.fIndex, thisversion) + else # ndims(A) == 3 + # 3D case: (nFaces, n3, n4) + n3 = size(A, 2) + n4 = size(A, 3) + f = OuterArray{InnerArray{ElType,2}, 3}(undef, nFaces, n3, n4) + for a in 1:nFaces + for i4 in 1:n4 + for i3 in 1:n3 + f[a, i3, i4] = InnerArray{ElType}(undef, A.fSize[a]...) + end + end + end + B = gcmarray{ElType, 3, InnerArray{ElType,2}}( + A.grid, defaultmeta, f, A.fSize, A.fIndex, thisversion) end return B end From 9bf8cb2ded134795c998d052cd207f080ad68cf6 Mon Sep 17 00:00:00 2001 From: gaelforget Date: Tue, 28 Jul 2026 09:44:23 +0200 Subject: [PATCH 2/8] add code to skip full initialization in similar --- src/types/gcmarray.jl | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/types/gcmarray.jl b/src/types/gcmarray.jl index f2121f1..1ed0fdf 100644 --- a/src/types/gcmarray.jl +++ b/src/types/gcmarray.jl @@ -279,29 +279,39 @@ function Base.similar(A::gcmarray; m::varmeta=defaultmeta) ElType = eltype(A) nFaces = length(A.fIndex) + full_init=true if ndims(A) == 1 + if full_init # 1D case: (nFaces,) f = OuterArray{InnerArray{ElType,2},1}(undef, nFaces) for a in 1:nFaces f[a] = InnerArray{ElType}(undef, A.fSize[a]...) end - B = gcmarray{ElType, 1, InnerArray{ElType,2}}( - A.grid, m, f, A.fSize, A.fIndex, thisversion) + else + f = fill(InnerArray{ElType}(undef, 0,0),nFaces) + end + B = gcmarray{ElType, 1, InnerArray{ElType,2}}( + A.grid, m, f, A.fSize, A.fIndex, thisversion) elseif ndims(A) == 2 # 2D case: (nFaces, n3) n3 = size(A, 2) + if full_init f = OuterArray{InnerArray{ElType,2}, 2}(undef, nFaces, n3) for a in 1:nFaces for i3 in 1:n3 f[a, i3] = InnerArray{ElType}(undef, A.fSize[a]...) end end + else + f = fill(InnerArray{ElType}(undef, 0,0),nFaces,n3) + end B = gcmarray{ElType, 2, InnerArray{ElType,2}}( A.grid, m, f, A.fSize, A.fIndex, thisversion) else # ndims(A) == 3 # 3D case: (nFaces, n3, n4) n3 = size(A, 2) n4 = size(A, 3) + if full_init f = OuterArray{InnerArray{ElType,2}, 3}(undef, nFaces, n3, n4) for a in 1:nFaces for i4 in 1:n4 @@ -310,8 +320,12 @@ function Base.similar(A::gcmarray; m::varmeta=defaultmeta) end end end + else + f = fill(InnerArray{ElType}(undef, 0,0),nFaces,n3,n4) + end B = gcmarray{ElType, 3, InnerArray{ElType,2}}( A.grid, m, f, A.fSize, A.fIndex, thisversion) + end return B end From 904898d92b72f8af1bf301a40900011da144e3af Mon Sep 17 00:00:00 2001 From: gaelforget Date: Tue, 28 Jul 2026 10:58:45 +0200 Subject: [PATCH 3/8] use full_init=true when needed --- src/Operations.jl | 10 ++++++---- src/ReadWrite.jl | 1 + src/VerticalDimension.jl | 1 + src/grids/tiles.jl | 2 +- src/types/gcmarray.jl | 3 +-- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Operations.jl b/src/Operations.jl index 5cadbbc..06c10e3 100644 --- a/src/Operations.jl +++ b/src/Operations.jl @@ -489,7 +489,7 @@ Returns a `gridpath` by default, or a `NamedTuple` with fields `lat`, `name`, function LatitudeCircle(lat,Γ::NamedTuple; format=:gridpath, range=(0.0,360.0)) - mskCint = similar(Γ.YC) + mskCint = similar(Γ.YC,full_init=true) for f in 1:mskCint.grid.nFaces yf = Γ.YC.f[f]; mf = mskCint.f[f] @inbounds for j in axes(mf,2), i in axes(mf,1) @@ -558,9 +558,11 @@ Compute edge mask (mskC,mskW,mskS) from domain interior mask (mskCint). This is used in `LatitudeCircles` and `Transect`. """ function edge_mask(mskCint::AbstractMeshArray) - mskC = similar(mskCint) - mskW = similar(mskCint) - mskS = similar(mskCint) + mskC = similar(mskCint,full_init=true) + mskW = similar(mskCint,full_init=true) + mskS = similar(mskCint,full_init=true) + + #need to allocate here exFLD = exchange(mskCint).MA # still needed for halo diff --git a/src/ReadWrite.jl b/src/ReadWrite.jl index e090218..06ad044 100644 --- a/src/ReadWrite.jl +++ b/src/ReadWrite.jl @@ -115,6 +115,7 @@ function read!(xx::Array,x::AbstractMeshArray) elseif n3>1 x[f,i3].=tmp[f] else + !isempty(x[f]) ? nothing : (x[f]=zeros(facesSize[f][1],facesSize[f][2])) x[f].=tmp[f] end end diff --git a/src/VerticalDimension.jl b/src/VerticalDimension.jl index 2b51e36..67529c5 100644 --- a/src/VerticalDimension.jl +++ b/src/VerticalDimension.jl @@ -7,6 +7,7 @@ function isosurface(θ,T,Γ) d=NaN*similar(θ[:,1]) nr=size(θ,2) for j=1:size(d,1) + !isempty(d[j]) ? nothing : (d[j]=NaN*zeros(d.fSize[j][1],d.fSize[j][2])) for k=1:nr-1 i=findall(isnan.(d[j]).&(θ[j,k].>T).&(θ[j,k+1].<=T)) a=(θ[j,k][i] .- T)./(θ[j,k][i] .- θ[j,k+1][i]) diff --git a/src/grids/tiles.jl b/src/grids/tiles.jl index 78599c1..8f89b26 100644 --- a/src/grids/tiles.jl +++ b/src/grids/tiles.jl @@ -52,7 +52,7 @@ read!(f,d) τ=Tiles(γ,30,30) td=Tiles(τ,d) -D=similar(d) +D=similar(d,full_init=true) Tiles!(τ,td,D) isa(td[1],Array) diff --git a/src/types/gcmarray.jl b/src/types/gcmarray.jl index 1ed0fdf..4cbda11 100644 --- a/src/types/gcmarray.jl +++ b/src/types/gcmarray.jl @@ -275,11 +275,10 @@ end import Base: display; display(X::AbstractMeshArray)=show(X) -function Base.similar(A::gcmarray; m::varmeta=defaultmeta) +function Base.similar(A::gcmarray; m::varmeta=defaultmeta,full_init=false) ElType = eltype(A) nFaces = length(A.fIndex) - full_init=true if ndims(A) == 1 if full_init # 1D case: (nFaces,) From 93cc9abf4823743d1ff5505de0ff068efb98ae29 Mon Sep 17 00:00:00 2001 From: gaelforget Date: Tue, 4 Aug 2026 11:31:29 +0200 Subject: [PATCH 4/8] =?UTF-8?q?lazy=20init=20=E2=80=94=20copyto!=20will=20?= =?UTF-8?q?allocate=20on=20first=20write?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/types/gcmarray.jl | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/src/types/gcmarray.jl b/src/types/gcmarray.jl index 4cbda11..beb31c8 100644 --- a/src/types/gcmarray.jl +++ b/src/types/gcmarray.jl @@ -336,38 +336,23 @@ Base.BroadcastStyle(::Type{<:AbstractMeshArray}) = Broadcast.ArrayStyle{Abstract function Base.similar(bc::Broadcast.Broadcasted{Broadcast.ArrayStyle{AbstractMeshArray}}, ::Type{ElType}) where ElType A = find_gcmarray(bc) nFaces = length(A.fIndex) - + if ndims(A) == 1 - # 1D case - f = OuterArray{InnerArray{ElType,2},1}(undef, nFaces) - for a in 1:nFaces - f[a] = InnerArray{ElType}(undef, A.fSize[a]...) - end + # 1D case: lazy init — copyto! will allocate on first write + f = fill(InnerArray{ElType}(undef, 0,0), nFaces) B = gcmarray{ElType, 1, InnerArray{ElType,2}}( A.grid, defaultmeta, f, A.fSize, A.fIndex, thisversion) elseif ndims(A) == 2 - # 2D case: (nFaces, n3) + # 2D case: lazy init — copyto! will allocate on first write n3 = size(A, 2) - f = OuterArray{InnerArray{ElType,2}, 2}(undef, nFaces, n3) - for a in 1:nFaces - for i3 in 1:n3 - f[a, i3] = InnerArray{ElType}(undef, A.fSize[a]...) - end - end + f = fill(InnerArray{ElType}(undef, 0,0), nFaces, n3) B = gcmarray{ElType, 2, InnerArray{ElType,2}}( A.grid, defaultmeta, f, A.fSize, A.fIndex, thisversion) else # ndims(A) == 3 - # 3D case: (nFaces, n3, n4) + # 3D case: lazy init — copyto! will allocate on first write n3 = size(A, 2) n4 = size(A, 3) - f = OuterArray{InnerArray{ElType,2}, 3}(undef, nFaces, n3, n4) - for a in 1:nFaces - for i4 in 1:n4 - for i3 in 1:n3 - f[a, i3, i4] = InnerArray{ElType}(undef, A.fSize[a]...) - end - end - end + f = fill(InnerArray{ElType}(undef, 0,0), nFaces, n3, n4) B = gcmarray{ElType, 3, InnerArray{ElType,2}}( A.grid, defaultmeta, f, A.fSize, A.fIndex, thisversion) end From 77ac70b628e9148fe3117da69804409d3649af39 Mon Sep 17 00:00:00 2001 From: gaelforget Date: Wed, 5 Aug 2026 10:36:11 +0200 Subject: [PATCH 5/8] use full_init=true when needed; add missing tests --- src/ReadWrite.jl | 6 ++--- test/testsets/unitgrid.jl | 46 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/ReadWrite.jl b/src/ReadWrite.jl index 06ad044..d88fb7b 100644 --- a/src/ReadWrite.jl +++ b/src/ReadWrite.jl @@ -34,7 +34,7 @@ _The second argument (MeshArray or gcmgrid) provides the grid specifications (x. ``` """ function read(fil::String, x::AbstractMeshArray) - y = similar(x; m=x.meta) + y = similar(x;m=x.meta,full_init=true) read!(fil, y) return y end @@ -90,7 +90,7 @@ end Reformat Array data into a MeshArray similar to `x`. """ function read(xx::Array,x::AbstractMeshArray) - y=similar(x; m=x.meta) + y=similar(x;m=x.meta,full_init=true) read!(xx,y) return y end @@ -252,7 +252,7 @@ function read_tiles(xx::Array,γ::gcmgrid) end function read_tiles(xx::Array,x::AbstractMeshArray) - tmp=similar(x) + tmp=similar(x,full_init=true) s=x.grid.ioSize (n1,n2)=x.grid.fSize[1] ni=Int(s[1]/n1) diff --git a/test/testsets/unitgrid.jl b/test/testsets/unitgrid.jl index 453aebc..610a5ec 100644 --- a/test/testsets/unitgrid.jl +++ b/test/testsets/unitgrid.jl @@ -27,4 +27,50 @@ gr=Grids_simple.grid_add_z(gr,dep,msk) @test haskey(gr,:hFacC) + + # Test read(xx::Array, x::AbstractMeshArray) — untested pathway + # Create a 4D array with shape (ioSize..., n3, n4) for PeriodicDomain grid + xx_data = randn(10, 10, 1, 1) # 10×10 ioSize, no extra dims + A_template = MeshArray(γ, Float64) + B = read(xx_data, A_template) + @test isa(B, MeshArray) + @test eltype(B) == Float64 + @test size(B) == size(A_template) + + # Test read(xx::Array, x::AbstractMeshArray) with n3 > 1 + xx_3d = randn(10, 10, 2, 1) + C_template = MeshArray(γ, Float64, 2) + C = read(xx_3d, C_template) + @test isa(C, MeshArray) + @test size(C) == size(C_template) + @test size(C, 2) == 2 + + # Test read(fil::String, x::AbstractMeshArray) with full_init=true + tmp_file = tempname() + write(tmp_file, Γ.XC) + A_template2 = MeshArray(γ, Float64) + D = read(tmp_file, A_template2) + @test isa(D, MeshArray) + @test size(D) == size(A_template2) + @test eltype(D) == Float64 + + # Test similar(x, full_init=true) pathway + A_1d = MeshArray(γ) + A_1d_full = similar(A_1d; full_init=true) + @test isa(A_1d_full, MeshArray) + @test size(A_1d_full) == size(A_1d) + # Verify faces are allocated (not 0×0) + @test size(A_1d_full.f[1]) == size(A_1d.f[1]) + + A_2d = MeshArray(γ, Float64, 3) + A_2d_full = similar(A_2d; full_init=true) + @test isa(A_2d_full, MeshArray) + @test size(A_2d_full) == size(A_2d) + @test size(A_2d_full.f[1]) == size(A_2d.f[1]) + + A_3d = MeshArray(γ, Float64, 3, 4) + A_3d_full = similar(A_3d; full_init=true) + @test isa(A_3d_full, MeshArray) + @test size(A_3d_full) == size(A_3d) + @test size(A_3d_full.f[1]) == size(A_3d.f[1]) end From 0830b53978e8ba4db25e16d4c5a15e190d8ab670 Mon Sep 17 00:00:00 2001 From: gaelforget Date: Wed, 5 Aug 2026 10:51:18 +0200 Subject: [PATCH 6/8] rename full_init arg as allocate, update docstrings --- src/Operations.jl | 8 ++++---- src/ReadWrite.jl | 20 +++++++++++--------- src/grids/tiles.jl | 2 +- src/types/gcmarray.jl | 19 ++++++++++++++----- test/testsets/unitgrid.jl | 10 +++++----- 5 files changed, 35 insertions(+), 24 deletions(-) diff --git a/src/Operations.jl b/src/Operations.jl index 06c10e3..c8b29c6 100644 --- a/src/Operations.jl +++ b/src/Operations.jl @@ -489,7 +489,7 @@ Returns a `gridpath` by default, or a `NamedTuple` with fields `lat`, `name`, function LatitudeCircle(lat,Γ::NamedTuple; format=:gridpath, range=(0.0,360.0)) - mskCint = similar(Γ.YC,full_init=true) + mskCint = similar(Γ.YC,allocate=true) for f in 1:mskCint.grid.nFaces yf = Γ.YC.f[f]; mf = mskCint.f[f] @inbounds for j in axes(mf,2), i in axes(mf,1) @@ -558,9 +558,9 @@ Compute edge mask (mskC,mskW,mskS) from domain interior mask (mskCint). This is used in `LatitudeCircles` and `Transect`. """ function edge_mask(mskCint::AbstractMeshArray) - mskC = similar(mskCint,full_init=true) - mskW = similar(mskCint,full_init=true) - mskS = similar(mskCint,full_init=true) + mskC = similar(mskCint,allocate=true) + mskW = similar(mskCint,allocate=true) + mskS = similar(mskCint,allocate=true) #need to allocate here diff --git a/src/ReadWrite.jl b/src/ReadWrite.jl index d88fb7b..aa3303b 100644 --- a/src/ReadWrite.jl +++ b/src/ReadWrite.jl @@ -26,15 +26,15 @@ function _load_binary(fil::String, y::AbstractMeshArray) end """ - read(fil::String,x::AbstractMeshArray) + read(fil::String, x::AbstractMeshArray) -Read array from file and return as a MeshArray. +Read binary file into a new MeshArray with the same structure as `x`. -_The second argument (MeshArray or gcmgrid) provides the grid specifications (x.grid.ioSize)._ -``` +Creates a new MeshArray (allocating all face arrays) and populates it from `fil`. +The template `x` provides grid specifications via `x.grid.ioSize`. """ function read(fil::String, x::AbstractMeshArray) - y = similar(x;m=x.meta,full_init=true) + y = similar(x;m=x.meta,allocate=true) read!(fil, y) return y end @@ -85,12 +85,14 @@ function read(xx::Array,γ::gcmgrid; verbose=false) end """ - read(xx::Array,x::AbstractMeshArray) + read(xx::Array, x::AbstractMeshArray) + +Reformat Array data into a new MeshArray with the same structure as `x`. -Reformat Array data into a MeshArray similar to `x`. +Creates a new MeshArray (allocating all face arrays) and populates it from the Array `xx`. """ function read(xx::Array,x::AbstractMeshArray) - y=similar(x;m=x.meta,full_init=true) + y=similar(x;m=x.meta,allocate=true) read!(xx,y) return y end @@ -252,7 +254,7 @@ function read_tiles(xx::Array,γ::gcmgrid) end function read_tiles(xx::Array,x::AbstractMeshArray) - tmp=similar(x,full_init=true) + tmp=similar(x,allocate=true) s=x.grid.ioSize (n1,n2)=x.grid.fSize[1] ni=Int(s[1]/n1) diff --git a/src/grids/tiles.jl b/src/grids/tiles.jl index 8f89b26..167572d 100644 --- a/src/grids/tiles.jl +++ b/src/grids/tiles.jl @@ -52,7 +52,7 @@ read!(f,d) τ=Tiles(γ,30,30) td=Tiles(τ,d) -D=similar(d,full_init=true) +D=similar(d,allocate=true) Tiles!(τ,td,D) isa(td[1],Array) diff --git a/src/types/gcmarray.jl b/src/types/gcmarray.jl index beb31c8..ac0667d 100644 --- a/src/types/gcmarray.jl +++ b/src/types/gcmarray.jl @@ -275,12 +275,21 @@ end import Base: display; display(X::AbstractMeshArray)=show(X) -function Base.similar(A::gcmarray; m::varmeta=defaultmeta,full_init=false) +""" + similar(A::gcmarray; m::varmeta=defaultmeta, allocate=false) + +Create a gcmarray with the same structure and type as `A`. + +If `allocate=true`, eagerly allocates all face arrays sized according to `A.fSize`. +If `allocate=false` (default), creates lazy empty placeholders; use when the array +will be filled in-place (e.g., `read!`, `readtiles`). +""" +function Base.similar(A::gcmarray; m::varmeta=defaultmeta,allocate=false) ElType = eltype(A) nFaces = length(A.fIndex) - + if ndims(A) == 1 - if full_init + if allocate # 1D case: (nFaces,) f = OuterArray{InnerArray{ElType,2},1}(undef, nFaces) for a in 1:nFaces @@ -294,7 +303,7 @@ function Base.similar(A::gcmarray; m::varmeta=defaultmeta,full_init=false) elseif ndims(A) == 2 # 2D case: (nFaces, n3) n3 = size(A, 2) - if full_init + if allocate f = OuterArray{InnerArray{ElType,2}, 2}(undef, nFaces, n3) for a in 1:nFaces for i3 in 1:n3 @@ -310,7 +319,7 @@ function Base.similar(A::gcmarray; m::varmeta=defaultmeta,full_init=false) # 3D case: (nFaces, n3, n4) n3 = size(A, 2) n4 = size(A, 3) - if full_init + if allocate f = OuterArray{InnerArray{ElType,2}, 3}(undef, nFaces, n3, n4) for a in 1:nFaces for i4 in 1:n4 diff --git a/test/testsets/unitgrid.jl b/test/testsets/unitgrid.jl index 610a5ec..d467352 100644 --- a/test/testsets/unitgrid.jl +++ b/test/testsets/unitgrid.jl @@ -45,7 +45,7 @@ @test size(C) == size(C_template) @test size(C, 2) == 2 - # Test read(fil::String, x::AbstractMeshArray) with full_init=true + # Test read(fil::String, x::AbstractMeshArray) with allocate=true tmp_file = tempname() write(tmp_file, Γ.XC) A_template2 = MeshArray(γ, Float64) @@ -54,22 +54,22 @@ @test size(D) == size(A_template2) @test eltype(D) == Float64 - # Test similar(x, full_init=true) pathway + # Test similar(x, allocate=true) pathway A_1d = MeshArray(γ) - A_1d_full = similar(A_1d; full_init=true) + A_1d_full = similar(A_1d; allocate=true) @test isa(A_1d_full, MeshArray) @test size(A_1d_full) == size(A_1d) # Verify faces are allocated (not 0×0) @test size(A_1d_full.f[1]) == size(A_1d.f[1]) A_2d = MeshArray(γ, Float64, 3) - A_2d_full = similar(A_2d; full_init=true) + A_2d_full = similar(A_2d; allocate=true) @test isa(A_2d_full, MeshArray) @test size(A_2d_full) == size(A_2d) @test size(A_2d_full.f[1]) == size(A_2d.f[1]) A_3d = MeshArray(γ, Float64, 3, 4) - A_3d_full = similar(A_3d; full_init=true) + A_3d_full = similar(A_3d; allocate=true) @test isa(A_3d_full, MeshArray) @test size(A_3d_full) == size(A_3d) @test size(A_3d_full.f[1]) == size(A_3d.f[1]) From 1f7fe917065db059123cea17c74a385f0d7feb87 Mon Sep 17 00:00:00 2001 From: gaelforget Date: Wed, 5 Aug 2026 10:52:05 +0200 Subject: [PATCH 7/8] bump version -- v0.6.0 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 31a7e62..123bef5 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "MeshArrays" uuid = "cb8c808f-1acf-59a3-9d2b-6e38d009f683" -version = "0.5.9" +version = "0.6.0" authors = ["gaelforget "] [deps] From 55562aeb57198e540fcb0dad44ab853618831db5 Mon Sep 17 00:00:00 2001 From: gaelforget Date: Wed, 5 Aug 2026 10:56:05 +0200 Subject: [PATCH 8/8] back to v0.5.10 (MITgcm compat needs it) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 123bef5..8945ab6 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "MeshArrays" uuid = "cb8c808f-1acf-59a3-9d2b-6e38d009f683" -version = "0.6.0" +version = "0.5.10" authors = ["gaelforget "] [deps]