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