Skip to content

Commit 6273f0a

Browse files
committed
Add Var.reordered_as
1 parent c53c5d2 commit 6273f0a

File tree

6 files changed

+219
-0
lines changed

6 files changed

+219
-0
lines changed

NEWS.md

+30
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,36 @@
11
ClimaAnalysis.jl Release Notes
22
===============================
33

4+
v0.5.9
5+
------
6+
7+
## Features
8+
9+
### Reorder dimensions
10+
Before, resampling requires that the order of the dimensions is the same between the two
11+
`OutputVar`s. This feature adds the functionality of reordering the dimensions in a
12+
`OutputVar` to match the ordering of dimensions in another `OutputVar`. See the example
13+
below of this functionality.
14+
15+
```julia
16+
julia> src_var.dims |> keys |> collect
17+
2-element Vector{String}:
18+
"long"
19+
"lat"
20+
21+
julia> dest_var.dims |> keys |> collect
22+
2-element Vector{String}:
23+
"lat"
24+
"long"
25+
26+
julia> reordered_var = ClimaAnalysis.reordered_as(src_var, dest_var);
27+
28+
julia> reordered_var.dims |> keys |> collect
29+
2-element Vector{String}:
30+
"lat"
31+
"long"
32+
```
33+
434
v0.5.8
535
------
636

docs/Project.toml

+2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
33
ClimaAnalysis = "29b5916a-a76c-4e73-9657-3c8fd22e65e6"
44
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
55
GeoMakie = "db073c08-6b98-4ee5-b6a4-5efafb3259c6"
6+
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
67

78
[compat]
89
CairoMakie = "0.12.5"
910
Documenter = "1"
1011
GeoMakie = "0.7.3"
12+
OrderedCollections = "1.3"

docs/src/api.md

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ Var.has_altitude
5454
Var.conventional_dim_name
5555
Var.dim_units
5656
Var.range_dim
57+
Var.reordered_as
5758
Var.resampled_as
5859
Var.convert_units
5960
Var.integrate_lonlat

docs/src/howdoi.md

+45
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,51 @@ The `Atmos` module in `ClimaAnalysis` comes with a function,
6767
`OutputVar` is returned where the values are linearly interpolated on fixed
6868
pressure levels.
6969
70+
## How do I reorder the dimensions in a `OutputVar` to match the dimensions in another `OutputVar`?
71+
72+
You can use the `reordered_as(src_var, dest_var)` function where `src_var` is a `OutputVar`
73+
with the dimensions you want to reorder to match the dimensions in the OutputVar `dest_var`.
74+
75+
```@setup reordered_as
76+
import ClimaAnalysis
77+
import OrderedCollections: OrderedDict
78+
79+
src_long = 0.0:180.0 |> collect
80+
src_lat = 0.0:90.0 |> collect
81+
src_data = ones(length(src_long), length(src_lat))
82+
src_dims = OrderedDict(["long" => src_long, "lat" => src_lat])
83+
src_attribs = Dict("long_name" => "hi")
84+
src_dim_attribs = OrderedDict([
85+
"long" => Dict("units" => "test_units1"),
86+
"lat" => Dict("units" => "test_units2"),
87+
])
88+
src_var =
89+
ClimaAnalysis.OutputVar(src_attribs, src_dims, src_dim_attribs, src_data)
90+
91+
dest_long = 20.0:180.0 |> collect
92+
dest_lat = 30.0:90.0 |> collect
93+
dest_data = zeros(length(dest_lat), length(dest_long))
94+
dest_dims = OrderedDict(["lat" => dest_lat, "long" => dest_long])
95+
dest_attribs = Dict("long_name" => "hi")
96+
dest_dim_attribs = OrderedDict([
97+
"lat" => Dict("units" => "test_units4"),
98+
"long" => Dict("units" => "test_units3"),
99+
])
100+
dest_var = ClimaAnalysis.OutputVar(
101+
dest_attribs,
102+
dest_dims,
103+
dest_dim_attribs,
104+
dest_data,
105+
)
106+
```
107+
108+
```@repl reordered_as
109+
src_var.dims |> keys |> collect
110+
dest_var.dims |> keys |> collect
111+
reordered_var = ClimaAnalysis.reordered_as(src_var, dest_var);
112+
reordered_var.dims |> keys |> collect
113+
```
114+
70115
## How do I resample the data in a `OutputVar` using the dimensions from another `OutputVar`?
71116
72117
You can use the `resampled_as(src_var, dest_var)` function where `src_var` is a

src/Var.jl

+43
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export OutputVar,
3737
units,
3838
dim_units,
3939
range_dim,
40+
reordered_as,
4041
resampled_as,
4142
has_units,
4243
convert_units,
@@ -787,6 +788,48 @@ function _check_dims_consistent(x::OutputVar, y::OutputVar)
787788
return nothing
788789
end
789790

791+
"""
792+
reordered_as(src_var::OutputVar, dest_var::OutputVar)
793+
794+
Reorder the dimensions in `src_var` to match the ordering of dimensions in `dest_var`.
795+
"""
796+
function reordered_as(src_var::OutputVar, dest_var::OutputVar)
797+
# Get the conventional dim names for both src_var and dest_var
798+
conventional_dim_name_src = conventional_dim_name.(keys(src_var.dims))
799+
conventional_dim_name_dest = conventional_dim_name.(keys(dest_var.dims))
800+
801+
# Check if the dimensions are the same (order does not matter)
802+
Set(conventional_dim_name_src) == Set(conventional_dim_name_dest) || error(
803+
"Dimensions are not the same between src ($conventional_dim_name_src) and dest ($conventional_dim_name_dest)",
804+
)
805+
806+
# Find permutation indices to reorder dims
807+
reorder_indices =
808+
indexin(conventional_dim_name_dest, conventional_dim_name_src)
809+
810+
# Reorder dims, dim_attribs, and data, but not attribs
811+
ret_dims = deepcopy(src_var.dims)
812+
ret_dims = OrderedDict(collect(ret_dims)[reorder_indices])
813+
ret_attribs = deepcopy(src_var.attributes)
814+
815+
# Cannot assume that every dimension is present in dim_attribs so we loop to reorder the
816+
# best we can and merge with src_var.dim_attributes to add any remaining pairs to
817+
# ret_dim_attribs
818+
ret_dim_attribs = empty(src_var.dim_attributes)
819+
src_var_dim_attribs = src_var.dim_attributes |> deepcopy
820+
src_var_dim_names = collect(keys(src_var.dims))
821+
for idx in reorder_indices
822+
dim_name = src_var_dim_names[idx]
823+
haskey(src_var_dim_attribs, dim_name) &&
824+
(ret_dim_attribs[dim_name] = src_var_dim_attribs[dim_name])
825+
end
826+
merge!(ret_dim_attribs, src_var_dim_attribs)
827+
828+
ret_data = copy(src_var.data)
829+
ret_data = permutedims(ret_data, reorder_indices)
830+
return OutputVar(ret_attribs, ret_dims, ret_dim_attribs, ret_data)
831+
end
832+
790833
"""
791834
resampled_as(src_var::OutputVar, dest_var::OutputVar)
792835

test/test_Var.jl

+98
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,104 @@ end
601601

602602
end
603603

604+
@testset "Reordering" begin
605+
# Reordering the dimensions of a var to match itself
606+
src_long = 0.0:180.0 |> collect
607+
src_lat = 0.0:90.0 |> collect
608+
src_data = ones(length(src_long), length(src_lat))
609+
src_dims = OrderedDict(["long" => src_long, "lat" => src_lat])
610+
src_attribs = Dict("long_name" => "hi")
611+
src_dim_attribs = OrderedDict([
612+
"long" => Dict("units" => "test_units1"),
613+
"lat" => Dict("units" => "test_units2"),
614+
])
615+
src_var = ClimaAnalysis.OutputVar(
616+
src_attribs,
617+
src_dims,
618+
src_dim_attribs,
619+
src_data,
620+
)
621+
reordered_var = ClimaAnalysis.reordered_as(src_var, src_var)
622+
@test reordered_var.attributes == src_var.attributes
623+
@test reordered_var.dims == src_var.dims
624+
@test reordered_var.dim_attributes == src_var.dim_attributes
625+
@test reordered_var.data == src_var.data
626+
627+
# Reordering the dimensions of src_var to match a different order of dimensions in
628+
# dest_var
629+
dest_long = 20.0:180.0 |> collect
630+
dest_lat = 30.0:90.0 |> collect
631+
dest_data = zeros(length(dest_lat), length(dest_long))
632+
dest_dims = OrderedDict(["lat" => dest_lat, "long" => dest_long])
633+
dest_attribs = Dict("long_name" => "hi")
634+
dest_dim_attribs = OrderedDict([
635+
"lat" => Dict("units" => "test_units4"),
636+
"long" => Dict("units" => "test_units3"),
637+
])
638+
dest_var = ClimaAnalysis.OutputVar(
639+
dest_attribs,
640+
dest_dims,
641+
dest_dim_attribs,
642+
dest_data,
643+
)
644+
reordered_var = ClimaAnalysis.reordered_as(src_var, dest_var)
645+
@test reordered_var.attributes == src_var.attributes
646+
@test reordered_var.dims ==
647+
OrderedDict(["lat" => src_lat, "long" => src_long])
648+
@test reordered_var.dim_attributes == OrderedDict([
649+
"lat" => Dict("units" => "test_units2"),
650+
"long" => Dict("units" => "test_units1"),
651+
])
652+
@test reordered_var.data == ones(length(src_lat), length(src_long))
653+
654+
# Reordering but dim_attributes is not available for every dimension
655+
src_dim_attribs_one = OrderedDict(["lat" => Dict("units" => "test_units2")])
656+
src_dim_attribs_empty = empty(src_dim_attribs_one)
657+
src_dim_attribs_extra = OrderedDict([
658+
"extra_info" => "hi",
659+
"lat" => Dict("units" => "test_units2"),
660+
])
661+
src_var_one = ClimaAnalysis.OutputVar(
662+
src_attribs,
663+
src_dims,
664+
src_dim_attribs_one,
665+
src_data,
666+
)
667+
src_var_empty = ClimaAnalysis.OutputVar(
668+
src_attribs,
669+
src_dims,
670+
src_dim_attribs_empty,
671+
src_data,
672+
)
673+
src_var_extra = ClimaAnalysis.OutputVar(
674+
src_attribs,
675+
src_dims,
676+
src_dim_attribs_extra,
677+
src_data,
678+
)
679+
reordered_var = ClimaAnalysis.reordered_as(src_var_one, dest_var)
680+
@test reordered_var.dim_attributes == src_dim_attribs_one
681+
reordered_var = ClimaAnalysis.reordered_as(src_var_empty, dest_var)
682+
@test reordered_var.dim_attributes == src_dim_attribs_empty
683+
reordered_var = ClimaAnalysis.reordered_as(src_var_extra, dest_var)
684+
@test reordered_var.dim_attributes == OrderedDict([
685+
"lat" => Dict("units" => "test_units2"),
686+
"extra_info" => "hi",
687+
])
688+
689+
# Error checking for dimensions not being the same in both
690+
src_long = 20.0:180.0 |> collect
691+
src_lat = 30.0:90.0 |> collect
692+
src_dims = OrderedDict(["long" => src_long, "lat" => src_lat])
693+
src_data = ones(length(src_long), length(src_lat))
694+
dest_lat = 30.0:90.0 |> collect
695+
dest_dims = OrderedDict(["lat" => dest_lat])
696+
dest_data = ones(length(src_lat))
697+
src_var = ClimaAnalysis.OutputVar(src_dims, src_data)
698+
dest_var = ClimaAnalysis.OutputVar(dest_dims, dest_data)
699+
@test_throws ErrorException ClimaAnalysis.reordered_as(src_var, dest_var)
700+
end
701+
604702
@testset "Resampling" begin
605703
src_long = 0.0:180.0 |> collect
606704
src_lat = 0.0:90.0 |> collect

0 commit comments

Comments
 (0)