@@ -198,15 +198,10 @@ function OceananigansSimulation(
198198 # Allocate space for a Field of UVVectors, which we need for remapping momentum fluxes
199199 temp_uv_vec = CC. Fields. Field (CC. Geometry. UVVector{FT}, boundary_space)
200200
201- # polar-exclusion mask
202- # Precompute polar-exclusion flux masks once (zeros ocean surface fluxes where |lat| ≥ 78° to avoid instability).
203- # _centers = cell-centered (T, S); _u = Face/Center (u); _v = Center/Face (v).
204- polar_exclusion_flux_mask_centers =
205- ocean_flux_highlat_mask (grid; location = (OC. Center (), OC. Center (), OC. Center ()))
206- polar_exclusion_flux_mask_u =
207- ocean_flux_highlat_mask (grid; location = (OC. Face (), OC. Center (), OC. Center ()))
208- polar_exclusion_flux_mask_v =
209- ocean_flux_highlat_mask (grid; location = (OC. Center (), OC. Face (), OC. Center ()))
201+ # Precompute mask (zero where |lat| ≥ 78°) for removing ocean surface fluxes at the poles
202+ # This mask lives on cell centers
203+ # (Will be removed when we switch to other grids)
204+ polar_mask = ocean_polar_mask (grid; location = (OC. Center (), OC. Center (), OC. Center ()))
210205
211206 remapping = (;
212207 remapper_cc,
@@ -216,10 +211,7 @@ function OceananigansSimulation(
216211 scratch_arr2,
217212 scratch_arr3,
218213 temp_uv_vec,
219- # polar-exclusion mask
220- polar_exclusion_flux_mask_centers,
221- polar_exclusion_flux_mask_u,
222- polar_exclusion_flux_mask_v,
214+ polar_mask,
223215 )
224216
225217 # COARE3 roughness params (allocated once, reused each timestep)
@@ -310,17 +302,17 @@ function FieldExchanger.resolve_area_fractions!(
310302 ocean_fraction = Interfacer. get_field (ocean_sim, Val (:area_fraction ))
311303 ice_fraction = Interfacer. get_field (ice_sim, Val (:area_fraction ))
312304
313- # Polar mask: 1 where |lat| ≥ 78° (same band used to zero ocean fluxes)
305+ # Polar mask: 0 where |lat| ≥ 78° (same band used to zero ocean fluxes)
314306 boundary_space = axes (ocean_fraction)
315307 FT = CC. Spaces. undertype (boundary_space)
316308 lat = CC. Fields. coordinate_field (boundary_space). lat
317309 polar_mask = CC. Fields. zeros (boundary_space)
318- polar_mask .= abs .(lat) .>= FT (78 )
310+ polar_mask .= abs .(lat) .< FT (78 )
319311
320- # Set land fraction to 1 and ice/ocean fraction to 0 where polar_mask is 1
321- @. land_fraction = ifelse .( polar_mask == FT ( 1 ), FT ( 1 ), land_fraction )
322- @. ice_fraction = ifelse .( polar_mask == FT ( 1 ), FT ( 0 ), ice_fraction)
323- @. ocean_fraction = ifelse .( polar_mask == FT ( 1 ), FT ( 0 ), ocean_fraction)
312+ # Set land fraction to 1 and ice/ocean fraction to 0 where polar_mask is 0
313+ @. land_fraction = polar_mask * land_fraction + ( 1 - polar_mask )
314+ @. ice_fraction = polar_mask * ice_fraction
315+ @. ocean_fraction = polar_mask * ocean_fraction
324316 end
325317
326318 # Update the ice concentration field in the ocean simulation
@@ -361,15 +353,14 @@ Interfacer.get_field(sim::OceananigansSimulation, ::Val{:surface_temperature}) =
361353 sim. ocean. model. tracers. T + sim. ocean_properties. C_to_K # convert from Celsius to Kelvin
362354
363355"""
364- ocean_flux_highlat_mask (grid; location)
356+ ocean_polar_mask (grid; location)
365357
366- Build the ocean flux high latitude mask once at setup.
358+ Build the ocean polar mask once at setup.
367359Currently we define ocean between 80°S to 80°N with 2 degree overlap in the coupler mask.
368360Returns a 2D mask (1.0 where |lat| < 78°, 0.0 elsewhere). This mask is on the ocean grid
369361(unlike the polar mask which is defined on the boundary_space)
370362"""
371- # polar-exclusion mask
372- function ocean_flux_highlat_mask (grid; location = (OC. Center (), OC. Center (), OC. Center ()))
363+ function ocean_polar_mask (grid; location = (OC. Center (), OC. Center (), OC. Center ()))
373364 polar_flux_lat_deg = 78.0 # zero fluxes where |lat| ≥ this (same band as polar_mask for atmosphere)
374365
375366 # latitude nodes: a StepRangeLen of size grid.Ny *in degrees*
@@ -409,8 +400,8 @@ function FluxCalculator.update_turbulent_fluxes!(sim::OceananigansSimulation, fi
409400 (; reference_density, heat_capacity) = sim. ocean_properties
410401 grid = sim. ocean. model. grid
411402 ice_concentration_field = sim. ice_concentration
412- # polar-exclusion mask
413- polar_excl_centers = sim. remapping. polar_exclusion_flux_mask_centers
403+ # for masking out the poles
404+ polar_mask = sim. remapping. polar_mask
414405
415406 # Convert the momentum fluxes from contravariant to Cartesian basis
416407 contravariant_to_cartesian! (sim. remapping. temp_uv_vec, F_turb_ρτxz, F_turb_ρτyz)
@@ -435,7 +426,11 @@ function FluxCalculator.update_turbulent_fluxes!(sim::OceananigansSimulation, fi
435426 F_turb_ρτxz_cell = sim. remapping. scratch_cc1
436427 F_turb_ρτyz_cell = sim. remapping. scratch_cc2
437428
438- # Weight by (1 - sea ice concentration); polar-exclusion mask applied via ifelse below
429+ # mask out the poles
430+ @. F_turb_ρτxz_cell = polar_mask * F_turb_ρτxz_cell
431+ @. F_turb_ρτyz_cell = polar_mask * F_turb_ρτyz_cell
432+
433+ # Weight by (1 - sea ice concentration)
439434 ice_concentration = OC. interior (ice_concentration_field, :, :, 1 )
440435 OC. interior (F_turb_ρτxz_cell, :, :, 1 ) .=
441436 OC. interior (F_turb_ρτxz_cell, :, :, 1 ) .* (1.0 .- ice_concentration) ./
@@ -453,13 +448,6 @@ function FluxCalculator.update_turbulent_fluxes!(sim::OceananigansSimulation, fi
453448 F_turb_ρτxz_cell,
454449 F_turb_ρτyz_cell,
455450 )
456- # polar-exclusion mask
457- polar_excl_u = sim. remapping. polar_exclusion_flux_mask_u
458- polar_excl_v = sim. remapping. polar_exclusion_flux_mask_v
459- flux_u = OC. interior (oc_flux_u, :, :, 1 )
460- flux_v = OC. interior (oc_flux_v, :, :, 1 )
461- flux_u .= ifelse .(polar_excl_u .≈ 0 , zero (flux_u), flux_u)
462- flux_v .= ifelse .(polar_excl_v .≈ 0 , zero (flux_v), flux_v)
463451
464452 # Remap the latent and sensible heat fluxes using scratch arrays
465453 CC. Remapping. interpolate! (sim. remapping. scratch_arr1, sim. remapping. remapper_cc, F_lh) # latent heat flux
@@ -473,9 +461,9 @@ function FluxCalculator.update_turbulent_fluxes!(sim::OceananigansSimulation, fi
473461 # everything on the surface, but later we will need to account for this.
474462 # One way we can do this is using directly ClimaOcean
475463 oc_flux_T = surface_flux (sim. ocean. model. tracers. T)
476- # polar-exclusion mask
477- @. remapped_F_lh = ifelse (polar_excl_centers .≈ 0 , zero ( remapped_F_lh), remapped_F_lh)
478- @. remapped_F_sh = ifelse (polar_excl_centers .≈ 0 , zero ( remapped_F_sh), remapped_F_sh)
464+ # mask out the poles
465+ @. remapped_F_lh = polar_mask * remapped_F_lh
466+ @. remapped_F_sh = polar_mask * remapped_F_sh
479467 OC. interior (oc_flux_T, :, :, 1 ) .+ =
480468 (1.0 .- ice_concentration) .* (remapped_F_lh .+ remapped_F_sh) ./
481469 (reference_density * heat_capacity)
@@ -490,12 +478,8 @@ function FluxCalculator.update_turbulent_fluxes!(sim::OceananigansSimulation, fi
490478 moisture_fresh_water_flux = sim. remapping. scratch_arr1 ./ reference_density
491479 oc_flux_S = surface_flux (sim. ocean. model. tracers. S)
492480 surface_salinity = OC. interior (sim. ocean. model. tracers. S, :, :, grid. Nz)
493- # polar-exclusion mask
494- @. moisture_fresh_water_flux = ifelse (
495- polar_excl_centers .≈ 0 ,
496- zero (moisture_fresh_water_flux),
497- moisture_fresh_water_flux,
498- )
481+ # mask out the poles
482+ @. moisture_fresh_water_flux = polar_mask * moisture_fresh_water_flux
499483 OC. interior (oc_flux_S, :, :, 1 ) .- =
500484 (1.0 .- ice_concentration) .* surface_salinity .* moisture_fresh_water_flux
501485 return nothing
@@ -537,8 +521,8 @@ function FieldExchanger.update_sim!(sim::OceananigansSimulation, csf)
537521 oc_flux_S = surface_flux (sim. ocean. model. tracers. S)
538522 OC. interior (oc_flux_S, :, :, 1 ) .= 0
539523
540- # polar-exclusion mask
541- polar_excl_centers = sim. remapping. polar_exclusion_flux_mask_centers
524+ # for masking out the poles
525+ polar_mask = sim. remapping. polar_mask
542526
543527 # Remap radiative flux onto scratch array; rename for clarity
544528 CC. Remapping. interpolate! (
@@ -561,7 +545,7 @@ function FieldExchanger.update_sim!(sim::OceananigansSimulation, csf)
561545 α = Interfacer. get_field (sim, Val (:surface_direct_albedo )) # scalar
562546 ϵ = Interfacer. get_field (sim, Val (:emissivity )) # scalar
563547
564- # Compute radiative contribution; polar-exclusion mask applied via ifelse
548+ # Compute radiative contribution
565549 rad_T_flux = sim. remapping. scratch_arr3
566550 rad_T_flux .=
567551 (1.0 .- ice_concentration) .* (
@@ -571,7 +555,8 @@ function FieldExchanger.update_sim!(sim::OceananigansSimulation, csf)
571555 σ .* (C_to_K .+ OC. interior (sim. ocean. model. tracers. T, :, :, Nz)) .^ 4
572556 )
573557 ) ./ (reference_density * heat_capacity)
574- @. rad_T_flux = ifelse (polar_excl_centers .≈ 0 , zero (rad_T_flux), rad_T_flux)
558+ # mask out the poles
559+ @. rad_T_flux = polar_mask * rad_T_flux
575560 OC. interior (oc_flux_T, :, :, 1 ) .+ = rad_T_flux
576561
577562 # Remap precipitation fields onto scratch arrays; rename for clarity
@@ -585,20 +570,13 @@ function FieldExchanger.update_sim!(sim::OceananigansSimulation, csf)
585570 sim. remapping. remapper_cc,
586571 csf. P_snow,
587572 )
588- remapped_P_liq =
589- ifelse .(
590- polar_excl_centers .≈ 0 ,
591- zero (sim. remapping. scratch_arr1),
592- sim. remapping. scratch_arr1,
593- )
594- remapped_P_snow =
595- ifelse .(
596- polar_excl_centers .≈ 0 ,
597- zero (sim. remapping. scratch_arr2),
598- sim. remapping. scratch_arr2,
599- )
573+ remapped_P_liq = sim. remapping. scratch_arr1
574+ remapped_P_snow = sim. remapping. scratch_arr2
575+ # mask out the poles
576+ @. remapped_P_liq = polar_mask * remapped_P_liq
577+ @. remapped_P_snow = polar_mask * remapped_P_snow
600578
601- # Update salinity flux with precipitation contribution, including polar masking
579+ # Update salinity flux with precipitation contribution
602580 OC. interior (oc_flux_S, :, :, 1 ) .- =
603581 OC. interior (sim. ocean. model. tracers. S, :, :, Nz) .* (1.0 .- ice_concentration) .*
604582 (remapped_P_liq .+ remapped_P_snow) ./ reference_density
0 commit comments