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")
In §2.2 of the tutorial in the docs, the following is stated:
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.1This 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 (indices1/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:
Footnotes
I am following the numbering convention from here ↩