Move NetCDF attribute function stubs to main module#5469
Move NetCDF attribute function stubs to main module#5469simone-silvestri wants to merge 6 commits intomainfrom
Conversation
…in module These functions were defined entirely in OceananigansNCDatasetsExt despite not depending on NCDatasets. This prevented downstream extensions (e.g. ClimaSeaIce) from adding methods at compile time, forcing them to use __init__ + @eval which breaks incremental compilation on Julia 1.12. By declaring function stubs in src/OutputWriters and importing them in the extension, any package extension can now extend these functions with a simple `import` + method definition at compile time. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace __init__ + @eval pattern with a direct import and compile-time method definition for default_output_attributes. The old pattern broke incremental compilation on Julia 1.12 because eval into a closed module is no longer allowed. Requires Oceananigans >= 0.106.4 (CliMA/Oceananigans.jl#5469). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
| default_output_attributes, | ||
| add_schedule_metadata!, |
There was a problem hiding this comment.
Related to my comments in CliMA/ClimaSeaIce.jl#127 (review) (including the use of import 🥲), feels like these methods should really live in OutputWriters, not in this extension.
There was a problem hiding this comment.
And the fact that you tried to use @eval to circumvent the impossibility to properly define the methods is a strong indication that the methods just currently live in the wrong places (the extensions, where they have nothing specific to the extensions)
There was a problem hiding this comment.
what do you mean? they do live in OutputWriters; they are just extended here. What specifically do you suggest?
There was a problem hiding this comment.
you mean put this chunk of code in OutputWriters?
#####
##### Variable attributes
#####
default_velocity_attributes(::RectilinearGrid) = Dict(
"u" => Dict("long_name" => "Velocity in the +x-direction.", "units" => "m/s"),
"v" => Dict("long_name" => "Velocity in the +y-direction.", "units" => "m/s"),
"w" => Dict("long_name" => "Velocity in the +z-direction.", "units" => "m/s"))
default_velocity_attributes(::LatitudeLongitudeGrid) = Dict(
"u" => Dict("long_name" => "Velocity in the zonal direction (+ = east).", "units" => "m/s"),
"v" => Dict("long_name" => "Velocity in the meridional direction (+ = north).", "units" => "m/s"),
"w" => Dict("long_name" => "Velocity in the vertical direction (+ = up).", "units" => "m/s"),
"displacement" => Dict("long_name" => "Sea surface height displacement", "units" => "m"))
default_velocity_attributes(ibg::ImmersedBoundaryGrid) = default_velocity_attributes(ibg.underlying_grid)
default_tracer_attributes(::Nothing) = Dict()
default_tracer_attributes(::BuoyancyForce{<:BuoyancyTracer}) = Dict("b" => Dict("long_name" => "Buoyancy", "units" => "m/s²"))
default_tracer_attributes(::BuoyancyForce{<:SeawaterBuoyancy{FT, <:LinearEquationOfState}}) where FT = Dict(
"T" => Dict("long_name" => "Temperature", "units" => "°C"),
"S" => Dict("long_name" => "Salinity", "units" => "practical salinity unit (psu)"))
default_tracer_attributes(::BuoyancyBoussinesqEOSModel) = Dict("T" => Dict("long_name" => "Conservative temperature", "units" => "°C"),
"S" => Dict("long_name" => "Absolute salinity", "units" => "g/kg"))
function default_output_attributes(model)
velocity_attrs = default_velocity_attributes(model.grid)
buoyancy = model isa ShallowWaterModel ? nothing : model.buoyancy
tracer_attrs = default_tracer_attributes(buoyancy)
return merge(velocity_attrs, tracer_attrs)
end
There was a problem hiding this comment.
and
#####
##### Saving schedule metadata as global attributes
#####
add_schedule_metadata!(attributes, schedule) = nothing
function add_schedule_metadata!(global_attributes, schedule::IterationInterval)
global_attributes["schedule"] = "IterationInterval"
global_attributes["interval"] = schedule.interval
global_attributes["output iteration interval"] = "Output was saved every $(schedule.interval) iteration(s)."
return nothing
end
function add_schedule_metadata!(global_attributes, schedule::TimeInterval)
global_attributes["schedule"] = "TimeInterval"
global_attributes["interval"] = schedule.interval
global_attributes["output time interval"] = "Output was saved every $(prettytime(schedule.interval))."
return nothing
end
function add_schedule_metadata!(global_attributes, schedule::WallTimeInterval)
global_attributes["schedule"] = "WallTimeInterval"
global_attributes["interval"] = schedule.interval
global_attributes["output time interval"] =
"Output was saved every $(prettytime(schedule.interval))."
return nothing
end
function add_schedule_metadata!(global_attributes, schedule::AveragedTimeInterval)
global_attributes["schedule"] = "AveragedTimeInterval"
global_attributes["interval"] = schedule.interval
global_attributes["output time interval"] = "Output was time-averaged and saved every $(prettytime(schedule.interval))."
global_attributes["time_averaging_window"] = schedule.window
global_attributes["time averaging window"] = "Output was time averaged with a window size of $(prettytime(schedule.window))"
global_attributes["time_averaging_stride"] = schedule.stride
global_attributes["time averaging stride"] = "Output was time averaged with a stride of $(schedule.stride) iteration(s) within the time averaging window."
return nothing
end
There was a problem hiding this comment.
what do you mean? they do live in
OutputWriters; they are just extended here.
The functions are declared in OutputWriters, the methods are defined in the extensions.
What specifically do you suggest?
To have the methods in OutputWriters, not in the extensions. I don't see anything specific to NCDatasets in those methods.
There was a problem hiding this comment.
yes you're right. there is no dependence on NCDatasets as a package; it is just that the methods are specific to NetCDF. But there is no functional barrier to putting them in OutputWriters. I can do that.
There was a problem hiding this comment.
I guess these methods were initially needed only internally in the extension, and that's fair, but then when they grew to be needed in downstream packages having them in the extension became odd, and really not technically necessary.
…iters These methods have no dependency on NCDatasets and belong in the main OutputWriters module, not the NCDatasets extension. Also removes the now-unused type aliases and imports from the extension. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
OutputWriters is included before BuoyancyFormulations and Models, so the method definitions can't live directly in OutputWriters.jl. Instead, keep stubs in OutputWriters and define the actual methods in a separate file (output_attributes.jl) that is included from Oceananigans.jl after Models. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The file is included at top-level Oceananigans scope before the using .Grids etc. statements, so it needs its own explicit imports. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
there is the problem that |
Summary
default_output_attributesandadd_schedule_metadata!as function stubs insrc/OutputWriters/netcdf_writer.jlOceananigansNCDatasetsExtso the extension extends (rather than defines) these functionsMotivation
These functions don't depend on NCDatasets but were defined entirely inside the extension. This prevented downstream package extensions (e.g.
ClimaSeaIceNCDatasetsExt) from adding methods at compile time, forcing them to use__init__+@evalwhich breaks incremental compilation on Julia 1.12.Companion PR: CliMA/ClimaSeaIce.jl —
ss/fix-netcdf-extension(removes__init__+@evalin favor of a directimport+ method definition)Test plan
🤖 Generated with Claude Code