@@ -113,7 +113,7 @@ Keyword arguments
113113- `start_date`: Date of the beginning of the simulation.
114114"""
115115function NetCDFWriter (
116- space,
116+ space:: Spaces.AbstractSpace ,
117117 output_dir;
118118 num_points = (180 , 90 , 50 ),
119119 compression_level = 9 ,
@@ -122,6 +122,10 @@ function NetCDFWriter(
122122 z_sampling_method = LevelsMethod (),
123123 start_date = nothing ,
124124)
125+ has_horizontal_space =
126+ space isa Spaces. ExtrudedFiniteDifferenceSpace ||
127+ space isa Spaces. AbstractSpectralElementSpace
128+
125129 horizontal_space = Spaces. horizontal_space (space)
126130 is_horizontal_space = horizontal_space == space
127131
@@ -201,6 +205,63 @@ function NetCDFWriter(
201205 )
202206end
203207
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+
204265"""
205266 interpolate_field!(writer::NetCDFWriter, field, diagnostic, u, p, t)
206267
@@ -212,61 +273,62 @@ function interpolate_field!(writer::NetCDFWriter, field, diagnostic, u, p, t)
212273
213274 space = axes (field)
214275
215- horizontal_space = Spaces . horizontal_space (space)
276+ has_horizontal_space = ! (space isa Spaces . FiniteDifferenceSpace )
216277
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
222287
223288 # Prepare the remapper if we don't have one for the given variable. We need one remapper
224289 # per variable (not one per diagnostic since all the time reductions return the same
225290 # type of space).
226291
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
228295 if ! haskey (writer. remappers, var. short_name)
229296
230297 # 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+ )
238320 else
239- hpts, vpts = target_coordinates (
321+ vpts = target_coordinates (
240322 space,
241323 writer. num_points,
242324 writer. z_sampling_method,
243325 )
244326 end
245327
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)
266329
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)
270332 end
271333
272334 remapper = writer. remappers[var. short_name]
@@ -321,9 +383,7 @@ function write_field!(writer::NetCDFWriter, field, diagnostic, u, p, t)
321383 interpolated_field =
322384 maybe_move_to_cpu (writer. preallocated_output_arrays[diagnostic])
323385
324- if islatlonbox (
325- Meshes. domain (Spaces. topology (Spaces. horizontal_space (space))),
326- )
386+ if islatlonbox (space)
327387 # ClimaCore works with LatLong points, but we want to have longitude
328388 # first in the output, so we have to flip things
329389 perm = collect (1 : length (size (interpolated_field)))
@@ -441,14 +501,8 @@ function write_field!(writer::NetCDFWriter, field, diagnostic, u, p, t)
441501 [date_type (nc[" date" ][time_index - 1 ]); curr_date]
442502 end
443503
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
452506
453507 # Add file to list of files that might need manual sync
454508 push! (writer. unsynced_datasets, nc)
0 commit comments