@@ -80,21 +80,197 @@ Regrid the given data as defined on the given dimensions to the `target_space` i
80
80
This function is allocating.
81
81
"""
82
82
function Regridders. regrid (regridder:: InterpolationsRegridder , data, dimensions)
83
+ # TODO : There is room for improvement in this function...
84
+
83
85
FT = ClimaCore. Spaces. undertype (regridder. target_space)
84
86
dimensions_FT = map (d -> FT .(d), dimensions)
85
87
86
- # Make a linear spline
87
- itp = Intp. extrapolate (
88
- Intp. interpolate (dimensions_FT, FT .(data), Intp. Gridded (Intp. Linear ())),
89
- regridder. extrapolation_bc,
88
+ coordinates = ClimaCore. Fields. coordinate_field (regridder. target_space)
89
+ device = ClimaComms. device (regridder. target_space)
90
+
91
+ has_3d_z = length (size (last (dimensions))) == 3
92
+ if eltype (coordinates) <: ClimaCore.Geometry.LatLongZPoint && has_3d_z
93
+ # If we have 3D altitudes, we do linear in the vertical and bilinear
94
+ # horizontal separately
95
+ @warn " Ignoring boundary conditions, implementing Periodic, Flat, Flat"
96
+
97
+ adapted_data = Adapt. adapt (ClimaComms. array_type (regridder. target_space), data)
98
+ xs, ys, zs = dimensions_FT
99
+ adapted_xs = Adapt. adapt (ClimaComms. array_type (regridder. target_space), xs)
100
+ adapted_ys = Adapt. adapt (ClimaComms. array_type (regridder. target_space), ys)
101
+ adapted_zs = Adapt. adapt (ClimaComms. array_type (regridder. target_space), zs)
102
+
103
+ return ClimaComms. allowscalar (ClimaComms. device (regridder. target_space)) do
104
+ map (regridder. coordinates) do coord
105
+ interpolation_3d_z (
106
+ adapted_data,
107
+ adapted_xs, adapted_ys, adapted_zs,
108
+ totuple (coord)... ,
109
+ )
110
+ end
111
+ end
112
+ else
113
+ # Make a linear spline
114
+ itp = Intp. extrapolate (
115
+ Intp. interpolate (
116
+ dimensions_FT,
117
+ FT .(data),
118
+ Intp. Gridded (Intp. Linear ()),
119
+ ),
120
+ regridder. extrapolation_bc,
121
+ )
122
+
123
+ # Move it to GPU (if needed)
124
+ gpuitp = Adapt. adapt (ClimaComms. array_type (regridder. target_space), itp)
125
+
126
+ return map (regridder. coordinates) do coord
127
+ gpuitp (totuple (coord)... )
128
+ end
129
+ end
130
+ end
131
+
132
+ """
133
+ interpolation_3d_z(data, xs, ys, zs, target_x, target_y, target_z)
134
+
135
+ Perform bilinear + vertical interpolation on a 3D dataset.
136
+
137
+ This function first performs linear interpolation along the z-axis at the four
138
+ corners of the cell containing the target (x, y) point. Then, it performs
139
+ bilinear interpolation in the x-y plane using the z-interpolated values.
140
+
141
+ Periodic is implemented on the x direction, Flat on the other ones.
142
+
143
+ # Arguments
144
+ - `data`: A 3D array of data values.
145
+ - `xs`: A vector of x-coordinates corresponding to the first dimension of `data`.
146
+ - `ys`: A vector of y-coordinates corresponding to the second dimension of `data`.
147
+ - `zs`: A 3D array of z-coordinates. `zs[i, j, :]` provides the z-coordinates for the data point `data[i, j, :]`.
148
+ - `target_x`: The x-coordinate of the target point.
149
+ - `target_y`: The y-coordinate of the target point.
150
+ - `target_z`: The z-coordinate of the target point.
151
+ """
152
+ function interpolation_3d_z (data, xs, ys, zs, target_x, target_y, target_z)
153
+ # Check boundaries
154
+ # if target_x < xs[begin] || target_x > xs[end]
155
+ # error(
156
+ # "target_x is out of bounds: $(target_x) not in [$(xs[1]), $(xs[end])]",
157
+ # )
158
+ # end
159
+ # if target_y < ys[begin] || target_y > ys[end]
160
+ # error(
161
+ # "target_y is out of bounds: $(target_y) not in [$(ys[1]), $(ys[end])]",
162
+ # )
163
+ # end
164
+
165
+ # Find nearest neighbors
166
+ x_period = xs[end ] - xs[begin ]
167
+ target_x = mod (target_x, x_period)
168
+
169
+ x_index = searchsortedfirst (xs, target_x)
170
+ y_index = searchsortedfirst (ys, target_y)
171
+
172
+ x0_index = x_index == 1 ? x_index : x_index - 1
173
+ x1_index = x0_index + 1
174
+
175
+ y0_index = y_index == 1 ? y_index : y_index - 1
176
+ # Flat
177
+ y0_index = clamp (y0_index, 1 , length (ys) - 1 )
178
+ y1_index = y0_index + 1
179
+ if y0_index == 1 && y0_index == length (ys) - 1
180
+ target_y = ys[y0_index]
181
+ end
182
+
183
+ # Interpolate in z-direction
184
+
185
+ z00 = @view zs[x0_index, y0_index, :]
186
+ z01 = @view zs[x0_index, y1_index, :]
187
+ z10 = @view zs[x1_index, y0_index, :]
188
+ z11 = @view zs[x1_index, y1_index, :]
189
+
190
+ f00 = linear_interp_z (view (data,x0_index, y0_index, :), z00, target_z)
191
+ f01 = linear_interp_z (view (data,x0_index, y1_index, :), z01, target_z)
192
+ f10 = linear_interp_z (view (data,x1_index, y0_index, :), z10, target_z)
193
+ f11 = linear_interp_z (view (data,x1_index, y1_index, :), z11, target_z)
194
+
195
+ # Bilinear interpolation in x-y plane
196
+ val = bilinear_interp (
197
+ f00,
198
+ f01,
199
+ f10,
200
+ f11,
201
+ xs[x0_index],
202
+ xs[x1_index],
203
+ ys[y0_index],
204
+ ys[y1_index],
205
+ target_x,
206
+ target_y,
90
207
)
208
+ return val
209
+ end
210
+
211
+ """
212
+ linear_interp_z(f, z, target_z)
213
+
214
+ Perform linear interpolation along the z-axis.
91
215
92
- # Move it to GPU (if needed)
93
- gpuitp = Adapt. adapt (ClimaComms. array_type (regridder. target_space), itp)
216
+ # Arguments
217
+ - `f`: A vector of function values corresponding to the z-coordinates in `z`.
218
+ - `z`: A vector of z-coordinates.
219
+ - `target_z`: The z-coordinate at which to interpolate.
94
220
95
- return map (regridder. coordinates) do coord
96
- gpuitp (totuple (coord)... )
221
+ # Returns
222
+ The linearly interpolated value at `target_z`.
223
+ """
224
+ function linear_interp_z (f, z, target_z)
225
+ # if target_z < z[begin] || target_z > z[end]
226
+ # error(
227
+ # "target_z is out of bounds: $(target_z) not in [$(z[1]), $(z[end])]",
228
+ # )
229
+ # end
230
+
231
+ index = searchsortedfirst (z, target_z)
232
+ # Handle edge cases for index
233
+ # Flat
234
+ if index == 1
235
+ z0 = z[index]
236
+ z1 = z[index + 1 ]
237
+ f0 = f[index]
238
+ f1 = f[index + 1 ]
239
+ else
240
+ z0 = z[index - 1 ]
241
+ z1 = z[index]
242
+ f0 = f[index - 1 ]
243
+ f1 = f[index]
97
244
end
245
+
246
+ return f0 + (target_z - z0) / (z1 - z0) * (f1 - f0)
247
+ end
248
+
249
+ """
250
+ bilinear_interp(f00, f01, f10, f11, x0, x1, y0, y1, target_x, target_y)
251
+
252
+ Perform bilinear interpolation on a 2D plane.
253
+
254
+ # Arguments
255
+ - `f00`: Function value at (x0, y0).
256
+ - `f01`: Function value at (x0, y1).
257
+ - `f10`: Function value at (x1, y0).
258
+ - `f11`: Function value at (x1, y1).
259
+ - `x0`: x-coordinate of the first corner.
260
+ - `x1`: x-coordinate of the second corner.
261
+ - `y0`: y-coordinate of the first corner.
262
+ - `y1`: y-coordinate of the second corner.
263
+ - `target_x`: The x-coordinate of the target point.
264
+ - `target_y`: The y-coordinate of the target point.
265
+ """
266
+ function bilinear_interp (f00, f01, f10, f11, x0, x1, y0, y1, target_x, target_y)
267
+ val = (
268
+ (x1 - target_x) * (y1 - target_y) / ((x1 - x0) * (y1 - y0)) * f00 +
269
+ (x1 - target_x) * (target_y - y0) / ((x1 - x0) * (y1 - y0)) * f01 +
270
+ (target_x - x0) * (y1 - target_y) / ((x1 - x0) * (y1 - y0)) * f10 +
271
+ (target_x - x0) * (target_y - y0) / ((x1 - x0) * (y1 - y0)) * f11
272
+ )
273
+ return val
98
274
end
99
275
100
276
end
0 commit comments