@@ -16,7 +16,8 @@ import ..Utils:
16
16
split_by_season,
17
17
time_to_date,
18
18
date_to_time,
19
- _data_at_dim_vals
19
+ _data_at_dim_vals,
20
+ _isequispaced
20
21
21
22
export OutputVar,
22
23
read_var,
@@ -80,37 +81,91 @@ struct OutputVar{T <: AbstractArray, A <: AbstractArray, B, C, ITP}
80
81
interpolant:: ITP
81
82
end
82
83
83
- function OutputVar (attribs, dims, dim_attribs, data)
84
- index2dim = keys (dims) |> collect
85
- dim2index =
86
- Dict ([dim_name => index for (index, dim_name) in enumerate (keys (dims))])
84
+ """
85
+ _make_interpolant(dims, data)
86
+
87
+ Make a linear interpolant from `dims`, a dictionary mapping dimension name to array and
88
+ `data`, an array containing data. Used in constructing a `OutputVar`.
89
+
90
+ If any element of the arrays in `dims` is a Dates.DateTime, then no interpolant is returned.
91
+ Interpolations.jl does not support interpolating on dates. If the longitudes span the entire
92
+ range and are equispaced, then a periodic boundary condition is added for the longitude
93
+ dimension. If the latitudes span the entire range and are equispaced, then a flat boundary
94
+ condition is added for the latitude dimension. In all other cases, an error is thrown when
95
+ extrapolating outside of `dim_array`.
96
+ """
97
+ function _make_interpolant (dims, data)
98
+ # If any element is DateTime, then return nothing for the interpolant because
99
+ # Interpolations.jl do not support DateTimes
100
+ for dim_array in values (dims)
101
+ eltype (dim_array) <: Dates.DateTime && return nothing
102
+ end
87
103
88
104
# We can only create interpolants when we have 1D dimensions
89
- if isempty (index2dim) ||
90
- any (d -> ndims (d) != 1 || length (d) == 1 , values (dims))
91
- itp = nothing
92
- else
93
- # Dimensions are all 1D, check that they are compatible with data
94
- size_data = size (data)
95
- for (dim_index, (dim_name, dim_array)) in enumerate (dims)
96
- dim_length = length (dim_array)
97
- data_length = size_data[dim_index]
98
- if dim_length != data_length
99
- error (
100
- " Dimension $dim_name has inconsistent size with provided data ($dim_length != $data_length )" ,
101
- )
102
- end
105
+ if isempty (dims) || any (d -> ndims (d) != 1 || length (d) == 1 , values (dims))
106
+ return nothing
107
+ end
108
+
109
+ # Dimensions are all 1D, check that they are compatible with data
110
+ size_data = size (data)
111
+ for (dim_index, (dim_name, dim_array)) in enumerate (dims)
112
+ dim_length = length (dim_array)
113
+ data_length = size_data[dim_index]
114
+ if dim_length != data_length
115
+ error (
116
+ " Dimension $dim_name has inconsistent size with provided data ($dim_length != $data_length )" ,
117
+ )
103
118
end
119
+ end
104
120
105
- dims_tuple = tuple (values (dims)... )
121
+ # Find boundary conditions for extrapolation
122
+ extp_bound_conds = (
123
+ _find_extp_bound_cond (dim_name, dim_array) for
124
+ (dim_name, dim_array) in dims
125
+ )
106
126
107
- # TODO : Make this lazy: we should compute the spline the first time we use
108
- # it, not when we create the object
109
- itp = Intp. extrapolate (
110
- Intp. interpolate (dims_tuple, data, Intp. Gridded (Intp. Linear ())),
111
- Intp. Throw (),
112
- )
113
- end
127
+ dims_tuple = tuple (values (dims)... )
128
+ extp_bound_conds_tuple = tuple (extp_bound_conds... )
129
+ return Intp. extrapolate (
130
+ Intp. interpolate (dims_tuple, data, Intp. Gridded (Intp. Linear ())),
131
+ extp_bound_conds_tuple,
132
+ )
133
+ end
134
+
135
+ """
136
+ _find_extp_bound_cond(dim_name, dim_array)
137
+
138
+ Find the appropriate boundary condition for the `dim_name` dimension.
139
+ """
140
+ function _find_extp_bound_cond (dim_name, dim_array)
141
+ min_of_dim, max_of_dim = extrema (dim_array)
142
+ dim_size = max_of_dim - min_of_dim
143
+ dsize = dim_array[begin + 1 ] - dim_array[begin ]
144
+
145
+ # If the dimension array span the entire range and is equispaced, then add the
146
+ # appropriate boundary condition
147
+ # We do not handle the cases when the array is not equispaced
148
+ (
149
+ conventional_dim_name (dim_name) == " longitude" &&
150
+ _isequispaced (dim_array) &&
151
+ isapprox (dim_size + dsize, 360.0 )
152
+ ) && return Intp. Periodic ()
153
+ (
154
+ conventional_dim_name (dim_name) == " latitude" &&
155
+ _isequispaced (dim_array) &&
156
+ isapprox (dim_size + dsize, 180.0 )
157
+ ) && return Intp. Flat ()
158
+ return Intp. Throw ()
159
+ end
160
+
161
+ function OutputVar (attribs, dims, dim_attribs, data)
162
+ index2dim = keys (dims) |> collect
163
+ dim2index =
164
+ Dict ([dim_name => index for (index, dim_name) in enumerate (keys (dims))])
165
+
166
+ # TODO : Make this lazy: we should compute the spline the first time we use
167
+ # it, not when we create the object
168
+ itp = _make_interpolant (dims, data)
114
169
115
170
function _maybe_process_key_value (k, v)
116
171
k != " units" && return k => v
710
765
Interpolate variable `x` onto the given `target_coord` coordinate using
711
766
multilinear interpolation.
712
767
713
- Extrapolation is now allowed and will throw a `BoundsError`.
768
+ Extrapolation is now allowed and will throw a `BoundsError` in most cases.
769
+
770
+ If any element of the arrays of the dimensions is a Dates.DateTime, then interpolation is
771
+ not possible. Interpolations.jl do not support making interpolations for dates. If the
772
+ longitudes span the entire range and are equispaced, then a periodic boundary condition is
773
+ added for the longitude dimension. If the latitudes span the entire range and are
774
+ equispaced, then a flat boundary condition is added for the latitude dimension. In all other
775
+ cases, an error is thrown when extrapolating outside of the array of the dimension.
714
776
715
777
Example
716
778
=======
0 commit comments