@@ -80,21 +80,197 @@ Regrid the given data as defined on the given dimensions to the `target_space` i
8080This function is allocating.
8181"""
8282function Regridders. regrid (regridder:: InterpolationsRegridder , data, dimensions)
83+ # TODO : There is room for improvement in this function...
84+
8385 FT = ClimaCore. Spaces. undertype (regridder. target_space)
8486 dimensions_FT = map (d -> FT .(d), dimensions)
8587
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,
90207 )
208+ return val
209+ end
210+
211+ """
212+ linear_interp_z(f, z, target_z)
213+
214+ Perform linear interpolation along the z-axis.
91215
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.
94220
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]
97244 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
98274end
99275
100276end
0 commit comments