Skip to content

Commit e171a53

Browse files
committed
more coverage
1 parent 3ea74ff commit e171a53

5 files changed

Lines changed: 239 additions & 0 deletions

File tree

test/legacy_compat_test.jl

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,35 @@
9595
@test ClimateTools.daymean_factor("6h") == 4
9696
@test ClimateTools.pr_timefactor("3h") == 10800.0
9797
@test size(ensemble_fct(cube)) == (3, 2, 2)
98+
end
99+
100+
@testset "Legacy compatibility helpers" begin
101+
@test ClimateTools._infer_temperature_unit([1.0, 10.0, 25.0]) == :Celsius
102+
@test ClimateTools._infer_temperature_unit([280.0, 281.0, 282.0]) == :K
103+
@test ClimateTools._infer_temperature_unit([missing, missing]) == :K
104+
@test ClimateTools._infer_temperature_unit([1.0, 2.0]; temperature_unit=:Kelvin) == :Kelvin
105+
106+
@test ClimateTools._as_kelvin([0.0, 10.0]; temperature_unit=:degC) == [273.15, 283.15]
107+
@test ClimateTools._as_kelvin([300.0, 301.0]; temperature_unit=:K) == [300.0, 301.0]
108+
@test ClimateTools._as_celsius([273.15, 283.15]; temperature_unit=:Kelvin) == [0.0, 10.0]
109+
@test ClimateTools._as_celsius([0.0, 10.0]; temperature_unit=:Celsius) == [0.0, 10.0]
110+
111+
hourly_times = collect(0.0:(1 / 24):1.0)
112+
monthly_times = [0.0, 31.0, 60.0]
113+
yearly_times = [0.0, 365.0, 730.0]
114+
@test ClimateTools.timeresolution(Float64[]) == "N/A"
115+
@test ClimateTools.timeresolution(hourly_times) == "1h"
116+
@test ClimateTools.timeresolution(monthly_times) == "Monthly"
117+
@test ClimateTools.timeresolution(yearly_times) == "Yearly"
118+
119+
@test ClimateTools.pr_timefactor("unknown") == 1.0
120+
@test ClimateTools.daymean_factor("unknown") == 1
121+
122+
missing_times = collect(DateTime(2004, 1, 1):Day(1):DateTime(2005, 12, 31))
123+
missing_cube = YAXArray(
124+
(Dim{:longitude}(1:1), Dim{:latitude}(1:1), Dim{:time}(missing_times)),
125+
fill(NaN, 1, 1, length(missing_times)),
126+
)
127+
yearly_missing = ClimateTools.prcp1(missing_cube)
128+
@test all(isnan, Array(yearly_missing))
98129
end

test/markov_test.jl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,28 @@ using Dates
1717
result_three = ClimateTools.MSModel(cube; k_regime=3)
1818
@test size(result_three) == (1,)
1919
@test Array(result_three)[1] isa ClimateTools.MarSwitching.MSM
20+
end
21+
22+
@testset "Markov Switching multidimensional cube" begin
23+
times = collect(Date(2021, 1, 1):Day(1):Date(2021, 2, 19))
24+
lon = [10.0, 20.0]
25+
lat = [45.0, 50.0]
26+
27+
base_signal = vcat(fill(-3.0, 25), fill(3.0, 25))
28+
data = Array{Float64}(undef, length(lon), length(lat), length(times))
29+
for ilon in eachindex(lon), ilat in eachindex(lat)
30+
data[ilon, ilat, :] .= base_signal .+ 0.1 * ilon .- 0.05 * ilat
31+
end
32+
33+
cube = YAXArray((Dim{:longitude}(lon), Dim{:latitude}(lat), Dim{:time}(times)), data)
34+
result = ClimateTools.MSModel(cube; k_regime=2, intercept="switching")
35+
36+
@test size(result) == (length(lon), length(lat), 1)
37+
@test Tuple(Symbol.(name.(result.axes))) == (:longitude, :latitude, :MSM)
38+
@test all(model -> model isa ClimateTools.MarSwitching.MSM, Array(result))
39+
end
40+
41+
@testset "Markov Switching validation" begin
42+
cube = YAXArray((Dim{:longitude}([1.0, 2.0]), Dim{:latitude}([3.0, 4.0])), [1.0 2.0; 3.0 4.0])
43+
@test_throws ErrorException ClimateTools.MSModel(cube)
2044
end

test/regrid_test.jl

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ using ClimateTools
33
using YAXArrays
44
using DimensionalData
55
using Dates
6+
using Serialization
67

78
@testset "Regridder rectilinear workflow" begin
89
src_lon = collect(0.0:1.0:4.0)
@@ -313,3 +314,98 @@ end
313314
out_multi = regrid_cube(ds, :pr, dest_multi; method="bilinear")
314315
@test Base.maximum(abs.(Array(out_multi) .- expected)) < 1e-6
315316
end
317+
318+
@testset "Regrid helper coverage" begin
319+
@test ClimateTools._normalize_regrid_method("linear") == :bilinear
320+
@test ClimateTools._normalize_regrid_method("nearest") == :nearest_s2d
321+
@test ClimateTools._normalize_regrid_method("idw") == :idw
322+
@test_throws ErrorException ClimateTools._normalize_regrid_method("cubic")
323+
324+
ascending, asc_flip = ClimateTools._prepare_source_coordinate([1.0, 2.0, 3.0], :longitude)
325+
descending, desc_flip = ClimateTools._prepare_source_coordinate([3.0, 2.0, 1.0], :latitude)
326+
@test ascending == [1.0, 2.0, 3.0]
327+
@test asc_flip == false
328+
@test descending == [1.0, 2.0, 3.0]
329+
@test desc_flip == true
330+
@test_throws ErrorException ClimateTools._prepare_source_coordinate([1.0, 1.0, 2.0], :longitude)
331+
@test_throws ErrorException ClimateTools._prepare_source_coordinate([1.0, 3.0, 2.0], :longitude)
332+
333+
@test ClimateTools._weighted_value((1.0, 2.0, 3.0, 4.0), (0.25, 0.25, 0.25, 0.25)) 2.5
334+
@test isnan(ClimateTools._weighted_value((1.0, missing, 3.0, 4.0), (0.25, 0.25, 0.25, 0.25)))
335+
@test ClimateTools._weighted_value((1.0, missing, 3.0, 5.0), (0.25, 0.25, 0.25, 0.25); skipna=true, na_thres=0.3) 3.0
336+
@test isnan(ClimateTools._weighted_value((1.0, missing, 3.0, 5.0), (0.25, 0.25, 0.25, 0.25); skipna=true, na_thres=0.2))
337+
338+
lon2d, lat2d = ClimateTools._destination_grid([0.0, 1.0], [10.0, 11.0])
339+
@test size(lon2d) == (2, 2)
340+
@test size(lat2d) == (2, 2)
341+
@test_throws ErrorException ClimateTools._destination_grid(reshape([0.0, 1.0], 2, 1), [10.0, 11.0])
342+
@test_throws ErrorException ClimateTools._destination_grid(ones(2, 2), ones(3, 1))
343+
344+
pts = [0.0 10.0; 1.0 11.0]
345+
lon_grid = [0.0 1.0; 0.0 1.0]
346+
lat_grid = [10.0 10.0; 11.0 11.0]
347+
@test all(isnan, ClimateTools.idw_griddata(pts, [NaN, NaN], lon_grid, lat_grid))
348+
349+
src_lon = [0.0, 1.0]
350+
src_lat = [10.0, 11.0]
351+
src = YAXArray((Dim{:longitude}(src_lon), Dim{:latitude}(src_lat)), [1.0 2.0; 3.0 4.0])
352+
dest = YAXArray((Dim{:longitude}([0.5]), Dim{:latitude}([10.5])), zeros(1, 1))
353+
regridder = Regridder(src, dest; method="bilinear")
354+
@test_throws ErrorException regrid(src, regridder; na_thres=1.5)
355+
356+
payload = ClimateTools._regridder_payload(regridder)
357+
restored = ClimateTools._regridder_from_payload(payload)
358+
@test restored.method == regridder.method
359+
@test restored.dest_lon == regridder.dest_lon
360+
361+
mktempdir() do tmpdir
362+
missing_version_path = joinpath(tmpdir, "missing-version.bin")
363+
open(missing_version_path, "w") do io
364+
Serialization.serialize(io, (method=:bilinear,))
365+
end
366+
@test_throws ErrorException load_regridder(missing_version_path)
367+
368+
bad_version_path = joinpath(tmpdir, "bad-version.bin")
369+
open(bad_version_path, "w") do io
370+
Serialization.serialize(io, (version=999, method=:bilinear))
371+
end
372+
@test_throws ErrorException load_regridder(bad_version_path)
373+
end
374+
end
375+
376+
@testset "Regrid rotated dataset helper fallback" begin
377+
north_pole_lon = 0.0
378+
north_pole_lat = 42.5
379+
380+
rlon_vals = collect(0.0:1.0:3.0)
381+
rlat_vals = collect(-1.0:1.0:2.0)
382+
rlon2d, rlat2d = ClimateTools.ndgrid(rlon_vals, rlat_vals)
383+
data = Float64.(rlon2d .+ 2 .* rlat2d)
384+
385+
pr_cube = YAXArray(
386+
(Dim{:rlon}(rlon_vals), Dim{:rlat}(rlat_vals)),
387+
data,
388+
Dict{String, Any}("grid_mapping" => "rotated_pole"),
389+
)
390+
rp_cube = YAXArray(
391+
(Dim{:maxStrlen64}(1:1),),
392+
[' '],
393+
Dict{String, Any}(
394+
"grid_north_pole_longitude" => north_pole_lon,
395+
"grid_north_pole_latitude" => north_pole_lat,
396+
),
397+
)
398+
ds = Dataset(pr=pr_cube, rotated_pole=rp_cube)
399+
400+
lon_geo, lat_geo = ClimateTools._extract_geographic_coords(ds, pr_cube, "rotated_pole")
401+
@test size(lon_geo) == size(data)
402+
@test size(lat_geo) == size(data)
403+
@test all(isfinite, lon_geo)
404+
@test all(isfinite, lat_geo)
405+
406+
dest = YAXArray((Dim{:longitude}(collect(range(minimum(lon_geo), maximum(lon_geo), length=3))), Dim{:latitude}(collect(range(minimum(lat_geo), maximum(lat_geo), length=3)))), zeros(3, 3))
407+
regridder = Regridder(ds, :pr, dest; method="idw")
408+
out = regrid(ds[:pr], regridder)
409+
@test size(out) == (3, 3)
410+
@test all(isfinite, Array(out))
411+
end

test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ using Dates
5555
include("spatial_test.jl")
5656
include("spatialsubset_test.jl")
5757
include("regrid_test.jl")
58+
include("xmap_utils_test.jl")
5859
include("functions_new_test.jl")
5960
include("functions_helpers_test.jl")
6061
include("climatology_test.jl")

test/xmap_utils_test.jl

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
using ClimateTools
2+
using Test
3+
using YAXArrays
4+
using DimensionalData
5+
6+
_xmap_sum_kernel(xout, xin; scale=1.0) = xout .= sum(xin) * scale
7+
8+
function _xmap_pair_kernel(xout, x1, x2, offset; scale=1.0)
9+
xout .= (sum(x1) + sum(x2) + offset) * scale
10+
end
11+
12+
@testset "xmap utility helpers" begin
13+
@test ClimateTools._normalize_xmap_dim(:time) == :time
14+
@test ClimateTools._normalize_xmap_dim("time") == :time
15+
@test ClimateTools._normalize_xmap_dims(:time) == (:time,)
16+
@test ClimateTools._normalize_xmap_dims((:longitude, "time")) == (:longitude, :time)
17+
@test ClimateTools._normalize_xmap_dims(["longitude", :latitude]) == (:longitude, :latitude)
18+
19+
axis = Dim{:stat}(["sum"])
20+
@test ClimateTools._normalize_xmap_output_axes() == ()
21+
@test ClimateTools._normalize_xmap_output_axes(axis) == (axis,)
22+
@test ClimateTools._normalize_xmap_output_axes((axis,)) == (axis,)
23+
@test ClimateTools._normalize_xmap_output_axes([axis]) == (axis,)
24+
25+
cube = YAXArray(
26+
(Dim{:longitude}([10.0, 20.0]), Dim{:latitude}([45.0, 50.0]), Dim{:time}(1:3)),
27+
reshape(Float64.(1:12), 2, 2, 3),
28+
)
29+
30+
single_window = ClimateTools._xmap_window(cube, (:time,))
31+
pair_window = ClimateTools._xmap_window(cube, (:longitude, :time))
32+
@test parentmodule(typeof(single_window)) == YAXArrays.Xmap
33+
@test parentmodule(typeof(pair_window)) == YAXArrays.Xmap
34+
35+
spec_no_axes = ClimateTools._xmap_output_spec(())
36+
spec_with_axes = ClimateTools._xmap_output_spec((axis,); outtype=Float32)
37+
@test spec_no_axes isa YAXArrays.Xmap.XOutput
38+
@test spec_with_axes isa YAXArrays.Xmap.XOutput
39+
40+
dropped = ClimateTools._drop_reduced_xmap_dims(
41+
YAXArray((Dim{:longitude}([10.0]), Dim{:latitude}([45.0, 50.0])), reshape([1.0, 2.0], 1, 2)),
42+
(:longitude,),
43+
)
44+
@test size(dropped) == (2,)
45+
@test Tuple(Symbol.(name.(dropped.axes))) == (:latitude,)
46+
end
47+
48+
@testset "xmap call reductions" begin
49+
cube = YAXArray(
50+
(Dim{:longitude}([10.0, 20.0]), Dim{:latitude}([45.0, 50.0]), Dim{:time}(1:4)),
51+
reshape(Float64.(1:16), 2, 2, 4),
52+
)
53+
54+
reduced = ClimateTools._xmap_call(
55+
_xmap_sum_kernel,
56+
cube;
57+
reduced_dims="time",
58+
function_kwargs=(scale=2.0,),
59+
)
60+
@test size(reduced) == (2, 2)
61+
@test Array(reduced) == 2.0 .* dropdims(sum(Array(cube); dims=3); dims=3)
62+
63+
stat_axis = Dim{:stat}(["combined"])
64+
shifted = YAXArray(cube.axes, Array(cube) .+ 1.0)
65+
combined = ClimateTools._xmap_call(
66+
_xmap_pair_kernel,
67+
(cube, shifted);
68+
reduced_dims=[:longitude, "time"],
69+
output_axes=[stat_axis],
70+
function_args=(5.0,),
71+
function_kwargs=(scale=0.5,),
72+
outtype=Float32,
73+
)
74+
75+
# Note: Xmap adds new dimensions (stat) BEFORE the existing ones (latitude)
76+
expected = Array{Float32}(undef, 1, 2)
77+
raw_cube = Array(cube)
78+
raw_shifted = Array(shifted)
79+
for ilat in 1:2
80+
expected[1, ilat] = Float32((sum(raw_cube[:, ilat, :]) + sum(raw_shifted[:, ilat, :]) + 5.0) * 0.5)
81+
end
82+
83+
@test size(combined) == (1, 2)
84+
@test Tuple(Symbol.(name.(combined.axes))) == (:stat, :latitude)
85+
@test Array(combined) expected
86+
@test eltype(Array(combined)) == Float32
87+
end

0 commit comments

Comments
 (0)