Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 24 additions & 12 deletions src/DataWrangling/Copernicus/Copernicus.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import ClimaOcean.DataWrangling:
metadata_filename,
inpainted_metadata_path,
reversed_vertical_axis,
available_variables
available_variables,
is_three_dimensional

using Scratch

Expand Down Expand Up @@ -129,20 +130,31 @@ end

inpainted_metadata_path(metadata::CopernicusMetadata) = joinpath(metadata.dir, inpainted_metadata_filename(metadata))

location(::CopernicusMetadata) = (Center, Center, Center)
location(metadata::CopernicusMetadata) = is_three_dimensional(metadata) ? (Center, Center, Center) : (Center, Center, Nothing)
longitude_interfaces(::CopernicusMetadata) = (0, 360)
latitude_interfaces(::CopernicusMetadata) = (-80, 90)

is_three_dimensional(metadata::CopernicusMetadata) =
metadata.name == :temperature ||
metadata.name == :salinity ||
metadata.name == :u_velocity ||
metadata.name == :v_velocity
Comment on lines +137 to +141
Copy link
Member

@glwagner glwagner Oct 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should blacklist the 2D variables rather than whitelisting 3D variables, since most variables are 3D

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I was trying to follow the design pattern here:

if is_three_dimensional(metadata)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can achieve this by using a ! conditional because is_three_dimensional == !is_two_dimensional

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for example:

is_three_dimensional(metadata::CopernicusMetadata) =
    !(metadata.name == :ice_concentration ||
      metadata.name == :ice_thickness)


function z_interfaces(metadata::CopernicusMetadata)
path = metadata_path(metadata)
ds = Dataset(path)
zc = - reverse(ds["depth"][:])
close(ds)
dz = zc[2] - zc[1]
zf = zc[1:end-1] .+ zc[2:end]
push!(zf, 0)
pushfirst!(zf, zf[1] - dz)
return zf
# Check if this is a 2D surface variable
if !is_three_dimensional(metadata)
return (0, 1)
Comment on lines +145 to +146
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better to build a Flat grid for 2D variables? Why do the z_interfaces matter?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this line seems to want to take something for z, I tried returning nothing but I guess it didn't work

grid = LatitudeLongitudeGrid(arch, FT; halo, longitude, latitude, z,

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, the size and the halo must be 2-tuples to use nothing in z.

else
path = metadata_path(metadata)
ds = Dataset(path)
zc = - reverse(ds["depth"][:])
close(ds)
dz = zc[2] - zc[1]
zf = zc[1:end-1] .+ zc[2:end]
push!(zf, 0)
pushfirst!(zf, zf[1] - dz)
return zf
end
end

end # module Copernicus
end # module Copernicus
2 changes: 1 addition & 1 deletion src/DataWrangling/DataWrangling.jl
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ function default_inpainting(metadata)
if metadata.name in (:temperature, :salinity)
return NearestNeighborInpainting(Inf)
elseif metadata.name in (:sea_ice_thickness, :sea_ice_concentration)
return nothing
return ValueInpainting(0)
else
return NearestNeighborInpainting(5)
end
Expand Down
66 changes: 62 additions & 4 deletions src/DataWrangling/inpainting.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,40 @@ struct NearestNeighborInpainting{M}
maxiter :: M
end

"""
ValueInpainting{V}

A structure representing a simple value inpainting algorithm, where all missing values are
substituted with a specified constant value.

# Fields
- `value :: V`: The constant value to use for inpainting missing data.
- `maxiter`: For cache compatibility (not used in the algorithm itself). Defaults to 0.

# Example
```julia
inpainting = ValueInpainting(0.0) # Fill missing values with 0
inpaint_mask!(field, mask; inpainting)
```
"""
struct ValueInpainting{V, M}
value :: V
maxiter :: M
end

# Convenience constructor that sets maxiter to a default value
ValueInpainting(value) = ValueInpainting(value, 0)

propagate_horizontally!(field, ::Nothing, substituting_field=deepcopy(field); kw...) = field

function propagating(field, mask, iter, inpainting::NearestNeighborInpainting)
nans = sum(isnan, field; condition=interior(mask))
return nans > 0 && iter < inpainting.maxiter
end

# ValueInpainting doesn't need iteration
propagating(field, mask, iter, inpainting::ValueInpainting) = false

"""
propagate_horizontally!(inpainting, field, mask [, substituting_field=deepcopy(field)])

Expand Down Expand Up @@ -57,6 +84,17 @@ function propagate_horizontally!(inpainting::NearestNeighborInpainting, field, m
return field
end

function propagate_horizontally!(inpainting::ValueInpainting, field, mask,
substituting_field=deepcopy(field))
grid = field.grid
arch = architecture(grid)

launch!(arch, grid, size(field), _fill_with_number!, field, mask, inpainting.value)
fill_halo_regions!(field)

return field
end

# Maybe we can remove this propagate field in lieu of a diffusion,
# Still we'll need to do this a couple of steps on the original grid
@kernel function _propagate_field!(substituting_field, ::NearestNeighborInpainting, field)
Expand Down Expand Up @@ -102,6 +140,12 @@ end
@inbounds field[i, j, k] *= !isnan(field[i, j, k])
end

@kernel function _fill_with_number!(field, mask, value)
i, j, k = @index(Global, NTuple)
FT_value = convert(eltype(field), value)
@inbounds field[i, j, k] = ifelse(mask[i, j, k], FT_value, field[i, j, k])
end

"""
inpaint_mask!(field, mask; inpainting=NearestNeighborInpainting(Inf))

Expand All @@ -117,10 +161,24 @@ Arguments
- `mask`: Boolean-valued `Field`, values where
`mask[i, j, k] == true` are inpainted.

- `inpainting`: The inpainting algorithm to use. The only option is
`NearestNeighborInpainting(maxiter)`, where an average
of the valid surrounding values is used `maxiter` times.
Default: `NearestNeighborInpainting(Inf)`.
- `inpainting`: The inpainting algorithm to use. Options include:
* `NearestNeighborInpainting(maxiter)`: Uses an average
of the valid surrounding values, repeated `maxiter` times.
Default: `NearestNeighborInpainting(Inf)`.
* `ValueInpainting(value)`: Fills all missing values with
the specified constant `value`.

# Examples
```julia
# Use nearest neighbor inpainting with default settings
inpaint_mask!(field, mask)

# Fill missing values with zero
inpaint_mask!(field, mask; inpainting=ValueInpainting(0))

# Fill missing values with a specific temperature
inpaint_mask!(field, mask; inpainting=ValueInpainting(273.15))
```
"""
function inpaint_mask!(field, mask; inpainting=NearestNeighborInpainting(Inf))

Expand Down
4 changes: 4 additions & 0 deletions src/DataWrangling/metadata_field.jl
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ function set_metadata_field!(field, data, metadatum)
return nothing
end

# TODO: sort out elegant way to handle NaNs for sea ice data in GLORYS
@kernel function _set_2d_metadata_field!(field, data, mangling, temp_units, conc_units)
i, j = @index(Global, NTuple)
d = mangle(i, j, data, mangling)
Expand All @@ -223,6 +224,8 @@ end
elseif !isnothing(conc_units)
d = convert_concentration(d, conc_units)
end

d = convert_temperature(d, temp_units)
@inbounds field[i, j, 1] = d
end

Expand All @@ -241,6 +244,7 @@ end
elseif !isnothing(conc_units)
d = convert_concentration(d, conc_units)
end

@inbounds field[i, j, k] = d
end

Expand Down
Loading