Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds a pertubation advection open boundary matching scheme #3977

Open
wants to merge 62 commits into
base: main
Choose a base branch
from

Conversation

jagoosw
Copy link
Collaborator

@jagoosw jagoosw commented Dec 6, 2024

This PR adds a PertubationAdvection open boundary condition which assumes that there is some mean flow (i.e. prescribed in a channel or from a coarser resolution model) which is homogeneous on the boundary but can vary in time.

On the boundary, we approximate (for an x-boundary) to:

$$\frac{\partial u}{\partial t} \approx -U\frac{\partial u\prime}{\partial x},$$

where $u=u\prime+U$
I have chosen to take an explicit/backwards Euler step:

$$\frac{u\prime(t^{n+1}) - u\prime(t^n)}{\Delta t} + \frac{U\prime(t^{n+1}) - U\prime(t^n)}{\Delta t} \approx -U\frac{u\prime(x_b, t^{n+1}) - u\prime(x_{b-1}, t^{n+1})}{\Delta x},$$

because then we don't have to store any other information (i.e. if we did a forward step we would need $u\prime(x_{b-1}, t^n)$ ). This results in:

$$u(t^{n+1}) = \frac{u^n+\tilde{U}u(x_{b-1}, t^{n+1})}{1+\tilde{U}},$$

where $\tilde{U}=\min\left(1, U\Delta t / \Delta x\right)$. I have also added restoring to $U$ (i.e. damping $u\prime\to0$) in some timescale $\tau$ which can be different for inflow and outflow to allow the scheme to work when there is inflow, which results in:

$$u(t^{n+1}) = \frac{u^n+\tilde{U}u(x_{b-1}, t^{n+1}) + U\tilde{\tau}}{1+\tilde{U}+\tilde{\tau}},$$

where $\tilde{\tau}=\Delta t / \tau$.

This has the limitation that if we want to extend to have wall tangent mean velocity ($\partial_t u \approx -(\vec{U}\cdot\nabla) u$) then we either have to solve the whole boundary at once since every point is going to depend on its boundary neighbours, or we need to take a forward Euler step in which case we need the first interior point at the previous time step.

I think we probably will need to address this for real nesting cases at some point.

u.mp4

For now, it seems to be working okay (left plot vorticity, right plot x-velocity):

(old plot) https://github.com/user-attachments/assets/21ae3eb2-5d3b-4c08-86fb-bb9c222e8e6a
and the drag coefficient is ~1.4 at Re = 100 and I'm running a case at Re=1000 now.

I have also checked the Strouhal number which matches exactly to the expected values (~0.17).

@jagoosw jagoosw added feature 🌟 Something new and shiny science 🌊 Sometimes addictive, sometimes allergenic numerics 🧮 So things don't blow up and boil the lobsters alive labels Dec 6, 2024
@jagoosw
Copy link
Collaborator Author

jagoosw commented Dec 6, 2024

@tomchor, do you think something like this:

"""
drag(modell; bounding_box = (-1, 3, -2, 2), ν = 1e-3)
Returns the drag within the `bounding_box` computed by:
```math
\\frac{\\partial \\vec{u}}{\\partial t} + (\\vec{u}\\cdot\\nabla)\\vec{u}=-\\nabla P + \\nabla\\cdot\\vec{\\tau} + \\vec{F},\\newline
\\vec{F}_T=\\int_\\Omega\\vec{F}dV = \\int_\\Omega\\left(\\frac{\\partial \\vec{u}}{\\partial t} + (\\vec{u}\\cdot\\nabla)\\vec{u}+\\nabla P - \\nabla\\cdot\\vec{\\tau}\\right)dV,\\newline
\\vec{F}_T=\\int_\\Omega\\left(\\frac{\\partial \\vec{u}}{\\partial t}\\right)dV + \\oint_{\\partial\\Omega}\\left(\\vec{u}(\\vec{u}\\cdot\\hat{n}) + P\\hat{n} - \\vec{\\tau}\\cdot\\hat{n}\\right)dS,\\newline
\\F_u=\\int_\\Omega\\left(\\frac{\\partial u}{\\partial t}\\right)dV + \\oint_{\\partial\\Omega}\\left(u(\\vec{u}\\cdot\\hat{n}) - \\tau_{xx}\\right)dS + \\int_{\\partial\\Omega}P\\hat{x}\\cdot d\\vec{S},\\newline
F_u=\\int_\\Omega\\left(\\frac{\\partial u}{\\partial t}\\right)dV - \\int_{\\partial\\Omega_1} \\left(u^2 - 2\\nu\\frac{\\partial u}{\\partial x} + P\\right)dS + \\int_{\\partial\\Omega_2}\\left(u^2 - 2\\nu\\frac{\\partial u}{\\partial x}+P\\right)dS - \\int_{\\partial\\Omega_2} uvdS + \\int_{\\partial\\Omega_4} uvdS,
```
where the bounding box is ``\\Omega`` which is formed from the boundaries ``\\partial\\Omega_{1}``, ``\\partial\\Omega_{2}``, ``\\partial\\Omega_{3}``, and ``\\partial\\Omega_{4}``
which have outward directed normals ``-\\hat{x}``, ``\\hat{x}``, ``-\\hat{y}``, and ``\\hat{y}``
"""
function drag(model;
bounding_box = (-1, 3, -2, 2),
ν = 1e-3)
u, v, _ = model.velocities
uᶜ = Field(@at (Center, Center, Center) u)
vᶜ = Field(@at (Center, Center, Center) v)
xc, yc, _ = nodes(uᶜ)
i₁ = findfirst(xc .> bounding_box[1])
i₂ = findlast(xc .< bounding_box[2])
j₁ = findfirst(yc .> bounding_box[3])
j₂ = findlast(yc .< bounding_box[4])
uₗ² = Field(uᶜ^2, indices = (i₁, j₁:j₂, 1))
uᵣ² = Field(uᶜ^2, indices = (i₂, j₁:j₂, 1))
uvₗ = Field(uᶜ*vᶜ, indices = (i₁:i₂, j₁, 1))
uvᵣ = Field(uᶜ*vᶜ, indices = (i₁:i₂, j₂, 1))
∂₁uₗ = Field(∂x(uᶜ), indices = (i₁, j₁:j₂, 1))
∂₁uᵣ = Field(∂x(uᶜ), indices = (i₂, j₁:j₂, 1))
∂ₜuᶜ = Field(@at (Center, Center, Center) model.timestepper.Gⁿ.u)
∂ₜu = Field(∂ₜuᶜ, indices = (i₁:i₂, j₁:j₂, 1))
p = model.pressures.pNHS
∫∂ₓp = Field(∂x(p), indices = (i₁:i₂, j₁:j₂, 1))
a_local = Field(Integral(∂ₜu))
a_flux = Field(Integral(uᵣ²)) - Field(Integral(uₗ²)) + Field(Integral(uvᵣ)) - Field(Integral(uvₗ))
a_viscous_stress = 2ν * (Field(Integral(∂₁uᵣ)) - Field(Integral(∂₁uₗ)))
a_pressure = Field(Integral(∫∂ₓp))
return a_local + a_flux + a_pressure - a_viscous_stress
end

could belong in Oceanostics?

@jagoosw
Copy link
Collaborator Author

jagoosw commented Dec 6, 2024

I have also run the same case with different domain lengths (6m, 12m - shown, 18m and 30m) and they all produce very similar Cd and St

@tomchor
Copy link
Collaborator

tomchor commented Dec 6, 2024

@tomchor, do you think something like this:

"""
drag(modell; bounding_box = (-1, 3, -2, 2), ν = 1e-3)
Returns the drag within the `bounding_box` computed by:
```math
\\frac{\\partial \\vec{u}}{\\partial t} + (\\vec{u}\\cdot\\nabla)\\vec{u}=-\\nabla P + \\nabla\\cdot\\vec{\\tau} + \\vec{F},\\newline
\\vec{F}_T=\\int_\\Omega\\vec{F}dV = \\int_\\Omega\\left(\\frac{\\partial \\vec{u}}{\\partial t} + (\\vec{u}\\cdot\\nabla)\\vec{u}+\\nabla P - \\nabla\\cdot\\vec{\\tau}\\right)dV,\\newline
\\vec{F}_T=\\int_\\Omega\\left(\\frac{\\partial \\vec{u}}{\\partial t}\\right)dV + \\oint_{\\partial\\Omega}\\left(\\vec{u}(\\vec{u}\\cdot\\hat{n}) + P\\hat{n} - \\vec{\\tau}\\cdot\\hat{n}\\right)dS,\\newline
\\F_u=\\int_\\Omega\\left(\\frac{\\partial u}{\\partial t}\\right)dV + \\oint_{\\partial\\Omega}\\left(u(\\vec{u}\\cdot\\hat{n}) - \\tau_{xx}\\right)dS + \\int_{\\partial\\Omega}P\\hat{x}\\cdot d\\vec{S},\\newline
F_u=\\int_\\Omega\\left(\\frac{\\partial u}{\\partial t}\\right)dV - \\int_{\\partial\\Omega_1} \\left(u^2 - 2\\nu\\frac{\\partial u}{\\partial x} + P\\right)dS + \\int_{\\partial\\Omega_2}\\left(u^2 - 2\\nu\\frac{\\partial u}{\\partial x}+P\\right)dS - \\int_{\\partial\\Omega_2} uvdS + \\int_{\\partial\\Omega_4} uvdS,
```
where the bounding box is ``\\Omega`` which is formed from the boundaries ``\\partial\\Omega_{1}``, ``\\partial\\Omega_{2}``, ``\\partial\\Omega_{3}``, and ``\\partial\\Omega_{4}``
which have outward directed normals ``-\\hat{x}``, ``\\hat{x}``, ``-\\hat{y}``, and ``\\hat{y}``
"""
function drag(model;
bounding_box = (-1, 3, -2, 2),
ν = 1e-3)
u, v, _ = model.velocities
uᶜ = Field(@at (Center, Center, Center) u)
vᶜ = Field(@at (Center, Center, Center) v)
xc, yc, _ = nodes(uᶜ)
i₁ = findfirst(xc .> bounding_box[1])
i₂ = findlast(xc .< bounding_box[2])
j₁ = findfirst(yc .> bounding_box[3])
j₂ = findlast(yc .< bounding_box[4])
uₗ² = Field(uᶜ^2, indices = (i₁, j₁:j₂, 1))
uᵣ² = Field(uᶜ^2, indices = (i₂, j₁:j₂, 1))
uvₗ = Field(uᶜ*vᶜ, indices = (i₁:i₂, j₁, 1))
uvᵣ = Field(uᶜ*vᶜ, indices = (i₁:i₂, j₂, 1))
∂₁uₗ = Field(∂x(uᶜ), indices = (i₁, j₁:j₂, 1))
∂₁uᵣ = Field(∂x(uᶜ), indices = (i₂, j₁:j₂, 1))
∂ₜuᶜ = Field(@at (Center, Center, Center) model.timestepper.Gⁿ.u)
∂ₜu = Field(∂ₜuᶜ, indices = (i₁:i₂, j₁:j₂, 1))
p = model.pressures.pNHS
∫∂ₓp = Field(∂x(p), indices = (i₁:i₂, j₁:j₂, 1))
a_local = Field(Integral(∂ₜu))
a_flux = Field(Integral(uᵣ²)) - Field(Integral(uₗ²)) + Field(Integral(uvᵣ)) - Field(Integral(uvₗ))
a_viscous_stress = 2ν * (Field(Integral(∂₁uᵣ)) - Field(Integral(∂₁uₗ)))
a_pressure = Field(Integral(∫∂ₓp))
return a_local + a_flux + a_pressure - a_viscous_stress
end

could belong in Oceanostics?

Sure! Why not? :)

Comment on lines 38 to 64
@inline function _fill_east_halo!(j, k, grid, u, bc::PAOBC, loc::Tuple{Face, Any, Any}, clock, model_fields)
i = grid.Nx + 1

Δt = clock.last_stage_Δt

Δt = ifelse(isinf(Δt), 0, Δt)

Δx = xspacing(i, j, k, grid, loc...)

ūⁿ⁺¹ = getbc(bc, j, k, grid, clock, model_fields)

uᵢⁿ = @inbounds u[i, j, k]
uᵢ₋₁ⁿ⁺¹ = @inbounds u[i - 1, j, k]

U = max(0, min(1, Δt / Δx * ūⁿ⁺¹))

τ = ifelse(ūⁿ⁺¹ >= 0,
bc.classification.matching_scheme.outflow_timescale,
bc.classification.matching_scheme.inflow_timescale)


τ̃ = Δt / τ

uᵢⁿ⁺¹ = (uᵢⁿ + U * uᵢ₋₁ⁿ⁺¹ + ūⁿ⁺¹ * τ̃) / (1 + τ̃ + U)

@inbounds u[i, j, k] = uᵢⁿ⁺¹#ifelse(active_cell(i, j, k, grid), uᵢⁿ⁺¹, zero(grid))
end
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we could implement everything using getindex() so that we implement it once (or at max twice; one for left and other for right BC) and can use it in three dimensions.

For example this

    uᵢⁿ     = @inbounds u[i, j, k]
    uᵢ₋₁ⁿ⁺¹ = @inbounds u[i - 1, j, k]

would become

    uᵢⁿ     = @inbounds getindex(u, boundary_indices...)
    uᵢ₋₁ⁿ⁺¹ = @inbounds getindex(u, one_off_boundary_indices...)

This would avoid errors when transcribing to other dimensions (I remember catching a couple for the flat extrapolation matching scheme).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yeah, I was trying to think of a clean way to do this

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Attempted this now (but I've still not written out the other directions in case we want to change things first)

@jagoosw jagoosw force-pushed the jsw/pertubation-advection-obc branch from 0e645c5 to 32cd354 Compare December 6, 2024 20:11
@jagoosw jagoosw force-pushed the jsw/pertubation-advection-obc branch from afecfb3 to e66f3d1 Compare December 6, 2024 20:43
@glwagner
Copy link
Member

glwagner commented Dec 6, 2024

There's a typo in your top equation right? I think it should be $\partial_t (u + U) = U \partial_x u$.

@glwagner
Copy link
Member

glwagner commented Dec 6, 2024

A problem I think this scheme might have is that as the flow changes direction the assumption that the perturbation is small falls apart, which is maybe why it's been a bit unstable so far in an oscillating case.

What do you mean? What is the consequence of this approximation?

@jagoosw
Copy link
Collaborator Author

jagoosw commented Dec 6, 2024

What do you mean? What is the consequence of this approximation?

I was thinking about this more and really to remain valid then in :

$$\partial_t (u\prime+U)\approx u\prime\partial_xu\prime + U\partial_xu\prime + (U - u\prime)/\tau$$

as $U\to0$ then we need $u\prime\partial_xu\prime\ll u\prime/\tau$ to still be able to throw away the first term, so I think its actually okay as long as you have the relaxation.

I found a typo in the west boundary which might be why the oscillating case wasn't working, rerunning now

@jagoosw
Copy link
Collaborator Author

jagoosw commented Dec 6, 2024

There's a typo in your top equation right? I think it should be ∂ t ( u + U ) = U ∂ x u .
Yeah thanks for catching!

@glwagner
Copy link
Member

glwagner commented Dec 6, 2024

What do you mean? What is the consequence of this approximation?

I was thinking about this more and really to remain valid then in :

as U → 0 then we need u ′ ∂ x u ′ ≪ u ′ / τ to still be able to throw away the first term, so I think its actually okay as long as you have the relaxation.

I found a typo in the west boundary which might be why the oscillating case wasn't working, rerunning now

Okay, I agree with you. You can also analyze the update formula / backward Euler step in the limit U -> 0. It doesn't look like it needs to be regularized in any way in that case though, it looks ok.

@tomchor
Copy link
Collaborator

tomchor commented Dec 9, 2024

Is there a reference for this method? If there is, please include it on the methods docstring. If not, it'd be good to include a brief explanation like the one at the top comment in the dosctring. I don't think this radiation method is very well known.

Comment on lines 10 to 30
We begin with the equation governing the fluid in the interior:
∂ₜu⃗ + u⃗⋅∇u⃗ = −∇P + F⃗,
and note that on the boundary the pressure gradient is zero.
We can then assume that the flow composes of mean (U⃗) and pertubation (u⃗′) components,
and considering the x-component of velocity, we can rewrite the equation as
∂ₜu₁ = -u₁∂₁u - u₂∂₂u₁ - u₃∂₃u₁ + F₁ ≈ - U₁∂₁u₁′ - U₂∂₂u₁′ - U₃∂₃u₁′ + F.

Simplify by assuming that U⃗ = Ux̂, an then take a numerical step to find u₁.

When the boundaries are filled the interior is at time tₙ₊₁ so we can take
a backwards euler step (in the case that the mean flow is boundary normal) on a right boundary:
(Uⁿ⁺¹ - Uⁿ) / Δt + (u′ⁿ⁺¹ - u′ⁿ) / Δt = - Uⁿ⁺¹ (u′ⁿ⁺¹ᵢ - u′ⁿ⁺¹ᵢ₋₁) / Δx + Fᵤ.

This can not be solved for general forcing, but if we assume the dominant forcing is
relaxation to the mean velocity (i.e. u′→0) then Fᵤ = -u′ / τ then we can find u′ⁿ⁺¹:
u′ⁿ⁺¹ = (uⁿ + Ũu′ⁿ⁺¹ᵢ₋₁ - Uⁿ⁺¹) / (1 + Ũ + Δt/τ),

where Ũ = U Δt / Δx, then uⁿ⁺¹ is:
uⁿ⁺¹ = (uᵢⁿ + Ũuᵢ₋₁ⁿ⁺¹ + Uⁿ⁺¹τ̃) / (1 + τ̃ + U)

where τ̃ = Δt/τ.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Is this not rendered correctly too @tomchor ?

Copy link
Collaborator

Choose a reason for hiding this comment

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

If I use FiraCode on chrome then some of the stuff in the first line of code doesn't render correctly:

image

On firefox it all renders fine:

image

And when I manually switch to JuliaMono it all renders fine.

But after finding out that this is all browser/machine dependent, I do think we shouldn't spend too much time trying to "fix" this. I say we move on from tweaking the unicode and address the rest of the comments so that we can merge.

@jagoosw
Copy link
Collaborator Author

jagoosw commented Jan 30, 2025

I can no longer see the buildkite logs (its says I have to be part of the buildkite organisation) so could someone check what the problem with those tests was please?

@tomchor
Copy link
Collaborator

tomchor commented Jan 30, 2025

A couple of them are failing with an Out of GPU memory error, but there's also this error:

image

@tomchor
Copy link
Collaborator

tomchor commented Jan 30, 2025

We still have a comment that hasn't been addressed, right? About what to do with the boundary_mean.jl file. After that's dealt with, I can make tests pass if you want.

@jagoosw
Copy link
Collaborator Author

jagoosw commented Jan 30, 2025

We still have a comment that hasn't been addressed, right? About what to do with the boundary_mean.jl file. After that's dealt with, I can make tests pass if you want.

yeah that's the only problem, whats wrong with the tests?

@tomchor
Copy link
Collaborator

tomchor commented Jan 30, 2025

We still have a comment that hasn't been addressed, right? About what to do with the boundary_mean.jl file. After that's dealt with, I can make tests pass if you want.

yeah that's the only problem, whats wrong with the tests?

No idea what's up with GPU errors. @glwagner mentioned at the some point that that server is pretty full, so I'm assuming some cleanup will be done soon. (I don't have access to that server btw, so I only know what people tell me.)

The error I posted in #3977 (comment) seems to be the only actual error rn though.

@jagoosw
Copy link
Collaborator Author

jagoosw commented Feb 5, 2025

On the boundary mean issue, perhaps we should make a new module called something like HelperFunctions since I imagine there's other things that are easiest to define with all of the other infrastructure (I.e. abstract operators, etc.) so could be user defined, but are also nice to have in the source code?

@glwagner any thoughts?

@glwagner
Copy link
Member

glwagner commented Feb 5, 2025

On the boundary mean issue, perhaps we should make a new module called something like HelperFunctions since I imagine there's other things that are easiest to define with all of the other infrastructure (I.e. abstract operators, etc.) so could be user defined, but are also nice to have in the source code?

@glwagner any thoughts?

Can you elaborate? What you're suggesting might make sense but I need more background, motivation, design proposal, etc.

@jagoosw
Copy link
Collaborator Author

jagoosw commented Feb 10, 2025

Can you elaborate? What you're suggesting might make sense but I need more background, motivation, design proposal, etc.

Basically, the best and most general way to implement a boundary condition that uses the boundary mean velocity is by using AbstractOperations, but the problem is that they're defined after boundary conditions so it can't go in the boundary conditions module.

I think there is kind of a grey area of things like this that aren't part of the core functionality of the code and could be implemented by a user, but are important and useful for us to support and maintain.

@glwagner
Copy link
Member

glwagner commented Feb 10, 2025

Can you elaborate? What you're suggesting might make sense but I need more background, motivation, design proposal, etc.

Basically, the best and most general way to implement a boundary condition that uses the boundary mean velocity is by using AbstractOperations, but the problem is that they're defined after boundary conditions so it can't go in the boundary conditions module.

I think there is kind of a grey area of things like this that aren't part of the core functionality of the code and could be implemented by a user, but are important and useful for us to support and maintain.

Possibly, this belongs in Models then. Are we assuming something specific about the presence of a velocity field vs tracer field? In that case, this is not a "finite volume engine" capability, but rather a "fluid dynamics model" capability, and it does not belong in BoundaryConditions (which has primitive utilities that should be independent of fluid dynamics).

@jagoosw
Copy link
Collaborator Author

jagoosw commented Feb 10, 2025

Possibly, this belongs in Models then. Are we assuming something specific about the presence of a velocity field vs tracer field? In that case, this is not a "finite volume engine" capability, but rather a "fluid dynamics model" capability, and it does not belong in BoundaryConditions (which has primitive utilities that should be independent of fluid dynamics) so isn't fluid specific.

That's a good idea, it doesn't make any assumptions about what it is and is just computing the mean of whatever field it is associated with on a boundary:

function update_boundary_condition!(bc::MOPABC, val_side, u, model)
grid = model.grid
loc = instantiated_location(u)
An = boundary_normal_area(val_side, grid)
(i, j, k), dims = boundary_adjacent_indices(val_side, grid, loc)
total_area = CUDA.@allowscalar sum(An; dims)[i, j, k]
= sum(u * An; dims) / total_area
bc.condition.value[] = CUDA.@allowscalar Ū[i, j, k]
return nothing
end

@glwagner
Copy link
Member

shouldn't you define a kernel for sum? the way implemented seems like its going to be slow

@glwagner
Copy link
Member

glwagner commented Feb 10, 2025

Also this function takes in model which to me clearly says it belongs in Models. This must only work with specific models right? It will not work as a generic boundary condition for any Field / with any fill_halo_regions!.

@jagoosw
Copy link
Collaborator Author

jagoosw commented Feb 10, 2025

Also this function takes in model which to me clearly says it belongs in Models. This must only work with specific models right? It will not work as a generic boundary condition for any Field / with any fill_halo_regions!.

Model doesn't actually do anything (if we wrote u.grid rather than model.grid) so maybe I should rewrite this like:

function (bam::BoundaryAdjacentMean)(val_side, u)
   ... the above function
   return bam.value
end

update_boundary_condition!(bc::MOPABC, val_side, u, model) = (bc.condition(val_side, u); nothing)

@jagoosw
Copy link
Collaborator Author

jagoosw commented Feb 10, 2025

shouldn't you define a kernel for sum? the way implemented seems like its going to be slow

Maybe? Doesn't sum go back to this reduction:

function Base.$(reduction)(f::Function,
c::AbstractField;
condition = nothing,
mask = get_neutral_mask(Base.$(reduction!)),
dims = :)
T = filltype(Base.$(reduction!), c)
loc = reduced_location(location(c); dims)
r = Field(loc, c.grid, T; indices=indices(c))
conditioned_c = condition_operand(f, c, condition, mask)
initialize_reduced_field!(Base.$(reduction!), identity, r, conditioned_c)
Base.$(reduction!)(identity, r, conditioned_c, init=false)
if dims isa Colon
return CUDA.@allowscalar first(r)
else
return r
end
end
Base.$(reduction)(c::AbstractField; kwargs...) = Base.$(reduction)(identity, c; kwargs...)
end

I guess its more efficient to preallocate and use the preallocating version? We could put a field like:

r = Field(loc, c.grid, T; indices=indices(c))

in the boundary adjacent mean struct.

I guess summing is always inefficient since it has to be serialized can't be straightforwardly parallelised without making a race condition though right?

@jagoosw jagoosw closed this Feb 10, 2025
@jagoosw jagoosw reopened this Feb 10, 2025
@glwagner
Copy link
Member

Where is update_boundary_condition! defined? In my opinion, update_boundary_condition! is defining a model-specific interface.

@glwagner
Copy link
Member

glwagner commented Feb 10, 2025

shouldn't you define a kernel for sum? the way implemented seems like its going to be slow

Maybe? Doesn't sum go back to this reduction:

function Base.$(reduction)(f::Function,
c::AbstractField;
condition = nothing,
mask = get_neutral_mask(Base.$(reduction!)),
dims = :)
T = filltype(Base.$(reduction!), c)
loc = reduced_location(location(c); dims)
r = Field(loc, c.grid, T; indices=indices(c))
conditioned_c = condition_operand(f, c, condition, mask)
initialize_reduced_field!(Base.$(reduction!), identity, r, conditioned_c)
Base.$(reduction!)(identity, r, conditioned_c, init=false)
if dims isa Colon
return CUDA.@allowscalar first(r)
else
return r
end
end
Base.$(reduction)(c::AbstractField; kwargs...) = Base.$(reduction)(identity, c; kwargs...)
end

I guess its more efficient to preallocate and use the preallocating version? We could put a field like:

r = Field(loc, c.grid, T; indices=indices(c))

in the boundary adjacent mean struct.
I guess summing is always inefficient since it has to be serialized can't be straightforwardly parallelised without making a race condition though right?

I'm confused. Is this working inside a kernel? You cannot launch a kernel (to compute sum) from within a kernel. Does this work on GPU? Furthermore, @allowscalar should never be used to fetch a generic index (eg i, j, k). The only acceptable usage is to fetch a single known index, typically first or 1, 1, 1.

@jagoosw
Copy link
Collaborator Author

jagoosw commented Feb 11, 2025

Where is update_boundary_condition! defined? In my opinion, update_boundary_condition! is defining a model-specific interface.

The fallback is defined in boundary conditions, but this is the first time we've actually implemented on in the source code. Happy for it to go in the model modules.

@jagoosw
Copy link
Collaborator Author

jagoosw commented Feb 11, 2025

shouldn't you define a kernel for sum? the way implemented seems like its going to be slow

Maybe? Doesn't sum go back to this reduction:

function Base.$(reduction)(f::Function,
c::AbstractField;
condition = nothing,
mask = get_neutral_mask(Base.$(reduction!)),
dims = :)
T = filltype(Base.$(reduction!), c)
loc = reduced_location(location(c); dims)
r = Field(loc, c.grid, T; indices=indices(c))
conditioned_c = condition_operand(f, c, condition, mask)
initialize_reduced_field!(Base.$(reduction!), identity, r, conditioned_c)
Base.$(reduction!)(identity, r, conditioned_c, init=false)
if dims isa Colon
return CUDA.@allowscalar first(r)
else
return r
end
end
Base.$(reduction)(c::AbstractField; kwargs...) = Base.$(reduction)(identity, c; kwargs...)
end

I guess its more efficient to preallocate and use the preallocating version? We could put a field like:

r = Field(loc, c.grid, T; indices=indices(c))

in the boundary adjacent mean struct.
I guess summing is always inefficient since it has to be serialized can't be straightforwardly parallelised without making a race condition though right?

I'm confused. Is this working inside a kernel? You cannot launch a kernel (to compute sum) from within a kernel. Does this work on GPU? Furthermore, @allowscalar should never be used to fetch a generic index (eg i, j, k). The only acceptable usage is to fetch a single known index, typically first or 1, 1, 1.

Not from within a kernel, and yeah I used in on GPU. We only want a single value where 2 of i, j, k and k will be 1 but the other will either be 1 or the end. I don't know how else we can pull the single value out?

@glwagner
Copy link
Member

Maybe I was confused then. You're saying that i, j, k don't vary and take just a single value per boundary condition?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature 🌟 Something new and shiny numerics 🧮 So things don't blow up and boil the lobsters alive science 🌊 Sometimes addictive, sometimes allergenic
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants