From b356fe0360d361ad5a0d716ba66e1b7553707f61 Mon Sep 17 00:00:00 2001 From: Gregory Wagner Date: Wed, 3 Sep 2025 13:58:38 -0700 Subject: [PATCH 01/11] Validate location input to FieldBoundaryConditions --- .../field_boundary_conditions.jl | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/BoundaryConditions/field_boundary_conditions.jl b/src/BoundaryConditions/field_boundary_conditions.jl index e8f3284645..6b1ca38b4e 100644 --- a/src/BoundaryConditions/field_boundary_conditions.jl +++ b/src/BoundaryConditions/field_boundary_conditions.jl @@ -138,7 +138,7 @@ FieldBoundaryConditions(default_bounded_bc::BoundaryCondition = NoFluxBoundaryCo FieldBoundaryConditions(west, east, south, north, bottom, top, immersed) """ - FieldBoundaryConditions(grid, location, indices=(:, :, :); + FieldBoundaryConditions(grid, location::Tuple, indices=(:, :, :); west = default_auxiliary_bc(grid, boundary, loc), east = default_auxiliary_bc(grid, boundary, loc), south = default_auxiliary_bc(grid, boundary, loc), @@ -148,7 +148,8 @@ FieldBoundaryConditions(default_bounded_bc::BoundaryCondition = NoFluxBoundaryCo immersed = NoFluxBoundaryCondition()) Return boundary conditions for auxiliary fields (fields whose values are -derived from a model's prognostic fields) on `grid` and at `location`. +derived from a model's prognostic fields) on `grid` and at `location`, +which is a 3-tuple of either `Center()`, `Face()`, or `nothing`. Keyword arguments ================= @@ -171,7 +172,7 @@ and the topology in the boundary-normal direction is used: - `nothing` for `Bounded` directions and `Face`-located fields - `nothing` for `Flat` directions and/or `Nothing`-located fields """ -function FieldBoundaryConditions(grid::AbstractGrid, loc, indices=(:, :, :); +function FieldBoundaryConditions(grid::AbstractGrid, loc::Tuple, indices=(:, :, :); west = default_auxiliary_bc(grid, Val(:west), loc), east = default_auxiliary_bc(grid, Val(:east), loc), south = default_auxiliary_bc(grid, Val(:south), loc), @@ -180,6 +181,14 @@ function FieldBoundaryConditions(grid::AbstractGrid, loc, indices=(:, :, :); top = default_auxiliary_bc(grid, Val(:top), loc), immersed = NoFluxBoundaryCondition()) + for ℓ in loc + if !(ℓ isa Union{Nothing, Face, Center}) + msg = string("Location $ℓ in $loc is not a valid location!", '\n', + "Locations must be Center(), Face(), or nothing.") + throw(ArgumentError(msg)) + end + end + return FieldBoundaryConditions(indices, west, east, south, north, bottom, top, immersed) end From 7b8e507557884f8957ca742f7cc25417ac7921a1 Mon Sep 17 00:00:00 2001 From: Gregory Wagner Date: Wed, 3 Sep 2025 14:01:13 -0700 Subject: [PATCH 02/11] add test --- test/test_boundary_conditions.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/test_boundary_conditions.jl b/test/test_boundary_conditions.jl index ac0e024c57..1b145ccb94 100644 --- a/test/test_boundary_conditions.jl +++ b/test/test_boundary_conditions.jl @@ -256,6 +256,10 @@ end grid = bbb_grid + @test_throws ArgumentError FieldBoundaryConditions(grid, (Center, Center(), Center())) + @test_throws ArgumentError FieldBoundaryConditions(grid, (Center(), Face, Center())) + @test_throws ArgumentError FieldBoundaryConditions(grid, (Center(), Center(), Nothing)) + T_bcs = FieldBoundaryConditions(grid, (Center(), Center(), Center()), east = ValueBoundaryCondition(simple_bc), west = ValueBoundaryCondition(simple_bc), From 08f4542a5a00300af01d95f0a1543cfd0841c28e Mon Sep 17 00:00:00 2001 From: Gregory Wagner Date: Sun, 5 Oct 2025 15:59:38 -0400 Subject: [PATCH 03/11] fixes --- .../field_boundary_conditions.jl | 18 ++++++++++-------- src/MultiRegion/multi_region_field.jl | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/BoundaryConditions/field_boundary_conditions.jl b/src/BoundaryConditions/field_boundary_conditions.jl index a338be37eb..c523bbc072 100644 --- a/src/BoundaryConditions/field_boundary_conditions.jl +++ b/src/BoundaryConditions/field_boundary_conditions.jl @@ -172,14 +172,7 @@ and the topology in the boundary-normal direction is used: - `nothing` for `Bounded` directions and `Face`-located fields - `nothing` for `Flat` directions and/or `Nothing`-located fields """ -function FieldBoundaryConditions(grid::AbstractGrid, loc::Tuple, indices=(:, :, :); - west = default_auxiliary_bc(grid, Val(:west), loc), - east = default_auxiliary_bc(grid, Val(:east), loc), - south = default_auxiliary_bc(grid, Val(:south), loc), - north = default_auxiliary_bc(grid, Val(:north), loc), - bottom = default_auxiliary_bc(grid, Val(:bottom), loc), - top = default_auxiliary_bc(grid, Val(:top), loc), - immersed = DefaultBoundaryCondition()) +function FieldBoundaryConditions(grid::AbstractGrid, loc::Tuple, indices=(:, :, :); kwargs...) for ℓ in loc if !(ℓ isa Union{Nothing, Face, Center}) @@ -189,6 +182,15 @@ function FieldBoundaryConditions(grid::AbstractGrid, loc::Tuple, indices=(:, :, end end + # Build defaults _after_ validating the location + west = get(kwargs, :west, default_auxiliary_bc(grid, Val(:west), loc)) + east = get(kwargs, :east, default_auxiliary_bc(grid, Val(:east), loc)) + south = get(kwargs, :south, default_auxiliary_bc(grid, Val(:south), loc)) + north = get(kwargs, :north, default_auxiliary_bc(grid, Val(:north), loc)) + bottom = get(kwargs, :bottom, default_auxiliary_bc(grid, Val(:bottom), loc)) + top = get(kwargs, :top, default_auxiliary_bc(grid, Val(:top), loc)) + immersed = get(kwargs, :immersed, DefaultBoundaryCondition()) + bcs = FieldBoundaryConditions(indices, west, east, south, north, bottom, top, immersed) return regularize_field_boundary_conditions(bcs, grid, loc) end diff --git a/src/MultiRegion/multi_region_field.jl b/src/MultiRegion/multi_region_field.jl index b425df8220..49b8b59308 100644 --- a/src/MultiRegion/multi_region_field.jl +++ b/src/MultiRegion/multi_region_field.jl @@ -193,7 +193,7 @@ function inject_regional_bcs(grid, connectivity, loc, indices; return FieldBoundaryConditions(indices, west, east, south, north, bottom, top, immersed) end -FieldBoundaryConditions(mrg::MultiRegionGrids, loc, indices; kwargs...) = +FieldBoundaryConditions(mrg::MultiRegionGrids, loc::Tuple, indices::MultiRegionObject; kwargs...) = construct_regionally(inject_regional_bcs, mrg, mrg.connectivity, Reference(loc), indices; kwargs...) function Base.show(io::IO, field::MultiRegionField) From 35e5adc8d21f0638d5a445b0a9f56f08713abb38 Mon Sep 17 00:00:00 2001 From: Gregory Wagner Date: Tue, 14 Oct 2025 06:50:25 -0400 Subject: [PATCH 04/11] fix test --- test/test_broadcasting.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/test_broadcasting.jl b/test/test_broadcasting.jl index e622c7412b..1bdcf6cdb4 100644 --- a/test/test_broadcasting.jl +++ b/test/test_broadcasting.jl @@ -41,7 +41,9 @@ include("dependencies_for_runtests.jl") a2 = CenterField(three_point_grid) - b2_bcs = FieldBoundaryConditions(grid, (Center, Center, Face), top=OpenBoundaryCondition(0), bottom=OpenBoundaryCondition(0)) + + loc = (Center(), Center(), Face()) + b2_bcs = FieldBoundaryConditions(grid, loc, top=OpenBoundaryCondition(0), bottom=OpenBoundaryCondition(0)) b2 = ZFaceField(three_point_grid, boundary_conditions=b2_bcs) b2 .= 1 From ac753eac14e58191cb80e7c69643b532cbabbd1f Mon Sep 17 00:00:00 2001 From: Gregory Wagner Date: Tue, 14 Oct 2025 06:52:59 -0400 Subject: [PATCH 05/11] fix docs --- docs/src/model_setup/boundary_conditions.md | 16 ++++++++-------- docs/src/model_setup/forcing_functions.md | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/src/model_setup/boundary_conditions.md b/docs/src/model_setup/boundary_conditions.md index 2f03f15e69..e69c87a3e3 100644 --- a/docs/src/model_setup/boundary_conditions.md +++ b/docs/src/model_setup/boundary_conditions.md @@ -153,18 +153,18 @@ The default boundary condition can be changed by passing a positional argument t as in ```jldoctest -julia> no_slip_bc = ValueBoundaryCondition(0.0) -ValueBoundaryCondition: 0.0 +julia> no_slip_bc = ValueBoundaryCondition(0) +ValueBoundaryCondition: 0 julia> free_slip_surface_bcs = FieldBoundaryConditions(no_slip_bc, top=FluxBoundaryCondition(nothing)) Oceananigans.FieldBoundaryConditions, with boundary conditions -├── west: DefaultBoundaryCondition (ValueBoundaryCondition: 0.0) -├── east: DefaultBoundaryCondition (ValueBoundaryCondition: 0.0) -├── south: DefaultBoundaryCondition (ValueBoundaryCondition: 0.0) -├── north: DefaultBoundaryCondition (ValueBoundaryCondition: 0.0) -├── bottom: DefaultBoundaryCondition (ValueBoundaryCondition: 0.0) +├── west: DefaultBoundaryCondition (ValueBoundaryCondition: 0) +├── east: DefaultBoundaryCondition (ValueBoundaryCondition: 0) +├── south: DefaultBoundaryCondition (ValueBoundaryCondition: 0) +├── north: DefaultBoundaryCondition (ValueBoundaryCondition: 0) +├── bottom: DefaultBoundaryCondition (ValueBoundaryCondition: 0) ├── top: FluxBoundaryCondition: Nothing -└── immersed: DefaultBoundaryCondition (ValueBoundaryCondition: 0.0) +└── immersed: DefaultBoundaryCondition (ValueBoundaryCondition: 0) ``` ## Boundary condition structures diff --git a/docs/src/model_setup/forcing_functions.md b/docs/src/model_setup/forcing_functions.md index 504f18539b..59b7458f4d 100644 --- a/docs/src/model_setup/forcing_functions.md +++ b/docs/src/model_setup/forcing_functions.md @@ -266,7 +266,7 @@ model.forcing.u # output ContinuousForcing{Nothing} at (Face, Center, Center) -├── func: Relaxation(rate=0.01, mask=exp(-(z + 1.0)^2 / (2 * 0.1^2)), target=0) +├── func: Relaxation(rate=0.01, mask=exp(-(z + 1)^2 / (2 * 0.1^2)), target=0) ├── parameters: nothing └── field dependencies: (:u,) ``` @@ -276,7 +276,7 @@ model.forcing.T # output ContinuousForcing{Nothing} at (Center, Center, Center) -├── func: Relaxation(rate=0.01, mask=exp(-(z + 1.0)^2 / (2 * 0.1^2)), target=20.0 + 0.001 * z) +├── func: Relaxation(rate=0.01, mask=exp(-(z + 1)^2 / (2 * 0.1^2)), target=20 + 0.001 * z) ├── parameters: nothing └── field dependencies: (:T,) ``` @@ -324,7 +324,7 @@ grid = RectilinearGrid(size=(32, 32, 32), x=(-10, 10), y=(-10, 10), z=(-4, 4), topology=(Periodic, Periodic, Bounded)) no_penetration = ImpenetrableBoundaryCondition() -slip_bcs = FieldBoundaryConditions(grid, (Center, Center, Face), +slip_bcs = FieldBoundaryConditions(grid, (Center(), Center(), Face()), top=no_penetration, bottom=no_penetration) w_slip = ZFaceField(grid, boundary_conditions=slip_bcs) From 6ac5bcba4c6ac044b13485599924cc0ab97b8f33 Mon Sep 17 00:00:00 2001 From: Gregory Wagner Date: Tue, 14 Oct 2025 08:37:46 -0400 Subject: [PATCH 06/11] fix doctest --- docs/src/model_setup/forcing_functions.md | 35 +++++++++++------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/docs/src/model_setup/forcing_functions.md b/docs/src/model_setup/forcing_functions.md index 59b7458f4d..6278c0d17f 100644 --- a/docs/src/model_setup/forcing_functions.md +++ b/docs/src/model_setup/forcing_functions.md @@ -21,7 +21,7 @@ By default, momentum and tracer forcing functions are assumed to be functions of u_forcing(x, y, z, t) = exp(z) * cos(x) * sin(t) grid = RectilinearGrid(size=(1, 1, 1), extent=(1, 1, 1)) -model = NonhydrostaticModel(grid=grid, forcing=(u=u_forcing,)) +model = NonhydrostaticModel(; grid, forcing=(; u=u_forcing)) model.forcing.u @@ -59,16 +59,15 @@ _(i)_ a single scalar parameter `s`, and _(ii)_ a `NamedTuple` of parameters, `p ```jldoctest parameterized_forcing # Forcing that depends on a scalar parameter `s` u_forcing_func(x, y, z, t, s) = s * z - u_forcing = Forcing(u_forcing_func, parameters=0.1) # Forcing that depends on a `NamedTuple` of parameters `p` T_forcing_func(x, y, z, t, p) = - p.μ * exp(z / p.λ) * cos(p.k * x) * sin(p.ω * t) - T_forcing = Forcing(T_forcing_func, parameters=(μ=1, λ=0.5, k=2π, ω=4π)) grid = RectilinearGrid(size=(1, 1, 1), extent=(1, 1, 1)) -model = NonhydrostaticModel(grid=grid, forcing=(u=u_forcing, T=T_forcing), buoyancy=SeawaterBuoyancy(), tracers=(:T, :S)) +forcing = (u=u_forcing, T=T_forcing) +model = NonhydrostaticModel(; grid, forcing, buoyancy=SeawaterBuoyancy(), tracers=(:T, :S)) model.forcing.T @@ -182,17 +181,16 @@ using Oceananigans.Operators: ∂zᶠᶜᶠ, ℑxzᶠᵃᶜ function u_forcing_func(i, j, k, grid, clock, model_fields, ε) # The vertical derivative of buoyancy, interpolated to the u-velocity location: N² = ℑxzᶠᵃᶜ(i, j, k, grid, ∂zᶠᶜᶠ, model_fields.b) - - # Set to zero in unstable stratification where N² < 0: - N² = max(N², zero(typeof(N²))) - - return @inbounds - ε * sqrt(N²) * model_fields.u[i, j, k] + N²⁺ = max(0, N²) # limit buoyancy frequency to strictly positive values + u_ijk = @inbounds model_fields.u[i, j, k] + return - ε * sqrt(N²⁺) * u_ijk end u_forcing = Forcing(u_forcing_func, discrete_form=true, parameters=1e-3) grid = RectilinearGrid(size=(1, 1, 1), extent=(1, 1, 1)) -model = NonhydrostaticModel(grid=grid, tracers=:b, buoyancy=BuoyancyTracer(), forcing=(u=u_forcing, b=b_forcing)) +forcing = (u=u_forcing, b=b_forcing) +model = NonhydrostaticModel(; grid, forcing, tracers=:b, buoyancy=BuoyancyTracer()) model.forcing.b @@ -227,7 +225,7 @@ of the velocity field are damped to zero everywhere on a time-scale of 1000 seco damping = Relaxation(rate = 1/1000) grid = RectilinearGrid(size=(1, 1, 1), extent=(1, 1, 1)) -model = NonhydrostaticModel(grid=grid, forcing=(u=damping, v=damping, w=damping)) +model = NonhydrostaticModel(; grid, forcing=(u=damping, v=damping, w=damping)) model.forcing.w @@ -250,23 +248,24 @@ velocity fields to zero and restores temperature to a linear gradient in the bot ```jldoctest sponge_layer grid = RectilinearGrid(size=(1, 1, 1), x=(0, 1), y=(0, 1), z=(-1, 0)) - damping_rate = 1/100 # relax fields on a 100 second time-scale +damping_rate = 1/100 # relax fields on a 100 second time-scale temperature_gradient = 0.001 # ⁰C m⁻¹ - surface_temperature = 20 # ⁰C (at z=0) +surface_temperature = 20 # ⁰C (at z=0) target_temperature = LinearTarget{:z}(intercept=surface_temperature, gradient=temperature_gradient) - bottom_mask = GaussianMask{:z}(center=-grid.Lz, width=grid.Lz/10) +bottom_mask = GaussianMask{:z}(center=-grid.Lz, width=grid.Lz/10) uvw_sponge = Relaxation(rate=damping_rate, mask=bottom_mask) - T_sponge = Relaxation(rate=damping_rate, mask=bottom_mask, target=target_temperature) +T_sponge = Relaxation(rate=damping_rate, mask=bottom_mask, target=target_temperature) -model = NonhydrostaticModel(grid=grid, forcing=(u=uvw_sponge, v=uvw_sponge, w=uvw_sponge, T=T_sponge), buoyancy=SeawaterBuoyancy(), tracers=(:T, :S)) +forcing=(u=uvw_sponge, v=uvw_sponge, w=uvw_sponge, T=T_sponge) +model = NonhydrostaticModel(; grid, forcing, buoyancy=SeawaterBuoyancy(), tracers=(:T, :S)) model.forcing.u # output ContinuousForcing{Nothing} at (Face, Center, Center) -├── func: Relaxation(rate=0.01, mask=exp(-(z + 1)^2 / (2 * 0.1^2)), target=0) +├── func: Relaxation(rate=0.01, mask=exp(-(z + 1.0)^2 / (2 * 0.1^2)), target=0) ├── parameters: nothing └── field dependencies: (:u,) ``` @@ -276,7 +275,7 @@ model.forcing.T # output ContinuousForcing{Nothing} at (Center, Center, Center) -├── func: Relaxation(rate=0.01, mask=exp(-(z + 1)^2 / (2 * 0.1^2)), target=20 + 0.001 * z) +├── func: Relaxation(rate=0.01, mask=exp(-(z + 1.0)^2 / (2 * 0.1^2)), target=20 + 0.001 * z) ├── parameters: nothing └── field dependencies: (:T,) ``` From 03761a5b418b3ad6e97dce3c6d705b6d15fdb9d6 Mon Sep 17 00:00:00 2001 From: Gregory Wagner Date: Tue, 14 Oct 2025 08:37:50 -0400 Subject: [PATCH 07/11] fix test --- test/test_forcings.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test_forcings.jl b/test/test_forcings.jl index 839080d327..e7f3622e1e 100644 --- a/test/test_forcings.jl +++ b/test/test_forcings.jl @@ -163,7 +163,8 @@ function advective_and_multiple_forcing(arch) constant_slip = AdvectiveForcing(w=1) zero_slip = AdvectiveForcing(w=0) no_penetration = ImpenetrableBoundaryCondition() - slip_bcs = FieldBoundaryConditions(grid, (Center, Center, Face), top=no_penetration, bottom=no_penetration) + loc = (Center(), Center(), Face()) + slip_bcs = FieldBoundaryConditions(grid, loc, top=no_penetration, bottom=no_penetration) slip_velocity = ZFaceField(grid, boundary_conditions=slip_bcs) set!(slip_velocity, 1) velocity_field_slip = AdvectiveForcing(w=slip_velocity) From 560f2bd957957229700776f0282e5d706bb8135f Mon Sep 17 00:00:00 2001 From: Gregory Wagner Date: Tue, 14 Oct 2025 08:54:38 -0400 Subject: [PATCH 08/11] some cleanup --- .../multi_region_boundary_conditions.jl | 78 ++++++++++--------- test/test_multi_region_advection_diffusion.jl | 37 +++++---- 2 files changed, 63 insertions(+), 52 deletions(-) diff --git a/src/MultiRegion/multi_region_boundary_conditions.jl b/src/MultiRegion/multi_region_boundary_conditions.jl index b0011be7a6..0c567ecaeb 100644 --- a/src/MultiRegion/multi_region_boundary_conditions.jl +++ b/src/MultiRegion/multi_region_boundary_conditions.jl @@ -100,8 +100,11 @@ function (::MultiRegionFillHalo{<:WestAndEast})(c, westbc, eastbc, loc, grid, bu device_copy_to!(westdst, westsrc) device_copy_to!(eastdst, eastsrc) - view(parent(c), 1:H, :, :) .= westdst - view(parent(c), N+H+1:N+2H, :, :) .= eastdst + west_halo = view(parent(c), 1:H, :, :) + west_halo .= westdst + + east_halo = view(parent(c), N+H+1:N+2H, :, :) + east_halo .= eastdst return nothing end @@ -119,8 +122,11 @@ function (::MultiRegionFillHalo{<:SouthAndNorth})(c, southbc, northbc, loc, grid device_copy_to!(southdst, southsrc) device_copy_to!(northdst, northsrc) - view(parent(c), :, 1:H, :) .= southdst - view(parent(c), :, N+H+1:N+2H, :) .= northdst + south_halo = view(parent(c), :, 1:H, :) + south_halo .= southdst + + north_halo = view(parent(c), :, N+H+1:N+2H, :) + north_halo .= northdst return nothing end @@ -138,8 +144,8 @@ function (::MultiRegionFillHalo{<:West})(c, bc, loc, grid, buffers) device_copy_to!(dst, src) - p = view(parent(c), 1:H, :, :) - p .= dst + west_halo = view(parent(c), 1:H, :, :) + west_halo .= dst return nothing end @@ -153,8 +159,8 @@ function (::MultiRegionFillHalo{<:East})(c, bc, loc, grid, buffers) device_copy_to!(dst, src) - p = view(parent(c), N+H+1:N+2H, :, :) - p .= dst + east_halo = view(parent(c), N+H+1:N+2H, :, :) + east_halo .= dst return nothing end @@ -168,8 +174,8 @@ function (::MultiRegionFillHalo{<:South})(c, bc, loc, grid, buffers) device_copy_to!(dst, src) - p = view(parent(c), :, 1:H, :) - p .= dst + south_halo = view(parent(c), :, 1:H, :) + south_halo .= dst return nothing end @@ -183,8 +189,8 @@ function (::MultiRegionFillHalo{<:North})(c, bc, loc, grid, buffers) device_copy_to!(dst, src) - p = view(parent(c), :, N+H+1:N+2H, :) - p .= dst + north_halo = view(parent(c), :, N+H+1:N+2H, :) + north_halo .= dst return nothing end @@ -194,15 +200,15 @@ end ##### @inline getregion(fc::FieldBoundaryConditions, i) = - FieldBoundaryConditions(_getregion(fc.west, i), - _getregion(fc.east, i), - _getregion(fc.south, i), - _getregion(fc.north, i), - _getregion(fc.bottom, i), - _getregion(fc.top, i), - fc.immersed, - fc.kernels, - _getregion(fc.ordered_bcs, i)) + FieldBoundaryConditions(_getregion(fc.west, i), + _getregion(fc.east, i), + _getregion(fc.south, i), + _getregion(fc.north, i), + _getregion(fc.bottom, i), + _getregion(fc.top, i), + fc.immersed, + fc.kernels, + _getregion(fc.ordered_bcs, i)) @inline getregion(bc::BoundaryCondition, i) = BoundaryCondition(bc.classification, _getregion(bc.condition, i)) @@ -214,27 +220,27 @@ end cf.field_dependencies_interp) @inline getregion(df::DiscreteBoundaryFunction, i) = - DiscreteBoundaryFunction(_getregion(df.func, i), _getregion(df.parameters, i)) + DiscreteBoundaryFunction(_getregion(df.func, i), _getregion(df.parameters, i)) @inline _getregion(fc::FieldBoundaryConditions, i) = - FieldBoundaryConditions(getregion(fc.west, i), - getregion(fc.east, i), - getregion(fc.south, i), - getregion(fc.north, i), - getregion(fc.bottom, i), - getregion(fc.top, i), - fc.immersed, - fc.kernels, - getregion(fc.ordered_bcs, i)) + FieldBoundaryConditions(getregion(fc.west, i), + getregion(fc.east, i), + getregion(fc.south, i), + getregion(fc.north, i), + getregion(fc.bottom, i), + getregion(fc.top, i), + fc.immersed, + fc.kernels, + getregion(fc.ordered_bcs, i)) @inline _getregion(bc::BoundaryCondition, i) = BoundaryCondition(bc.classification, getregion(bc.condition, i)) @inline _getregion(cf::ContinuousBoundaryFunction{X, Y, Z, I}, i) where {X, Y, Z, I} = - ContinuousBoundaryFunction{X, Y, Z, I}(cf.func, - getregion(cf.parameters, i), - cf.field_dependencies, - cf.field_dependencies_indices, - cf.field_dependencies_interp) + ContinuousBoundaryFunction{X, Y, Z, I}(cf.func, + getregion(cf.parameters, i), + cf.field_dependencies, + cf.field_dependencies_indices, + cf.field_dependencies_interp) @inline _getregion(df::DiscreteBoundaryFunction, i) = DiscreteBoundaryFunction(getregion(df.func, i), getregion(df.parameters, i)) diff --git a/test/test_multi_region_advection_diffusion.jl b/test/test_multi_region_advection_diffusion.jl index 735cf20b83..86ff75da7e 100644 --- a/test/test_multi_region_advection_diffusion.jl +++ b/test/test_multi_region_advection_diffusion.jl @@ -9,7 +9,7 @@ function Δ_min(grid) return min(Δx_min, Δy_min) end -function solid_body_tracer_advection_test(grid; P = XPartition, regions = 1) +function solid_body_tracer_advection_test(grid; partition=XPartition, regions=1) if grid isa RectilinearGrid L = 0.1 @@ -21,7 +21,7 @@ function solid_body_tracer_advection_test(grid; P = XPartition, regions = 1) cᵢ(x, y, z) = Gaussian(x, 0, L) eᵢ(x, y, z) = Gaussian(x, y, L) - mrg = MultiRegionGrid(grid, partition = P(regions)) + mrg = MultiRegionGrid(grid, partition = partition(regions)) model = HydrostaticFreeSurfaceModel(grid = mrg, tracers = (:c, :e), @@ -47,9 +47,9 @@ function solid_body_tracer_advection_test(grid; P = XPartition, regions = 1) return model.tracers end -function solid_body_rotation_test(grid; P = XPartition, regions = 1) +function solid_body_rotation_test(grid; partition=XPartition, regions=1) - mrg = MultiRegionGrid(grid, partition = P(regions)) + mrg = MultiRegionGrid(grid, partition=partition(regions)) free_surface = ExplicitFreeSurface(gravitational_acceleration = 1) coriolis = HydrostaticSphericalCoriolis(rotation_rate = 1) @@ -82,9 +82,9 @@ function solid_body_rotation_test(grid; P = XPartition, regions = 1) return merge(model.velocities, model.tracers, (; η = model.free_surface.η)) end -function diffusion_cosine_test(grid; P = XPartition, regions = 1, closure, field_name = :c) +function diffusion_cosine_test(grid; partition=XPartition, regions = 1, closure, field_name = :c) - mrg = MultiRegionGrid(grid, partition = P(regions)) + mrg = MultiRegionGrid(grid, partition=partition(regions)) # For MultiRegionGrids with regions > 1, the SplitExplicitFreeSurface extends the # halo region in the horizontal. Because the extented halo region size cannot exceed @@ -121,7 +121,7 @@ end Nx = Ny = 32 -partitioning = [XPartition] +partitions_to_test = [XPartition] for arch in archs grid_rect = RectilinearGrid(arch, @@ -149,9 +149,11 @@ for arch in archs cs = Array(interior(cs)) es = Array(interior(es)) - for regions in (2,), P in partitioning - @info " Testing $regions $(P)s on $(typeof(grid).name.wrapper) on the $arch" - c, e = solid_body_tracer_advection_test(grid; P=P, regions=regions) + for partition in partitions_to_test + regions = 2 + G = typeof(grid).name.wrapper + @info " Testing $regions $partition on $G on the $arch" + c, e = solid_body_tracer_advection_test(grid; partition, regions) c = interior(reconstruct_global_field(c)) e = interior(reconstruct_global_field(e)) @@ -181,9 +183,11 @@ for arch in archs cs = Array(interior(cs)) ηs = Array(interior(ηs)) - for regions in (2,), P in partitioning - @info " Testing $regions $(P)s on $(typeof(grid).name.wrapper) on the $arch" - u, v, w, c, η = solid_body_rotation_test(grid; P=P, regions=regions) + for partition = partitions_to_test + regions = 2 + G = typeof(grid).name.wrapper + @info " Testing $regions $partition on $G on the $arch" + u, v, w, c, η = solid_body_rotation_test(grid; partition, regions=regions) u = interior(reconstruct_global_field(u)) v = interior(reconstruct_global_field(v)) @@ -218,10 +222,11 @@ for arch in archs fs = diffusion_cosine_test(grid; closure, field_name, regions = 1) fs = Array(interior(fs)) - for regions in (2,), P in partitioning - @info " Testing diffusion of $field_name on $regions $(P)s with $(typeof(closure).name.wrapper) on $arch" + for partition in partitions_to_test + regions = 2 + @info " Testing diffusion of $field_name on $regions $(partition)s with $(typeof(closure).name.wrapper) on $arch" - f = diffusion_cosine_test(grid; closure, P, field_name, regions) + f = diffusion_cosine_test(grid; closure, partition, field_name, regions) f = interior(reconstruct_global_field(f)) @test all(isapprox(f, fs, atol=1e-20, rtol=1e-15)) From b744649aca8a34c528d590618a293d5ecfc9c2bd Mon Sep 17 00:00:00 2001 From: Gregory Wagner Date: Tue, 14 Oct 2025 09:28:48 -0400 Subject: [PATCH 09/11] fix doctest --- docs/src/model_setup/forcing_functions.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/src/model_setup/forcing_functions.md b/docs/src/model_setup/forcing_functions.md index 6278c0d17f..9c36ef8c5b 100644 --- a/docs/src/model_setup/forcing_functions.md +++ b/docs/src/model_setup/forcing_functions.md @@ -112,7 +112,8 @@ S_forcing_func(x, y, z, t, S, μ) = - μ * S S_forcing = Forcing(S_forcing_func, parameters=0.01, field_dependencies=:S) grid = RectilinearGrid(size=(1, 1, 1), extent=(1, 1, 1)) -model = NonhydrostaticModel(grid=grid, forcing=(w=w_forcing, S=S_forcing), buoyancy=SeawaterBuoyancy(), tracers=(:T, :S)) +forcing = (w=w_forcing, S=S_forcing) +model = NonhydrostaticModel(; grid, forcing, buoyancy=SeawaterBuoyancy(), tracers=(:T, :S)) model.forcing.w @@ -275,7 +276,7 @@ model.forcing.T # output ContinuousForcing{Nothing} at (Center, Center, Center) -├── func: Relaxation(rate=0.01, mask=exp(-(z + 1.0)^2 / (2 * 0.1^2)), target=20 + 0.001 * z) +├── func: Relaxation(rate=0.01, mask=exp(-(z + 1.0)^2 / (2 * 0.1^2)), target=20.0 + 0.001 * z) ├── parameters: nothing └── field dependencies: (:T,) ``` From ba6a8b01232398aee30c644c8b11479cbfeee80f Mon Sep 17 00:00:00 2001 From: Gregory Wagner Date: Tue, 14 Oct 2025 09:36:32 -0400 Subject: [PATCH 10/11] rm unnecessary type param --- src/BoundaryConditions/continuous_boundary_function.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BoundaryConditions/continuous_boundary_function.jl b/src/BoundaryConditions/continuous_boundary_function.jl index ab87e8f324..48839e6177 100644 --- a/src/BoundaryConditions/continuous_boundary_function.jl +++ b/src/BoundaryConditions/continuous_boundary_function.jl @@ -74,7 +74,7 @@ The regularization of `bc.condition::ContinuousBoundaryFunction` requries of the boundary. """ function regularize_boundary_condition(boundary_func::ContinuousBoundaryFunction, - grid, loc, dim, Side, field_names) where C + grid, loc, dim, Side, field_names) # Set boundary-normal location to Nothing: LX, LY, LZ = Tuple(i == dim ? Nothing : destantiate(loc[i]) for i = 1:3) From 930606c529439c2315c52c209f5aa4faef378738 Mon Sep 17 00:00:00 2001 From: Gregory Wagner Date: Tue, 14 Oct 2025 09:38:24 -0400 Subject: [PATCH 11/11] import FieldBoundaryConditions --- src/MultiRegion/multi_region_field.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MultiRegion/multi_region_field.jl b/src/MultiRegion/multi_region_field.jl index 49b8b59308..0209107beb 100644 --- a/src/MultiRegion/multi_region_field.jl +++ b/src/MultiRegion/multi_region_field.jl @@ -7,7 +7,7 @@ using Oceananigans.OutputWriters: output_indices using Base: @propagate_inbounds import Oceananigans.DistributedComputations: reconstruct_global_field, CommunicationBuffers -import Oceananigans.BoundaryConditions: regularize_field_boundary_conditions +import Oceananigans.BoundaryConditions: regularize_field_boundary_conditions, FieldBoundaryConditions import Oceananigans.Grids: xnodes, ynodes import Oceananigans.Fields: set!, compute!, compute_at!, interior, validate_field_data, validate_boundary_conditions import Oceananigans.Fields: validate_indices, communication_buffers