Skip to content

Clarify default boundary conditions for GradientC2F #2473

@valentinaschueller

Description

@valentinaschueller

In §2.2 of the tutorial in the docs, the following is stated:

Center to face gradient

Uses the same stencil, but doesn't work directly:

sinz = sin.(column_center_coords.z)
gradc2f = ClimaCore.Operators.GradientC2F()
# ∇sinz = gradc2f.(sinz) ## this would throw an error

This throws an error because face values at the boundary are not well-defined:

I am on v0.14.50, and this does not seem to be true, ∇sinz = gradc2f.(sinz) will not throw an error.
I have tried this with some experiments and it seems that the following boundary condition is applied:
At indices 0, n+1, homogeneous Dirichlet data is assumed, that is, at (nonexisting) cell centers outside the domain.1
This leads to a boundary gradient value that is half the size of what you would get by applying homogeneous Dirichlet boundary conditions the standard way (i.e., SetValue(0.0)).
If I understand the code behavior correctly, it seems that with SetValue(), the Dirichlet data is placed at the faces (indices 1/2, n+1/2).
Below I have included a script that I wrote to understand what's going on, maybe it is helpful.

I am not so familiar with the code, so I am not sure that this is actually what's going on.
But perhaps you can clarify this in the documentation/the tutorial.


Example script:

import ClimaCore as CC
using ClimaComms
using ClimaCorePlots
using Plots

function get_coord(lower_boundary::Float64, upper_boundary::Float64, nelems::Int)
    device = ClimaComms.device()
    domain = CC.Domains.IntervalDomain(
        CC.Geometry.ZPoint(lower_boundary),
        CC.Geometry.ZPoint(upper_boundary);
        boundary_names=(:bottom, :top),
    )
    mesh = CC.Meshes.IntervalMesh(domain, nelems=nelems)
    cspace = CC.Spaces.CenterFiniteDifferenceSpace(device, mesh)
    coord = CC.Fields.coordinate_field(cspace)
    return coord
end

function plot_gradient(lower_boundary::Float64, upper_boundary::Float64, nelems::Int, color)
    coord = get_coord(lower_boundary, upper_boundary, nelems)
    sinz = sin.(coord.z)

    bottom_bc = CC.Operators.SetValue(0.0)
    top_bc = CC.Operators.SetValue(0.0)
    gradc2f = CC.Operators.GradientC2F(bottom=bottom_bc, top=top_bc)
    # uncomment the line below to switch to GradientC2F without boundary conditions
    # gradc2f = CC.Operators.GradientC2F()
    ∇sinz = gradc2f.(sinz)
    @info parent(CC.Geometry.WVector.(∇sinz))[end]
    plot!(
        map(x -> x.w, CC.Geometry.WVector.(∇sinz)), 
        color=color, 
        label="nelems = $nelems",
        xlabel="z",
        ylabel="∇sin(z) approximation"
    )
end

lower_boundary = 0.0
upper_boundary = 10.0
nelems = 10

plot()
plot_gradient(0.0, 10.0, 10, :black)
plot_gradient(0.0, 10.0, 5, :blue)
plot_gradient(0.0, 10.0, 20, :green)
plot_gradient(0.0, 10.0, 100, :red)
savefig("grad_comparison.png")

Footnotes

  1. I am following the numbering convention from here

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions