Skip to content

Commit 27aa9e8

Browse files
committed
Add default_num_points
`default_num_points` is a new function that automatically computes a tuple with the number of points along each direction of a `space`. `default_num_points` is now the default value for the `NetCDF` writer, so that users will automatically get a reasonably sampled output.
1 parent 74dce08 commit 27aa9e8

File tree

6 files changed

+150
-5
lines changed

6 files changed

+150
-5
lines changed

NEWS.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ the interval `[0.0, 10.0]`. If one knows that the data represents a time
1414
average, then the time of `10.0` is the time average over the interval
1515
`[0.0, 10.0]`.
1616

17+
### NetCDF writer now defaults to a reasonable number of points
18+
19+
`ClimaDiagnostics.Writers.NetCDF` now has a new default argument that depends on
20+
the input Space. With this new default, obtained by calling the
21+
`ClimaDiagnostics.Writers.default_num_points(space)` function, the output
22+
diagnostics will be sampled with approximately the same resolution as the given
23+
`space`.
24+
1725
### Support for `lazy`
1826

1927
Starting version `0.2.13`, `ClimaDiagnostics` supports diagnostic variables

docs/src/api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,6 @@ ClimaDiagnostics.Writers.NetCDFWriter
5050
ClimaDiagnostics.Writers.HDF5Writer
5151
ClimaDiagnostics.Writers.interpolate_field!
5252
ClimaDiagnostics.Writers.write_field!
53+
ClimaDiagnostics.Writers.default_num_points
5354
Base.close
5455
```

src/Writers.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ Writers can implement:
1515
"""
1616
module Writers
1717

18+
import ClimaCore
19+
1820
import ..AbstractWriter, ..ScheduledDiagnostic
1921
import ..ScheduledDiagnostics: output_short_name, output_long_name
2022

src/netcdf_writer.jl

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ Keyword arguments
121121
function NetCDFWriter(
122122
space::Spaces.AbstractSpace,
123123
output_dir;
124-
num_points = (180, 90, 50),
124+
num_points = default_num_points(space),
125125
compression_level = 9,
126126
sync_schedule = ClimaComms.device(space) isa ClimaComms.CUDADevice ?
127127
EveryStepSchedule() : nothing,
@@ -149,7 +149,7 @@ function NetCDFWriter(
149149
Spaces.horizontal_space(space) isa
150150
Spaces.SpectralElementSpace1D ? 1 : 2
151151

152-
num_vpts = Meshes.nelements(Grids.vertical_topology(space).mesh)
152+
num_vpts = Spaces.nlevels(space)
153153

154154
# For any configuration, it is reasonable to assume that the last
155155
# value of `num_pts` is the number of vertical points
@@ -214,16 +214,17 @@ end
214214
function NetCDFWriter(
215215
space::Spaces.Spaces.FiniteDifferenceSpace,
216216
output_dir;
217-
num_points = (180, 90, 50),
217+
num_points = default_num_points(space),
218218
compression_level = 9,
219219
sync_schedule = ClimaComms.device(space) isa ClimaComms.CUDADevice ?
220220
EveryStepSchedule() : nothing,
221221
z_sampling_method = LevelsMethod(),
222222
start_date = nothing,
223223
)
224224
if z_sampling_method isa LevelsMethod
225-
num_vpts = Meshes.nelements(Grids.vertical_topology(space).mesh)
226-
@warn "Disabling vertical interpolation, the provided number of points is ignored (using $num_vpts)"
225+
num_vpts = Spaces.nlevels(ClimaCore.Spaces.center_space(space))
226+
num_vpts == last(num_points) ||
227+
@warn "Disabling vertical interpolation, the provided number of points is ignored (using $num_vpts)"
227228
num_points = (num_vpts,)
228229
end
229230
vpts = target_coordinates(space, num_points, z_sampling_method)

src/netcdf_writer_coordinates.jl

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,3 +661,90 @@ Prepare the matrix of horizontal coordinates with the correct type according to
661661
and `domain` (e.g., `ClimaCore.Geometry.LatLongPoint`s).
662662
"""
663663
function hcoords_from_horizontal_space(space, domain, hpts) end
664+
665+
666+
"""
667+
default_num_points(space)
668+
669+
Return a tuple with number of points that are optimally suited to interpolate the given
670+
`space`.
671+
672+
"Optimally suited" here means approximately the same as the number of points as the given
673+
`space`.
674+
"""
675+
function default_num_points(
676+
space::ClimaCore.Spaces.ExtrudedFiniteDifferenceSpace,
677+
)
678+
horizontal_space = ClimaCore.Spaces.horizontal_space(space)
679+
num_horz = default_num_points(horizontal_space)
680+
681+
vertical_space = ClimaCore.Spaces.FiniteDifferenceSpace(
682+
Spaces.vertical_topology(space),
683+
Spaces.staggering(space),
684+
)
685+
num_vert = default_num_points(vertical_space)
686+
return (num_horz..., num_vert...)
687+
end
688+
689+
# 2D sphere
690+
function default_num_points(
691+
space::ClimaCore.Spaces.CubedSphereSpectralElementSpace2D,
692+
)
693+
# A cubed sphere has 4 panels to cover the range of longitudes, each panel has
694+
# `num_elements_per_panel` elements, each with `unique_degrees_of_freedom` points. Same
695+
# for latitudes, except that we need 2 panels to cover from 0 to 180.
696+
697+
unique_degrees_of_freedom = ClimaCore.Quadratures.unique_degrees_of_freedom(
698+
ClimaCore.Grids.quadrature_style(space),
699+
)
700+
num_elements_per_panel = ClimaCore.Meshes.n_elements_per_panel_direction(
701+
ClimaCore.Spaces.topology(space).mesh,
702+
)
703+
num_lat = 2 * num_elements_per_panel * unique_degrees_of_freedom
704+
num_lon = 2num_lat
705+
return (num_lon, num_lat)
706+
end
707+
708+
# TODO: Maybe move to ClimaCore?
709+
const RectilinearSpectralElementSpace1D =
710+
ClimaCore.Spaces.SpectralElementSpace1D{
711+
<:ClimaCore.Grids.SpectralElementGrid1D{
712+
<:ClimaCore.Topologies.IntervalTopology,
713+
},
714+
}
715+
716+
# 1D box
717+
function default_num_points(space::RectilinearSpectralElementSpace1D)
718+
unique_degrees_of_freedom = ClimaCore.Quadratures.unique_degrees_of_freedom(
719+
ClimaCore.Grids.quadrature_style(space),
720+
)
721+
return (
722+
unique_degrees_of_freedom *
723+
ClimaCore.Meshes.nelements(ClimaCore.Spaces.topology(space).mesh),
724+
)
725+
end
726+
727+
# 2D box
728+
function default_num_points(
729+
space::ClimaCore.Spaces.RectilinearSpectralElementSpace2D,
730+
)
731+
unique_degrees_of_freedom = ClimaCore.Quadratures.unique_degrees_of_freedom(
732+
ClimaCore.Grids.quadrature_style(space),
733+
)
734+
735+
return (
736+
unique_degrees_of_freedom * ClimaCore.Meshes.nelements(
737+
ClimaCore.Spaces.topology(space).mesh.intervalmesh1,
738+
),
739+
unique_degrees_of_freedom * ClimaCore.Meshes.nelements(
740+
ClimaCore.Spaces.topology(space).mesh.intervalmesh2,
741+
),
742+
)
743+
end
744+
745+
# Column
746+
function default_num_points(space::Spaces.FiniteDifferenceSpace)
747+
# We always want the center space for interpolation
748+
cspace = Spaces.center_space(space)
749+
return (ClimaCore.Spaces.nlevels(cspace),)
750+
end

test/writers.jl

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import ClimaCore
1010
import ClimaCore.Fields
1111
import ClimaCore.Spaces
1212
import ClimaCore.Geometry
13+
import ClimaCore.CommonSpaces
1314
import ClimaComms
1415

1516
import ClimaDiagnostics
@@ -38,6 +39,51 @@ output_dir = mktempdir(pwd())
3839
end
3940

4041
@testset "NetCDFWriter" begin
42+
@testset "default_num_points" begin
43+
@test Writers.default_num_points(
44+
CommonSpaces.ExtrudedCubedSphereSpace(;
45+
z_elem = 10,
46+
z_min = 0,
47+
z_max = 1,
48+
radius = 10,
49+
h_elem = 10,
50+
n_quad_points = 4,
51+
staggering = CommonSpaces.CellCenter(),
52+
),
53+
) == (120, 60, 10)
54+
55+
@test Writers.default_num_points(
56+
CommonSpaces.SliceXZSpace(;
57+
z_elem = 10,
58+
x_min = 0,
59+
x_max = 1,
60+
z_min = 0,
61+
z_max = 1,
62+
periodic_x = false,
63+
n_quad_points = 4,
64+
x_elem = 4,
65+
staggering = CommonSpaces.CellCenter(),
66+
),
67+
) == (12, 10)
68+
@test Writers.default_num_points(
69+
CommonSpaces.Box3DSpace(;
70+
z_elem = 10,
71+
x_min = 0,
72+
x_max = 1,
73+
y_min = 0,
74+
y_max = 1,
75+
z_min = 0,
76+
z_max = 10,
77+
periodic_x = false,
78+
periodic_y = false,
79+
n_quad_points = 4,
80+
x_elem = 3,
81+
y_elem = 4,
82+
staggering = CommonSpaces.CellCenter(),
83+
),
84+
) == (9, 12, 10)
85+
end
86+
4187
space = SphericalShellSpace()
4288
field = Fields.coordinate_field(space).z
4389

0 commit comments

Comments
 (0)