Skip to content

Commit a5735a2

Browse files
Add 1D interpolation.
1 parent 8c8d463 commit a5735a2

File tree

8 files changed

+364
-0
lines changed

8 files changed

+364
-0
lines changed

.dev/Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[deps]
2+
JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899"

.dev/climaformat.jl

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#!/usr/bin/env julia
2+
#
3+
# This is an adapted version of format.jl from JuliaFormatter with the
4+
# following license:
5+
#
6+
# MIT License
7+
# Copyright (c) 2019 Dominique Luna
8+
#
9+
# Permission is hereby granted, free of charge, to any person obtaining a
10+
# copy of this software and associated documentation files (the
11+
# "Software"), to deal in the Software without restriction, including
12+
# without limitation the rights to use, copy, modify, merge, publish,
13+
# distribute, sublicense, and/or sell copies of the Software, and to permit
14+
# persons to whom the Software is furnished to do so, subject to the
15+
# following conditions:
16+
#
17+
# The above copyright notice and this permission notice shall be included
18+
# in all copies or substantial portions of the Software.
19+
#
20+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21+
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
23+
# NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
24+
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
25+
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
26+
# USE OR OTHER DEALINGS IN THE SOFTWARE.
27+
#
28+
using Pkg
29+
Pkg.activate(@__DIR__)
30+
Pkg.instantiate()
31+
32+
using JuliaFormatter
33+
34+
help = """
35+
Usage: climaformat.jl [flags] [FILE/PATH]...
36+
37+
Formats the given julia files using the CLIMA formatting options. If paths
38+
are given it will format the julia files in the paths. Otherwise, it will
39+
format all changed julia files.
40+
41+
-v, --verbose
42+
Print the name of the files being formatted with relevant details.
43+
44+
-h, --help
45+
Print this message.
46+
"""
47+
48+
function parse_opts!(args::Vector{String})
49+
i = 1
50+
opts = Dict{Symbol,Union{Int,Bool}}()
51+
while i length(args)
52+
arg = args[i]
53+
if arg[1] != '-'
54+
i += 1
55+
continue
56+
end
57+
if arg == "-v" || arg == "--verbose"
58+
opt = :verbose
59+
elseif arg == "-h" || arg == "--help"
60+
opt = :help
61+
else
62+
error("invalid option $arg")
63+
end
64+
if opt in (:verbose, :help)
65+
opts[opt] = true
66+
deleteat!(args, i)
67+
end
68+
end
69+
return opts
70+
end
71+
72+
opts = parse_opts!(ARGS)
73+
if haskey(opts, :help)
74+
write(stdout, help)
75+
exit(0)
76+
end
77+
if isempty(ARGS)
78+
filenames = readlines(`git ls-files "*.jl"`)
79+
else
80+
filenames = ARGS
81+
end
82+
83+
format(filenames; opts...)

Project.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
name = "ClimaInterpolations"
2+
uuid = "dd0f122e-fa3b-47f3-bcf0-93bbc60d885e"
3+
authors = ["sriharshakandala <[email protected]>"]
4+
version = "0.1.0"

src/ClimaInterpolations.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module ClimaInterpolations
2+
3+
include("Interpolation1D.jl")
4+
5+
end

src/Interpolation1D.jl

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
module Interpolation1D
2+
3+
include("stencils1d.jl")
4+
5+
"""
6+
interpolate(xpts, fval, xtarg)
7+
8+
Interpolate `fval` defined on `xpts` at `xtarg`.
9+
Reference:
10+
1). Berrut, J., and Trefethen, L. N., 2004. Barycentric Lagrange Interpolation, SIAM REVIEW
11+
Vol. 46, No. 3, pp. 501–517.
12+
DOI. 10.1137/S0036144502417715
13+
"""
14+
Base.@pure function interpolate(
15+
xpts::AbstractArray{FT,1},
16+
fval::AbstractArray{FT,1},
17+
xtarg::FT,
18+
) where {FT<:AbstractFloat}
19+
length(xpts) == 1 && return fval[1] # flat extrapolation
20+
l, tot, mi, tol = FT(1), -FT(0), 0, (10 * eps(FT))
21+
@inbounds begin
22+
for i in eachindex(xpts)
23+
dx = xtarg - xpts[i]
24+
if abs(dx) < tol
25+
mi = i
26+
break
27+
else
28+
l *= dx
29+
end
30+
end
31+
if mi == 0
32+
for i in eachindex(xpts)
33+
xi = xpts[i]
34+
wi = FT(1)
35+
for j in eachindex(xpts)
36+
if j i
37+
wi *= (xi - xpts[j])
38+
end
39+
end
40+
tot += fval[i] * l / ((xtarg - xi) * wi)
41+
end
42+
else
43+
tot = fval[mi]
44+
end
45+
end
46+
return tot
47+
end
48+
49+
"""
50+
interpolate1d!(
51+
ftarget::AbstractArray{FT,N},
52+
xsource::AbstractArray{FT,N},
53+
xtarget::AbstractArray{FT,N},
54+
fsource::AbstractArray{FT,N},
55+
order,
56+
extrapolate = Flat(),
57+
) where {FT,N}
58+
59+
Interpolate `fsource`, defined on grid `xsource`, onto the `xtarget` grid.
60+
Here the source grid `xsource` is an N-dimensional array of columns.
61+
The first dimension is assumed to be the column dimension.
62+
Each column can have a different grid.
63+
"""
64+
function interpolate1d!(
65+
ftarget::AbstractArray{FT,N},
66+
xsource::AbstractArray{FT,N},
67+
xtarget::AbstractArray{FT,N},
68+
fsource::AbstractArray{FT,N},
69+
order,
70+
extrapolate = Flat(),
71+
) where {FT,N}
72+
@assert N 1
73+
(nsource, coldims_source...) = size(xsource)
74+
(ntarget, coldims_target...) = size(xtarget)
75+
@assert coldims_source == coldims_target
76+
@assert size(ftarget) == size(xtarget)
77+
@assert size(fsource) == size(xsource)
78+
coldims = coldims_source
79+
colcidxs = CartesianIndices(coldims)
80+
@inbounds begin
81+
for colcidx in colcidxs
82+
colidx = Tuple(colcidx)
83+
first = 1
84+
for i1 = 1:ntarget
85+
(st, en) = get_stencil(
86+
order,
87+
view(xsource, :, colidx...),
88+
xtarget[i1, colidx...],
89+
first = first,
90+
extrapolate = extrapolate,
91+
)
92+
first = st
93+
ftarget[i1, colidx...] = interpolate(
94+
view(xsource, st:en, colidx...),
95+
view(fsource, st:en, colidx...),
96+
xtarget[i1, colidx...],
97+
)
98+
end
99+
end
100+
end
101+
return nothing
102+
end
103+
104+
end

src/stencils1d.jl

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
2+
abstract type Order1D end
3+
abstract type Extrapolate1D end
4+
5+
struct Linear <: Order1D end
6+
7+
struct Flat <: Extrapolate1D end
8+
struct LinearExtrapolation <: Extrapolate1D end
9+
10+
function get_stencil(alg::Linear, xsource, xtarg; first = 1, extrapolate = Flat())
11+
last = length(xsource)
12+
if xtarg < xsource[1] # extrapolation
13+
# Linear extrapolation
14+
extrapolate == LinearExtrapolation() && return (1, 2)
15+
# Flat extrapolation by default
16+
return (1, 1)
17+
end
18+
if xtarg > xsource[last] # extrapolation
19+
# Linear extrapolation
20+
extrapolate == LinearExtrapolation() && return (last - 1, last)
21+
# Flat extrapolation by default
22+
return (last, last)
23+
end
24+
st = first
25+
for i = first:last
26+
if xtarg xsource[i]
27+
st = max(i - 1, 1)
28+
break
29+
end
30+
end
31+
return (st, st < last ? st + 1 : st)
32+
end

test/Project.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[deps]
2+
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
3+
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
4+
ClimaInterpolations = "dd0f122e-fa3b-47f3-bcf0-93bbc60d885e"

test/interpolation1D.jl

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import ClimaInterpolations.Interpolation1D:
2+
interpolate, Linear, get_stencil, interpolate1d!, Flat, LinearExtrapolation
3+
using Test
4+
5+
6+
function get_uniform_column_grids(
7+
::Type{FT},
8+
xmin,
9+
xmax,
10+
xmintarg,
11+
xmaxtarg,
12+
nsource,
13+
ntarget,
14+
) where {FT}
15+
return (
16+
Vector{FT}(range(xmin, xmax, length = nsource)),
17+
Vector{FT}(range(xmintarg, xmaxtarg, length = ntarget)),
18+
)
19+
end
20+
21+
function test_single_column(
22+
::Type{FT},
23+
xmin,
24+
xmax,
25+
nsource,
26+
ntarget;
27+
toler,
28+
xmintarg = xmin,
29+
xmaxtarg = xmax,
30+
extrapolation = Flat(),
31+
) where {FT}
32+
@testset "1D linear interpolation on single column with $FT" begin
33+
xsource, xtarget =
34+
get_uniform_column_grids(FT, xmin, xmax, xmintarg, xmaxtarg, nsource, ntarget)
35+
fsource = sin.(xsource) # function defined on source grid
36+
ftarget = zeros(FT, ntarget) # allocated function on target grid
37+
interpolate1d!(ftarget, xsource, xtarget, fsource, Linear(), extrapolation)
38+
diff = maximum(
39+
abs.(ftarget .- sin.(xtarget)) .* (xtarget .≤ xmax) .* (xtarget .≥ xmin),
40+
)
41+
@test diff toler
42+
# test extrapolation
43+
if xmintarg < xmin || xmaxtarg > xmax
44+
if extrapolation == Flat()
45+
left_boundary_pass = true
46+
right_boundary_pass = true
47+
for i = 1:length(xtarget)
48+
if xtarget[i] < xmin
49+
left_boundary_pass = ftarget[i] == fsource[1]
50+
end
51+
if xtarget[i] > xmax
52+
right_boundary_pass = ftarget[i] == fsource[end]
53+
end
54+
end
55+
@testset "testing Flat extrapolation" begin
56+
@test left_boundary_pass
57+
@test right_boundary_pass
58+
end
59+
end
60+
end
61+
62+
end
63+
return nothing
64+
end
65+
66+
function test_multiple_columns(
67+
::Type{FT},
68+
xmin,
69+
xmax,
70+
nsource,
71+
ntarget,
72+
nlon,
73+
nlat;
74+
toler,
75+
xmintarg = xmin,
76+
xmaxtarg = xmax,
77+
extrapolation = Flat(),
78+
) where {FT}
79+
@testset "1D linear interpolation on multiple columns with $FT" begin
80+
xsource, xtarget =
81+
get_uniform_column_grids(FT, xmin, xmax, xmintarg, xmaxtarg, nsource, ntarget)
82+
83+
xsourcecols = repeat(xsource, 1, nlon, nlat)
84+
xtargetcols = repeat(xtarget, 1, nlon, nlat)
85+
fsourcecols = sin.(xsourcecols)
86+
ftargetcols = zeros(FT, ntarget, nlon, nlat)
87+
88+
interpolate1d!(
89+
ftargetcols,
90+
xsourcecols,
91+
xtargetcols,
92+
fsourcecols,
93+
Linear(),
94+
extrapolation,
95+
)
96+
diff = maximum(abs.(ftargetcols .- sin.(xtargetcols))[:])
97+
@test diff toler
98+
end
99+
return nothing
100+
end
101+
102+
# single column linear interpolation tests without extrapolation
103+
test_single_column(Float32, 0.0, 2 * π, 150, 200, toler = 0.0003)
104+
test_single_column(Float64, 0.0, 2 * π, 150, 200, toler = 0.0003)
105+
# single column linear interpolation tests with Flat extrapolation
106+
test_single_column(
107+
Float32,
108+
0.0,
109+
2 * π,
110+
150,
111+
200,
112+
toler = 0.0003,
113+
xmintarg = -1.0,
114+
xmaxtarg = 2 * π + 1.0,
115+
extrapolation = Flat(),
116+
)
117+
test_single_column(
118+
Float64,
119+
0.0,
120+
2 * π,
121+
150,
122+
200,
123+
toler = 0.0003,
124+
xmintarg = -1.0,
125+
xmaxtarg = 2 * π + 1.0,
126+
extrapolation = Flat(),
127+
)
128+
# multiple column liner interpolation tests without extrapolation
129+
test_multiple_columns(Float32, 0.0, 2 * π, 150, 200, 2560, 1280, toler = 0.0003)
130+
test_multiple_columns(Float64, 0.0, 2 * π, 150, 200, 2560, 1280, toler = 0.0003)

0 commit comments

Comments
 (0)