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
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions