@@ -113,7 +113,7 @@ Keyword arguments
113
113
- `start_date`: Date of the beginning of the simulation.
114
114
"""
115
115
function NetCDFWriter (
116
- space,
116
+ space:: Spaces.AbstractSpace ,
117
117
output_dir;
118
118
num_points = (180 , 90 , 50 ),
119
119
compression_level = 9 ,
@@ -122,6 +122,10 @@ function NetCDFWriter(
122
122
z_sampling_method = LevelsMethod (),
123
123
start_date = nothing ,
124
124
)
125
+ has_horizontal_space =
126
+ space isa Spaces. ExtrudedFiniteDifferenceSpace ||
127
+ space isa Spaces. AbstractSpectralElementSpace
128
+
125
129
horizontal_space = Spaces. horizontal_space (space)
126
130
is_horizontal_space = horizontal_space == space
127
131
@@ -201,6 +205,63 @@ function NetCDFWriter(
201
205
)
202
206
end
203
207
208
+ function NetCDFWriter (
209
+ space:: Spaces.Spaces.FiniteDifferenceSpace ,
210
+ output_dir;
211
+ num_points = (180 , 90 , 50 ),
212
+ compression_level = 9 ,
213
+ sync_schedule = ClimaComms. device (space) isa ClimaComms. CUDADevice ?
214
+ EveryStepSchedule () : nothing ,
215
+ z_sampling_method = LevelsMethod (),
216
+ start_date = nothing ,
217
+ )
218
+ if z_sampling_method isa LevelsMethod
219
+ num_vpts = Meshes. nelements (Grids. vertical_topology (space). mesh)
220
+ @warn " Disabling vertical interpolation, the provided number of points is ignored (using $num_vpts )"
221
+ num_points = (num_vpts,)
222
+ end
223
+ vpts = target_coordinates (space, num_points, z_sampling_method)
224
+ target_zcoords = Geometry. ZPoint .(vpts)
225
+ remapper = Remapper (space; target_zcoords)
226
+
227
+ comms_ctx = ClimaComms. context (space)
228
+
229
+ coords_z = Fields. coordinate_field (space). z
230
+ maybe_move_to_cpu =
231
+ ClimaComms. device (coords_z) isa ClimaComms. CUDADevice &&
232
+ ClimaComms. iamroot (comms_ctx) ? Array : identity
233
+
234
+ interpolated_physical_z = maybe_move_to_cpu (interpolate (remapper, coords_z))
235
+
236
+ preallocated_arrays =
237
+ ClimaComms. iamroot (comms_ctx) ?
238
+ Dict {ScheduledDiagnostic, ClimaComms.array_type(space)} () :
239
+ Dict {ScheduledDiagnostic, Nothing} ()
240
+
241
+ unsynced_datasets = Set {NCDatasets.NCDataset} ()
242
+
243
+ return NetCDFWriter{
244
+ typeof (num_points),
245
+ typeof (interpolated_physical_z),
246
+ typeof (preallocated_arrays),
247
+ typeof (sync_schedule),
248
+ typeof (z_sampling_method),
249
+ typeof (start_date),
250
+ }(
251
+ output_dir,
252
+ Dict {String, Remapper} (),
253
+ num_points,
254
+ compression_level,
255
+ interpolated_physical_z,
256
+ Dict {String, NCDatasets.NCDataset} (),
257
+ z_sampling_method,
258
+ preallocated_arrays,
259
+ sync_schedule,
260
+ unsynced_datasets,
261
+ start_date,
262
+ )
263
+ end
264
+
204
265
"""
205
266
interpolate_field!(writer::NetCDFWriter, field, diagnostic, u, p, t)
206
267
@@ -212,61 +273,62 @@ function interpolate_field!(writer::NetCDFWriter, field, diagnostic, u, p, t)
212
273
213
274
space = axes (field)
214
275
215
- horizontal_space = Spaces . horizontal_space (space)
276
+ has_horizontal_space = ! (space isa Spaces . FiniteDifferenceSpace )
216
277
217
- # We have to deal with to cases: when we have an horizontal slice (e.g., the
218
- # surface), and when we have a full space. We distinguish these cases by checking if
219
- # the given space has the horizontal_space attribute. If not, it is going to be a
220
- # SpectralElementSpace2D and we don't have to deal with the z coordinates.
221
- is_horizontal_space = horizontal_space == space
278
+ if has_horizontal_space
279
+ horizontal_space = Spaces. horizontal_space (space)
280
+
281
+ # We have to deal with to cases: when we have an horizontal slice (e.g., the
282
+ # surface), and when we have a full space. We distinguish these cases by checking if
283
+ # the given space has the horizontal_space attribute. If not, it is going to be a
284
+ # SpectralElementSpace2D and we don't have to deal with the z coordinates.
285
+ is_horizontal_space = horizontal_space == space
286
+ end
222
287
223
288
# Prepare the remapper if we don't have one for the given variable. We need one remapper
224
289
# per variable (not one per diagnostic since all the time reductions return the same
225
290
# type of space).
226
291
227
- # TODO : Expand this once we support spatial reductions
292
+ # TODO : Expand this once we support spatial reductions.
293
+ # TODO : More generally, this can be clean up to have less conditionals
294
+ # depending on the type of space and use dispatch instead
228
295
if ! haskey (writer. remappers, var. short_name)
229
296
230
297
# hpts, vpts are ranges of numbers
231
- # hcoords, zcoords are ranges of Geometry.Points
232
-
233
- zcoords = []
234
-
235
- if is_horizontal_space
236
- hpts = target_coordinates (space, writer. num_points)
237
- vpts = []
298
+ # target_hcoords, target_zcoords are ranges of Geometry.Points
299
+
300
+ target_zcoords = nothing
301
+ target_hcoords = nothing
302
+
303
+ if has_horizontal_space
304
+ if is_horizontal_space
305
+ hpts = target_coordinates (space, writer. num_points)
306
+ vpts = []
307
+ else
308
+ hpts, vpts = target_coordinates (
309
+ space,
310
+ writer. num_points,
311
+ writer. z_sampling_method,
312
+ )
313
+ end
314
+
315
+ target_hcoords = hcoords_from_horizontal_space (
316
+ horizontal_space,
317
+ Meshes. domain (Spaces. topology (horizontal_space)),
318
+ hpts,
319
+ )
238
320
else
239
- hpts, vpts = target_coordinates (
321
+ vpts = target_coordinates (
240
322
space,
241
323
writer. num_points,
242
324
writer. z_sampling_method,
243
325
)
244
326
end
245
327
246
- hcoords = hcoords_from_horizontal_space (
247
- horizontal_space,
248
- Meshes. domain (Spaces. topology (horizontal_space)),
249
- hpts,
250
- )
251
-
252
- # When we disable vertical_interpolation, we override the vertical points with
253
- # the reference values for the vertical space.
254
- if writer. z_sampling_method isa LevelsMethod && ! is_horizontal_space
255
- # We need Array(parent()) because we want an array of values, not a DataLayout
256
- # of Points
257
- vpts = Array (
258
- parent (
259
- space. grid. vertical_grid. center_local_geometry. coordinates,
260
- ),
261
- )[
262
- :,
263
- 1 ,
264
- ]
265
- end
328
+ target_zcoords = Geometry. ZPoint .(vpts)
266
329
267
- zcoords = [Geometry. ZPoint (p) for p in vpts]
268
-
269
- writer. remappers[var. short_name] = Remapper (space, hcoords, zcoords)
330
+ writer. remappers[var. short_name] =
331
+ Remapper (space, target_hcoords, target_zcoords)
270
332
end
271
333
272
334
remapper = writer. remappers[var. short_name]
@@ -321,9 +383,7 @@ function write_field!(writer::NetCDFWriter, field, diagnostic, u, p, t)
321
383
interpolated_field =
322
384
maybe_move_to_cpu (writer. preallocated_output_arrays[diagnostic])
323
385
324
- if islatlonbox (
325
- Meshes. domain (Spaces. topology (Spaces. horizontal_space (space))),
326
- )
386
+ if islatlonbox (space)
327
387
# ClimaCore works with LatLong points, but we want to have longitude
328
388
# first in the output, so we have to flip things
329
389
perm = collect (1 : length (size (interpolated_field)))
@@ -441,14 +501,8 @@ function write_field!(writer::NetCDFWriter, field, diagnostic, u, p, t)
441
501
[date_type (nc[" date" ][time_index - 1 ]); curr_date]
442
502
end
443
503
444
- # TODO : It would be nice to find a cleaner way to do this
445
- if length (dim_names) == 3
446
- v[time_index, :, :, :] = interpolated_field
447
- elseif length (dim_names) == 2
448
- v[time_index, :, :] = interpolated_field
449
- elseif length (dim_names) == 1
450
- v[time_index, :] = interpolated_field
451
- end
504
+ colons = ntuple (_ -> Colon (), length (dim_names))
505
+ v[time_index, colons... ] = interpolated_field
452
506
453
507
# Add file to list of files that might need manual sync
454
508
push! (writer. unsynced_datasets, nc)
0 commit comments