Add `scheme` slot to `Value` (`Value{MS}`) and migrate `PerturbationAdvection`'s Center-field path
# Add `scheme` slot to `Value` (`Value{MS}`) and migrate `PerturbationAdvection`'s Center-field path
## Background
Today only the `Open` classification carries a scheme slot ([`boundary_condition_classifications.jl:70-72`](https://github.com/CliMA/Oceananigans.jl/blob/main/src/BoundaryConditions/boundary_condition_classifications.jl#L70-L72)):
```julia
struct Open{MS} <: AbstractBoundaryConditionClassification
scheme :: MS
end
```
The other classifications (`Value`, `Gradient`, `Flux`, `Mixed`) are bare singletons:
```julia
struct Value <: AbstractBoundaryConditionClassification end
```
This is asymmetric. `Open` is documented as "specify the component of a velocity field normal to a boundary" — fundamentally a face-located concept — yet `Open` is currently the only home for *any* scheme, including schemes whose natural home is a Dirichlet condition on a `Center` scalar.
`PerturbationAdvection` is the present-day evidence: it ships under `Open{<:PerturbationAdvection}` with separate halo-fill methods for Face-located fields (u, v, w — semantically correct for Open) and Center-located fields ([`perturbation_advection.jl:272-308`](https://github.com/CliMA/Oceananigans.jl/blob/main/src/BoundaryConditions/perturbation_advection.jl#L272-L308) — tracers, scalars — semantically a stretch).
## Proposed change
Add a scheme slot to `Value`, mirroring `Open`:
```julia
# src/BoundaryConditions/boundary_condition_classifications.jl
struct Value{MS} <: AbstractBoundaryConditionClassification
scheme :: MS
end
Value() = Value(nothing)
(v::Value)() = v
Adapt.adapt_structure(to, v::Value) = Value(adapt(to, v.scheme))
```
And extend the `ValueBoundaryCondition` constructor to accept the kwarg:
```julia
# src/BoundaryConditions/boundary_condition.jl
ValueBoundaryCondition(val; scheme = nothing, kwargs...) =
BoundaryCondition(Value(scheme), val; kwargs...)
```
`Value()` is preserved as a default constructor, so every existing `BoundaryCondition{<:Value}` dispatch keeps matching. The change is backwards-compatible for all existing call sites and any downstream code that pattern-matches on `<:Value`.
Total surface: ~10 lines in two files. No behavior change for any existing code path.
## Implications for `PerturbationAdvection`
With `Value{MS}` available, the principled home for `PerturbationAdvection` splits across two classifications:
```julia
const PAOBC_Face = BoundaryCondition{<:Open{<:PerturbationAdvection}} # u, v, w
const PAOBC_Center = BoundaryCondition{<:Value{<:PerturbationAdvection}} # tracers, ρθ, ρq
```
The user-facing API would split symmetrically:
```julia
# Face fields (boundary value lives on the face — Open is semantically natural)
u_bcs = FieldBoundaryConditions(east = OpenBoundaryCondition(U₀; scheme = PerturbationAdvection(...)))
# Center fields (Dirichlet target nudging the outermost cell — Value is semantically natural)
c_bcs = FieldBoundaryConditions(east = ValueBoundaryCondition(c₀; scheme = PerturbationAdvection(...)))
```
This is the design that would have been chosen if `Value{MS}` had existed when `PerturbationAdvection` was first written. The current "all under `Open`" path for Center fields is a historical artifact of `Open` being the only classification with a scheme slot, not a deliberate semantic choice.
### Suggested migration
Not a hard break — a deprecation cycle:
1. Add `Value{MS}` (this issue).
2. Add `PerturbationAdvection` halo-fill methods that dispatch on `<:Value{<:PerturbationAdvection}` for Center fields. The kernel bodies port directly from the existing Center methods on `<:Open{<:PerturbationAdvection}`.
3. Keep the existing `Open{<:PerturbationAdvection}` Center methods with a deprecation warning recommending users switch to `ValueBoundaryCondition(c₀; scheme = PerturbationAdvection(...))`.
4. After a release or two, retire the `Open` Center path. The Face path (u, v, w) stays under `Open` forever — that's where it belongs.
Tests in `test_boundary_conditions_integration.jl` covering the Center-field `PerturbationAdvection` path (`test_perturbation_advection_tracer_*`) would gain mirrored `Value{MS}` versions during the migration.
## Downstream motivation
Breeze.jl is independently designing an `AcousticRelaxation` scheme for per-substep open-boundary relaxation of ρ and (ρθ) in its compressible-substep solver ([NumericalEarth/Breeze.jl#748](https://github.com/NumericalEarth/Breeze.jl/issues/748), following on from [#747](https://github.com/NumericalEarth/Breeze.jl/pull/747)). The scheme attaches to a `Center` scalar's BC and operates by nudging the outermost cell toward a prescribed wall value — same shape as `PerturbationAdvection`'s Center-field case. Routing it through `Open` would propagate the same semantic stretch; routing through `Value{MS}` is the clean home.
So the same upstream change unblocks a downstream consumer *and* lets Oceananigans tidy up an existing inconsistency in its own code.
## Open questions
- **`Gradient{MS}` and `Flux{MS}` for symmetry?** Worth considering for completeness — the same kind of pattern could exist for Neumann (e.g., a "relax-the-flux" scheme) or for sophisticated flux parameterizations. Not motivated by any concrete consumer right now. Could be done in this issue or deferred.
- **Naming**: keep `MS` as the type parameter name (historical "matching scheme" — same letter that's in `Open{MS}`), or rename to something more descriptive (`S`, `Scheme`)? Consistency with `Open` argues for `MS`.
- **`show` methods**: `Base.summary(bc::OBC{Open{MS}}) where MS = string("OpenBoundaryCondition{$MS}: ", ...)` (`show_boundary_conditions.jl:26`). A mirrored method on `BoundaryCondition{<:Value}` would surface the scheme name. Cheap and consistent.
## References
- [Oceananigans `Open` classification](https://github.com/CliMA/Oceananigans.jl/blob/main/src/BoundaryConditions/boundary_condition_classifications.jl#L60-L78)
- [Oceananigans `PerturbationAdvection`](https://github.com/CliMA/Oceananigans.jl/blob/main/src/BoundaryConditions/perturbation_advection.jl)
- [`ValueBoundaryCondition` constructor](https://github.com/CliMA/Oceananigans.jl/blob/main/src/BoundaryConditions/boundary_condition.jl#L109)
- [NumericalEarth/Breeze.jl#748](https://github.com/NumericalEarth/Breeze.jl/issues/748) — downstream consumer (Breeze's `AcousticRelaxation` scheme refactor).
- [NumericalEarth/Breeze.jl#747](https://github.com/NumericalEarth/Breeze.jl/pull/747) — original Breeze PR where this design conversation started.
1 条评论