RingGrids
RingGrids is a submodule that has been developed for SpeedyWeather.jl which is technically independent (SpeedyWeather.jl however imports it and so does SpeedyTransforms) and can also be used without running simulations. It is just not put into its own respective repository.
RingGrids defines several iso-latitude grids, which are mathematically described in the section on Grids. In brief, they include the regular latitude-longitude grids (here called FullClenshawGrid
) as well as grids which latitudes are shifted to the Gaussian latitudes and reduced grids, meaning that they have a decreasing number of longitudinal points towards the poles to be more equal-area than full grids.
Defined grids
RingGrids defines and exports the following grids, see also Implemented grids for a more mathematical description.
Full grids
using SpeedyWeather.RingGrids
subtypes(RingGrids.AbstractFullGrid)
4-element Vector{Any}:
FullClenshawGrid
FullGaussianGrid
FullHEALPixGrid
FullOctaHEALPixGrid
and reduced grids
subtypes(RingGrids.AbstractReducedGrid)
5-element Vector{Any}:
HEALPixGrid
OctaHEALPixGrid
OctahedralClenshawGrid
OctahedralGaussianGrid
OctaminimalGaussianGrid
The following explanation of how to use these can be mostly applied to any of them, however, there are certain functions that are not defined, e.g. the full grids can be trivially converted to a Matrix
(i.e. they are rectangular grids) but not the OctahedralGaussianGrid
.
We use the term ring, short for iso-latitude ring, to refer to a sequence of grid points that all share the same latitude. A latitude-longitude grid is a ring grid, as it organises its grid-points into rings. However, other grids, like the cubed-sphere are not based on iso-latitude rings. SpeedyWeather.jl only works with ring grids because its a requirement for the Spherical Harmonic Transform to be efficient. See Grids.
Grid versus Field
With "grid" we mean the discretization of space. Also called tesselation given that we are tiling a space with polygons, we subdivide the sphere into grid cells, with vertices, faces and centres. In that sense, a grid does not contain any data it purely describes the location of grid cells. Grids in RingGrids are identified by their name, e.g. FullGaussianGrid, and a resolution parameter where we use nlat_half
(the number of latitudes on one hemisphere, Equator included) for all grids. This is because some grids have an even number some an odd number number of latitudes so not all nlat
are valid, but all nlat_half
are. While an instance of a grid stores some precomputed arrays to facilitate faster indexing it does not store coordinates and similar grid information, these can be recomputed on the fly whenever needed given a grid. All grids are considered to be two-dimensional, so they do not contain information about the vertical or time, for example. The horizontal grid points are unravelled into a vector, starting at 0˚E at the north pole, going first east, then ring by ring to the south pole.
Data on a grid is called a Field
, many variables, like temperature are a field. Surface temperature would be a 2D field (though represented as a vector), temperature on several vertical layers of the atmosphere would be 3D (data represented as a matrix, horizontal x vertical), including time would make it 4D. Several fields can share the same grid. Given that the grid is always two-dimensional, a 2D and 3D field can also share the same grid, leaving the 3rd dimension not further specified for flexibility.
Creating a grid
All grids are specified by name and the resolution parameter nlat_half::Integer
(number of latitude rings on one hemisphere, Equator included). An instance of a grid is simply created by
grid = FullGaussianGrid(24)
48-ring FullGaussianGrid
├ nlat_half=24 (4608 points, ~2.99˚, full)
└ architecture: SpeedyWeather.Architectures.CPU{KernelAbstractions.CPU}
As a second argument architecture
can be provided which helps to share information on the computing architecture (CPU/GPU) but this will not be further explained here.
Accessing coordinates
With a grid
you can get the coordinates through get_lat
, get_latd
, get_colat
, get_lond
but note that the latter is only defined for full grids as the reduced grids do not share the same longitudes across rings. To obtain the longitudes and latitudes for all grid points use get_londlatds
, get_lonlats
, get_loncolats
, e.g.
grid = OctaminimalGaussianGrid(2) # a tiny grid with 4 latitudes only
get_latd(grid)
4-element Vector{Float64}:
59.44440828916677
19.87571914744092
-19.875719147440904
-59.44440828916677
Creating a Field
Creating Field
, that means data on a grid can be done in many ways, for example using zeros
, ones,
rand, or
randn`
grid = HEALPixGrid(2) # smallest HEALPix
field = zeros(grid) # 2D field
field = rand(grid, 10) # 3D field with 10 vertical layers or time steps
field = randn(Float32, grid, 2, 2) # 4D using Float32 as element type
12×2×2, 3-ring HEALPixField{Float32, 3} on Array on SpeedyWeather.Architectures.CPU{KernelAbstractions.CPU}:
[:, :, 1] =
0.528445 1.38823
0.902092 -2.28841
0.0137256 -0.169604
0.56375 1.04275
0.24798 0.205433
0.367311 1.54549
-0.277966 0.291509
-0.077809 -0.275442
1.1505 -0.103696
1.708 -0.3625
-1.44639 -0.432482
0.802202 -1.40018
[:, :, 2] =
-1.00012 0.419898
-1.22261 -0.314402
-0.174813 -0.817883
-0.856552 -0.115961
0.820857 -1.51992
-0.6256 -0.0162026
0.00974021 0.881491
-0.39891 0.31353
2.075 0.748379
0.649459 0.881274
-2.35438 -1.12761
0.107548 0.748362
Note that in this case all fields share the same grid
. Or the grid can be created on the fly when the grid type is specified, followed by nlat_half
. Every ?Grid
also has a corresponding ?Field
type, e.g.
field = zeros(OctaminimalGaussianGrid, 2) # nlat_half=2
field = HEALPixField(undef, 2) # using undef initializor
field = HEALPixField{Float16}(undef, 2, 3) # using Float16 as eltype
12×3, 3-ring HEALPixField{Float16, 2} on Array on SpeedyWeather.Architectures.CPU{KernelAbstractions.CPU}:
0.0 0.0 0.0
0.0 0.0 0.0
0.0 0.0 0.0
0.0 0.0 0.0
6.0e-8 0.0 0.0
0.0 0.0 0.0
0.0 0.0 0.0
0.0 0.0 0.0
1.0e-7 0.0 0.0
0.0 0.0 0.0
0.0 0.0 0.0
0.0 0.0 NaN
Creating a Field from data
A field
has field.data
(some AbstractArray{T, N}
) and field.grid
(some AbstractGrid
as described above). The first dimension of data
describes the horizontal as the grid points on every grid (full and reduced) are unravelled west to east then north to south, meaning that it starts at 90˚N and 0˚E then walks eastward for 360˚ before jumping on the next latitude ring further south, this way circling around the sphere till reaching the south pole. This may also be called ring order.
Data in a Matrix
which follows this ring order can be put on a FullGaussianGrid
like so
data = randn(Float32, 8, 4)
field = FullGaussianGrid(data, input_as=Matrix)
field = FullGaussianField(data, input_as=Matrix) # equivalent
32-element, 4-ring FullGaussianField{Float32, 1} on Array on SpeedyWeather.Architectures.CPU{KernelAbstractions.CPU}:
-0.38344693
1.405129
-0.19379693
-2.852835
-0.6202498
2.3144612
0.78139436
0.7354708
-0.6882388
0.3935247
⋮
-0.5921266
-0.37767333
-2.021389
0.10450825
-0.40006062
0.59731764
-1.5947759
0.21457809
-0.040189262
Both return a field (there is no data in the grid itself) and create an instance of FullGaussianGrid
on the fly. The input_as=Matrix
is required to denote that the horizontal dimension is not unravelled into a vector, triggering a reshape
internally, input_as=Vector
is the default. A full Gaussian grid has always $2N$ x $N$ grid points, but a FullClenshawGrid
has $2N$ x $N-1$, if those dimensions don't match, the creation will throw an error.
If you have the data and know which grid it comes one you can also create a Field
by providing both
data = randn(Float32, 8, 4) # data of some shape
grid = OctaminimalGaussianGrid(1) # you need to know the nlat_half (here 1) of that grid!
field = Field(data, grid)
8×4, 2-ring OctaminimalGaussianField{Float32, 2} on Array on SpeedyWeather.Architectures.CPU{KernelAbstractions.CPU}:
-0.266735 -1.75085 -2.41422 -0.822543
-1.95032 -1.59219 0.254568 -1.90568
0.121229 -0.76742 -0.022275 1.12088
-0.544872 -1.00373 1.66832 -0.723818
-0.719442 1.24238 -0.485193 0.724279
-0.394944 1.08514 0.102127 0.0360136
-0.850132 0.338575 0.884069 -0.659707
0.903572 0.715901 0.465761 1.55655
But you can also automatically let nlat_half
be calculated from the shape of the data. Note that you have to provide the name of the field though, FullGaussianField
here to create a Field
on a FullGaussianGrid
.
field = FullGaussianField(data, input_as=Matrix)
32-element, 4-ring FullGaussianField{Float32, 1} on Array on SpeedyWeather.Architectures.CPU{KernelAbstractions.CPU}:
-0.2667355
-1.950322
0.12122941
-0.5448722
-0.71944165
-0.3949442
-0.8501315
0.9035725
-1.7508496
-1.5921855
⋮
0.4657607
-0.82254314
-1.9056796
1.1208777
-0.72381777
0.7242793
0.036013626
-0.65970683
1.5565485
To return to the original data array you can reshape the data of a full grid (which is representable as a matrix) as follows
data == Matrix(FullGaussianField(data, input_as=Matrix))
true
which in general is Array(field, as=Vector)
for no reshaping (equivalent to field.data
including possible conversion to Array
) and Array(field, as=Matrix)
with reshaping (full grids only).
Visualising Fields
As only the full fields can be reshaped into a matrix, the underlying data structure of any AbstractField
is a vector for consistency. As shown in the examples above, one can therefore inspect the data as if it was a vector. But a field has through field.grid
all the geometric information available to plot it on a map, SpeedyWeather also implements extensions for Makie's and UnicodePlots's' heatmap
, also see Visualisation via Makie and Visualisation via UnicodePlots.
using CairoMakie # triggers loading of Makie extension, or do using UnicodePlots instead!
grid = OctahedralGaussianGrid(24)
field = randn(grid)
heatmap(field)

Reduced fields are automatically interpolated to the corresponding full fields so that they can be visualised as a matrix.
Indexing Fields
All fields have a single index ij
which follows the ring order. While this is obviously not super exciting here are some examples how to make better use of the information that the data sits on a two-dimensional grid covering the sphere. We obtain the latitudes of the rings of a grid by calling get_latd
(get_lond
is only defined for full grids, or use get_londlatds
for latitudes, longitudes per grid point not per ring). Now we could calculate Coriolis and add it on the grid as follows
grid = OctahedralClenshawGrid(5) # define a grid
field = randn(grid) # random data on that grid
latd = get_latd(grid) # get a vector of latitudes [˚N] per ring, north to south
rotation = 7.29e-5 # angular frequency of Earth's rotation [rad/s]
coriolis = 2rotation*sind.(latd) # vector of coriolis parameters per latitude ring
for (j, ring) in enumerate(eachring(field)) # loop over every ring j
f = coriolis[j]
for ij in ring # loop over every longitude point on that ring
field[ij] += f
end
end
eachring(field)
accesses field.grid.rings
a precomputed vector of UnitRange
indices, such that we can loop over the ring index j
(j=1
being closest to the North pole) pull the coriolis parameter at that latitude and then loop over all in-ring indices i
(changing longitudes) to do something on the grid. Something similar can be done to scale/unscale with the cosine of latitude for example.
We can always loop over all horizontal grid points with eachgridpoint
and over every other dimensions with eachlayer
, e.g. for 2D fields you can do
for ij in eachgridpoint(grid)
field[ij]
end
or use eachindex
instead. For 3D fields eachindex
loops over all elements, including the 3rd dimension but eachgridpoint
would only loop over the horizontal. To loop with an index k
over all additional dimensions (vertical, time, ...) do
field = zeros(grid, 2, 3) # 4D, 2D defined by grid x 2 x 3
for k in eachlayer(field) # loop over 2 x 3
for ij in eachgridpoint(field) # loop over 2D grid points
field[ij, k]
end
end
Interpolation between grids
In most cases we will want to use RingGrids so that our data directly comes with the geometric information of where the grid-point is one the sphere. We have seen how to use get_latd
, get_lond
, ... for that above. This information generally can also be used to interpolate our data from one grid to another or to request an interpolated value on some coordinates. Using a OctahedralGaussianField
(data on an OctahedralGaussianGrid
) from above we can use the interpolate
function to get it onto a FullGaussianGrid
(or any other grid for purpose)
grid = OctahedralGaussianGrid(4)
field = randn(Float32, grid)
interpolate(FullGaussianGrid, field)
By default this will linearly interpolate (it's an Anvil interpolator, see below) onto a grid with the same nlat_half
, but we can also coarse-grain or fine-grain by specifying any other grid
as an instance not a type (or provide nlat_half
as the second argument), e.g.
output_grid = HEALPixGrid(6)
interpolate(output_grid, field)
interpolate(HEALPixGrid, 6, field) # equivalent
To change the number format during interpolation you can preallocate the output field
output_field = Field(Float16, output_grid)
interpolate!(output_field, field)
Which will convert from Float64
to Float16
on the fly too. Note that we use interpolate!
here not interpolate
without !
as the former writes into output_field
and therefore changes it in place.
Interpolation on coordinates
One can also interpolate a 2D field onto a given coordinate ˚N, ˚E like so
interpolate(30.0, 10.0, field)
0.5234863f0
we interpolated the data from field
onto 30˚N, 10˚E. To do this simultaneously for many coordinates they can be packed into a vector too
interpolate([30.0, 40.0, 50.0], [10.0, 10.0, 10.0], field)
3-element Vector{Float32}:
0.5234863
1.138837
0.7421146
which returns the data on field
at 30˚N, 40˚N, 50˚N, and 10˚E respectively. Note how the interpolation here retains the element type of field
.
Performance for RingGrid interpolation
Every time an interpolation like interpolate(30.0, 10.0, field)
is called, several things happen, which are important to understand to know how to get the fastest interpolation out of this module in a given situation. Under the hood an interpolation takes three arguments
- output array
- input field
- interpolator
The output array is just an array into which the interpolated data is written, providing this prevents unnecessary allocation of memory in case the destination array of the interpolation already exists. Such a destination can be a field but it does not have to be (see Interpolation on coordinates). The input field contains the data which is subject to interpolation, it must come on a ring grid, however, its coordinate information is actually already in the interpolator. The interpolator knows about the geometry of the grid of the field and the coordinates it is supposed to interpolate onto. It has therefore precalculated the indices that are needed to access the right data on the input field and the weights it needs to apply in the actual interpolation operation. The only thing it does not know is the actual data values of that field. So in the case you want to interpolate from field A to field B many times, you can just reuse the same interpolator. If you want to change the coordinates of the output but its total number of points remain constant then you can update the locator inside the interpolator and only else you will need to create a new interpolator. Let's look at this in practice. Say we have two grids and want to interpolate between them
grid_in = HEALPixGrid(4)
grid_out = FullClenshawGrid(6)
interp = RingGrids.interpolator(grid_out, grid_in)
AnvilInterpolator{Float64} for 7-ring HEALPixGrid
├ nlat_half=4 (48 points, ~29.3˚, reduced)
└ architecture: SpeedyWeather.Architectures.CPU{KernelAbstractions.CPU}
└ onto: 264 points
Now we have created an interpolator interp
which knows about the geometry where to interpolate from and the coordinates there to interpolate to. It is also initialized, meaning it has precomputed the indices to of grid_in
that are supposed to be used. It just does not know about the data (it has only seen grids, no fields). We can now do
field_in = rand(grid_in)
field_out = zeros(grid_out)
interpolate!(field_out, field_in, interp)
which is identical to interpolate(field_out, field_in)
but you can reuse interp
for other data. The interpolation can also handle various element types (the interpolator interp
does not have to be updated for this either)
field_out = zeros(Float16, grid_out)
interpolate!(field_out, field_in, interp)
and we have converted data from a HEALPixField{Float64}
(Float64
is always default if not specified) to a FullClenshawField{Float16}
including the type conversion Float64-Float16 on the fly. Technically there are three data types and their combinations possible: The input data will come with a type, the output array has an element type and the interpolator has precomputed weights with a given type. Say we want to go from Float16 data on an OctahedralGaussianGrid
to Float16 on a FullClenshawGrid
but using Float32 precision for the interpolation itself, we would do this by
field_in = randn(OctahedralGaussianField{Float16}, 24)
field_out = zeros(FullClenshawField{Float16}, 24)
interp = RingGrids.interpolator(field_out, field_in, NF=Float32)
interpolate!(field_out, field_in, interp)
As a last example we want to illustrate a situation where we would always want to interpolate onto 10 coordinates, but their locations may change. In order to avoid recreating an interpolator object we would do (AnvilInterpolator
is described in Anvil interpolator)
npoints = 10 # number of coordinates to interpolate onto
grid = HEALPixGrid(24)
interp = AnvilInterpolator(grid, npoints, NF=Float32)
AnvilInterpolator{Float32} for 47-ring HEALPixGrid
├ nlat_half=24 (1728 points, ~4.9˚, reduced)
└ architecture: SpeedyWeather.Architectures.CPU{KernelAbstractions.CPU}
└ onto: 10 points
with the first argument being the input grid and then the number of points to interpolate onto. The number format used for the interpolator is provided as NF
. However, interp
is not yet initialized as it does not know about the destination coordinates yet. Let's define them, but note that we already decided there's only 10 of them above.
londs = collect(-10.0:2.0:8.0)
latds = collect(0.0:5.0:45.0)
now we can update the locator inside our interpolator as follows
RingGrids.update_locator!(interp, londs, latds)
With data matching the input from above, a nlat_half=24
HEALPixGrid, and allocate 10-element output vector
output_vec = zeros(10)
field_in = rand(grid)
we can use the interpolator as follows
interpolate!(output_vec, field_in, interp)
10-element Vector{Float64}:
0.8710519066147959
0.6353694186748435
0.3722537000105374
0.43345751424978096
0.642058202617076
0.5827298430253576
0.1566683023170532
0.8107771689645376
0.3853662807031979
0.2521237526289433
which is the approximately the same as doing it directly without creating an interpolator first and updating its locator
interpolate(londs, latds, field_in)
10-element Vector{Float64}:
0.871051906137655
0.6353694179872488
0.3722537004868922
0.43345751086984674
0.6420582042880308
0.5827298440655148
0.15666830034609613
0.8107771658471706
0.3853662801269159
0.25212375182368274
but allows for a reuse of the interpolator. Note that the two output arrays are not exactly identical because we manually set our interpolator interp
to use Float32
for the interpolation whereas the default is Float64
.
Anvil interpolator
Currently the only interpolator implemented is a 4-point bilinear interpolator, which schematically works as follows. Anvil interpolation is the bilinear average of a, b, c, d which are values at grid points in an anvil-shaped configuration at location x, which is denoted by Δab, Δcd, Δy, the fraction of distances between a-b, c-d, and ab-cd, respectively. Note that a, c and b, d do not necessarily share the same longitude/x-coordinate.
0..............1 # fraction of distance Δab between a, b
|< Δab >|
0^ a -------- o - b # anvil-shaped average of a, b, c, d at location x
.Δy |
. |
.v x
. |
1 c ------ o ---- d
|< Δcd >|
0...............1 # fraction of distance Δcd between c, d
^ fraction of distance Δy between a-b and c-d.
This interpolation is chosen as by definition of the ring grids, a and b share the same latitude, so do c and d, but the longitudes can be different for all four, a, b, c, d.
Function index
Core.Type
— MethodCreate a new grid
of type Grid
with resolution parameter nlat_half
. architecture
is the device type (CPU/GPU). Precomputes the ring indices rings
.
Core.Type
— MethodZero generator function for the 4-point average AnvilLocator. Use update_locator! to update the grid indices used for interpolation and their weights. The number format NF is the format used for the calculations within the interpolation, the input data and/or output data formats may differ.
SpeedyWeather.RingGrids.AbstractField
— TypeAbstract supertype for all fields, i.e. data on a grid. A field is an AbstractArray
with a number format T
, a number of dimensions N
, an ArrayType
(e.g. Vector
, Matrix
, Tensor
) and a grid type Grid
. Fields can be full or reduced, 2D, 3D, or 4D, depending on the grid and the number of dimensions. Fields are used to store data on the grid, e.g. temperature, pressure, or any other variable. Every Field
has data
the grid-free array of the data and grid
which contains information about the grid the field is defined on.
SpeedyWeather.RingGrids.AbstractField2D
— TypeAbstract supertype for all 2D fields, i.e. fields with the horizontal dimensions only. Note that this is a <:AbstractVector
as the horizontal dimensions are unravelled into a vector for all grids to be conistent with the reduced grids that cannot be represented as a matrix.
SpeedyWeather.RingGrids.AbstractField3D
— TypeAbstract supertype for all 3D fields, i.e. fields with horizontal and one vertical (or time etc) dimension.
SpeedyWeather.RingGrids.AbstractField4D
— TypeAbstract supertype for all 4D fields, i.e. fields with horizontal and (in most cases) a vertical and a time dimensions, though these additional dimensions are arbitrary.
SpeedyWeather.RingGrids.AbstractFullField
— TypeAbstract supertype for all fields on full grids, i.e. grids with a constant number of longitude points across latitude rings.
SpeedyWeather.RingGrids.AbstractFullGrid
— TypeAbstract supertype for all full grids, representing a horizontal grid with a constant number of longitude points across latitude rings. Different latitudes can be used, Gaussian latitudes, equi-angle latitudes (also called Clenshaw from Clenshaw-Curtis quadrature), or others.
SpeedyWeather.RingGrids.AbstractGrid
— TypeAbstract supertype for all grids in RingGrids.jl, representing a discretization (particularly a tessellation or tiling) of the sphere. A "grid" does not contain any data only its resolution is defined by nlat_half
(number of latitude rings on one hemisphere including the equator). A grid does not contain the coordinates of the grid points, vertices, latitudes, longtiudes etc but they can be recomputed from the grid object (or its type and resolution) at any time.
A grid is regarded as 2D but a field (data on a grid) can have N additional dimensions, e.g. for vertical levels or time. In that sense, a grid does not have a number format / eltype. This is a property of a Field
which can be different for every field even when using the same grid. Points on the grid (cell centres) are unravalled into a vector ordered 0 to 360˚E, starting at the north pole, then going ring by ring to the south pole. This way both full (those representable as a matrix) and reduced grids (not representable as a matrix, with fewer longitude points towards the poles) can be represented.
A grid has a parameter Architecture
(the type of the field architecture
) that can be used to store information about the architecture the grid is on, e.g. CPU or GPU.
Furthermore all grids have a rings
to allow ring-by-ring indexing. Other precomputed indices can be added for specific grids.
SpeedyWeather.RingGrids.AbstractInterpolator
— Typeabstract type AbstractInterpolator end
Supertype for Interpolators. Every Interpolator <: AbstractInterpolator is expected to have two fields,
- geometry, which describes the grid G to interpolate from
- locator, which locates the indices on G and their weights to interpolate onto a new grid.
NF is the number format used to calculate the interpolation, which can be different from the input data and/or the interpolated data on the new grid.
SpeedyWeather.RingGrids.AbstractLocator
— TypeSupertype of every Locator, which locates the indices on a grid to be used to perform an interpolation. E.g. AnvilLocator uses a 4-point stencil for every new coordinate to interpolate onto. Higher order stencils can be implemented by defining OtherLocator <: AbstractLocactor.
SpeedyWeather.RingGrids.AbstractReducedField
— TypeAbstract supertype for all fields on reduced grids, i.e. grids with a reduced number of longitude points towards the poles.
SpeedyWeather.RingGrids.AbstractReducedGrid
— TypeAbstract supertype for all reduced grids, representing arrays of ring grids that have a reduced number of longitude points towards the poles, i.e. they are not "full", see AbstractFullGrid
. Data on these grids (a Field
) cannot be represented as matrix and has to be unravelled into a vector, ordered 0 to 360˚E then north to south, ring by ring. Examples for reduced grids are the octahedral Gaussian or Clenshaw grids, or the HEALPix grid.
SpeedyWeather.RingGrids.AbstractSphericalDistance
— Typeabstract type AbstractSphericalDistance end
Super type of formulas to calculate the spherical distance or great-circle distance. To define a NewFormula
, define struct NewFormula <: AbstractSphericalDistance end
and the actual calculation as a functor
function NewFormula(lonlat1::Tuple, lonlat2::Tuple; radius=DEFAULT_RADIUS, kwargs...)
assuming inputs in degrees and returning the distance in meters (or radians for radius=1). Then use the general interface spherical_distance(NewFormula, args...; kwargs...)
SpeedyWeather.RingGrids.AnvilLocator
— TypeContains arrays that locates grid points of a given field to be uses in an interpolation and their weights. This Locator is a 4-point average in an anvil-shaped grid-point arrangement between two latitude rings.
SpeedyWeather.RingGrids.Field
— TypeField contains data on a grid. There is only one Field
type which can be used for all grids. data
is the data array, the first dimension is always the horizontal dimension (unravelled into a vector for compatibility across full and reduced grids), the other dimensions can be used for the vertical and/or time or other dimensions. The grid
can be shared across multiple fields, e.g. a 2D and a 3D field can share the same grid which just defines the discretization and the architecture (CPU/GPU) the grid is on.
data::AbstractArray
grid::AbstractGrid
SpeedyWeather.RingGrids.FullClenshawGrid
— TypeA FullClenshawGrid
is a discretization of the sphere, a full grid, subtyping AbstractFullGrid
, using equidistant latitudes for each ring (a regular lon-lat grid). These require the Clenshaw-Curtis quadrature in the spectral transform, hence the name. One ring is on the equator, total number of rings is odd, no rings on the north or south pole.
The first dimension of data on this grid (a Field
) represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions can be used for the vertical and/or time or other dimensions. Note that a Grid
does not contain any data, it only describes the discretization of the space, see Field
for a data on a Grid
. But a "grid" only defines the two horizontal dimensions, two fields, one 2D and one 3D, possibly different ArrayTypes or element types, can share the same grid which just defines the discretization and the architecture (CPU/GPU) the grid is on.
The resolution parameter of the horizontal grid is nlat_half
(number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings
.
nlat_half::Int64
architecture::Any
rings::Any
whichring::Any
SpeedyWeather.RingGrids.FullGaussianGrid
— TypeA FullGaussianGrid
is a discretization of the sphere that uses Gaussian latitudes for each latitude ring. As a "full" grid it has the same number of longitude points on every latitude ring, i.e. it is representable as a matrix, with denser grid points towards the poles. The Gaussian latitudes enable to use the Gaussian quadrature for the spectral transform, hence the name. No ring is on the equator, total number of rings is even and symmetric around the Equator, no rings on the north or south pole.
The first dimension of data on this grid (a Field
) represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions can be used for the vertical and/or time or other dimensions. Note that a Grid
does not contain any data, it only describes the discretization of the space, see Field
for a data on a Grid
. But a "grid" only defines the two horizontal dimensions, two fields, one 2D and one 3D, possibly different ArrayTypes or element types, can share the same grid which just defines the discretization and the architecture (CPU/GPU) the grid is on.
The resolution parameter of the horizontal grid is nlat_half
(number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings
.
nlat_half::Int64
architecture::Any
rings::Any
whichring::Any
SpeedyWeather.RingGrids.FullHEALPixGrid
— TypeA FullHEALPixGrid
is like a HEALPixGrid
but with every latitude ring having the same number of longitude points (a full grid). This grid is mostly defined for output to minimize the interpolation needed from a HEALPixGrid to a full grid. A FullHEALPixGrid
has none of the equal-area properties of the HEALPixGrid
. It only shares the latitudes with the HEALPixGrid
but uses the longitudes from the FullGaussianGrid
without offset, i.e. the first longitude point on every ring is at 0˚E.
nlat_half::Int64
architecture::Any
rings::Any
whichring::Any
SpeedyWeather.RingGrids.FullOctaHEALPixGrid
— TypeA FullOctaHEALPixGrid
is like a OctaHEALPixGrid
but with every latitude ring having the same number of longitude points (a full grid). This grid is mostly defined for output to minimize the interpolation needed from an OctaHEALPixGrid
to a full grid required for output. A FullOctaHEALPixGrid
has none of the equal-area properties of the OctaHEALPixGrid
. It only shares the latitudes with the OctaHEALPixGrid
but uses the longitudes from the FullGaussianGrid
without offset, i.e. the first longitude point on every ring is at 0˚E.
nlat_half::Int64
architecture::Any
rings::Any
whichring::Any
SpeedyWeather.RingGrids.GridGeometry
— TypeContains general precomputed arrays describing the grid.
grid::Any
nlat_half::Int64
nlat::Int64
npoints::Int64
londs::Any
latd::Any
nlons::Any
lon_offsets::Any
SpeedyWeather.RingGrids.GridGeometry
— MethodGridGeometry(
grid::AbstractGrid;
NF,
ArrayType
) -> SpeedyWeather.RingGrids.GridGeometry{<:AbstractGrid{Architecture}, Vector{Float64}, Vector{Int64}} where Architecture
Precomputed arrays describing the geometry of the Grid with resolution nlat_half. Contains longitudes, latitudes of grid points, their ring index j and their unravelled indices ij.
SpeedyWeather.RingGrids.HEALPixGrid
— TypeA HEALPixGrid
is a equal-area discretization of the sphere. A HEALPix grid has 12 faces, each nside
xnside
grid points, each covering the same area of the sphere. They start with 4 longitude points on the northern-most ring, increase by 4 points per ring in the "polar cap" (the top half of the 4 northern-most faces) but have a constant number of longitude points in the equatorial belt. The southern hemisphere is symmetric to the northern, mirrored around the Equator. HEALPix grids have a ring on the Equator. For more details see Górski et al. 2005, DOI:10.1086/427976.
rings
are the precomputed ring indices, for nlat_half = 4 it is rings = [1:4, 5:12, 13:20, 21:28, 29:36, 37:44, 45:48]
. So the first ring has indices 1:4 in the unravelled first dimension, etc. For efficient looping see eachring
and eachgrid
. whichring
is a precomputed vector of ring indices for each grid point ij, i.e. whichring[ij]
gives the ring index j of grid point ij. Fields are
nlat_half::Int64
architecture::Any
rings::Any
whichring::Any
SpeedyWeather.RingGrids.Haversine
— MethodHaversine(lonlat1::Tuple, lonlat2::Tuple; radius) -> Any
Haversine formula calculating the great-circle or spherical distance (in meters) on the sphere between two tuples of longitude-latitude points in degrees ˚E, ˚N. Use keyword argument radius
to change the radius of the sphere (default 6371e3 meters, Earth's radius), use radius=1
to return the central angle in radians or radius=360/2π
to return degrees.
SpeedyWeather.RingGrids.OctaHEALPixGrid
— TypeAn OctaHEALPixGrid
is an equal-area discretization of the sphere. It has 4 faces, each nlat_half x nlat_half
in size, covering 90˚ in longitude, pole to pole. As part of the HEALPix family of grids, the grid points are equal area. They start with 4 longitude points on the northern-most ring, increase by 4 points per ring towards the Equator with one ring on the Equator before reducing the number of points again towards the south pole by 4 per ring. There is no equatorial belt for OctaHEALPix grids. The southern hemisphere is symmetric to the northern, mirrored around the Equator. OctaHEALPix grids have a ring on the Equator. For more details see Górski et al. 2005, DOI:10.1086/427976, the OctaHEALPix grid belongs to the family of HEALPix grids with Nθ = 1, Nφ = 4 but is not explicitly mentioned therein.
rings
are the precomputed ring indices, for nlat_half = 3 (in contrast to HEALPix this can be odd) it is rings = [1:4, 5:12, 13:24, 25:32, 33:36]
. For efficient looping see eachring
and eachgrid
. whichring
is a precomputed vector of ring indices for each grid point ij, i.e. whichring[ij]
gives the ring index j of grid point ij. Fields are
nlat_half::Int64
architecture::Any
rings::Any
whichring::Any
SpeedyWeather.RingGrids.OctahedralClenshawGrid
— TypeAn OctahedralClenshawGrid
is a discretization of the sphere that uses equidistant latitudes for each latitude ring. The spherical harmonic transform on this grid uses the Clenshaw-Curtis quadrature, hence the name. As a reduced grid it has a different number of longitude points on every latitude ring. These grids are called octahedral because after starting with 20 points on the first ring around the north pole they increase the number of longitude points for each ring by 4, such that they can be conceptually thought of as lying on the 4 faces of an octahedron on each hemisphere. Hence, these grids have 20, 24, 28, ... longitude points for ring 1, 2, 3, ... There is a ring on the Equator with 16 + 4nlat_half longitude points before reducing the number of longitude points per ring by 4 towards the southern-most ring j = nlat. The first point on every ring is at longitude 0˚E, i.e. no offset is applied to the longitude values (in contrast to HEALPix grids).
The first dimension of data on this grid (a Field
) represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions can be used for the vertical and/or time or other dimensions. Note that a Grid
does not contain any data, it only describes the discretization of the space, see Field
for a data on a Grid
. But a "grid" only defines the two horizontal dimensions, two fields, one 2D and one 3D, possibly different ArrayTypes or element types, can share the same grid which just defines the discretization and the architecture (CPU/GPU) the grid is on.
The resolution parameter of the horizontal grid is nlat_half
(number of latitude rings on one hemisphere, Equator included) rings
are the precomputed ring indices, the the example above rings = [1:20, 21:44, 45:72, ...]
. whichring
is a precomputed vector of ring indices for each grid point ij, i.e. whichring[ij]
gives the ring index j of grid point ij. For efficient looping see eachring
and eachgrid
. Fields are
nlat_half::Int64
architecture::Any
rings::Any
whichring::Any
SpeedyWeather.RingGrids.OctahedralGaussianGrid
— TypeAn OctahedralGaussianGrid
is a discretization of the sphere that uses Gaussian latitudes for each latitude ring. As a reduced grid it has a different number of longitude points on every latitude ring. These grids are called octahedral because after starting with 20 points on the first ring around the north pole they increase the number of longitude points for each ring by 4, such that they can be conceptually thought of as lying on the 4 faces of an octahedron on each hemisphere. Hence, these grids have 20, 24, 28, ... longitude points for ring 1, 2, 3, ... There is no ring on the Equator and the two rings around it have 16 + 4nlat_half longitude points before reducing the number of longitude points per ring by 4 towards the southern-most ring j = nlat. The first point on every ring is at longitude 0˚E, i.e. no offset is applied to the longitude values (in contrast to HEALPix grids).
The first dimension of data on this grid (a Field
) represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions can be used for the vertical and/or time or other dimensions. Note that a Grid
does not contain any data, it only describes the discretization of the space, see Field
for a data on a Grid
. But a "grid" only defines the two horizontal dimensions, two fields, one 2D and one 3D, possibly different ArrayTypes or element types, can share the same grid which just defines the discretization and the architecture (CPU/GPU) the grid is on.
The resolution parameter of the horizontal grid is nlat_half
(number of latitude rings on one hemisphere, Equator included) rings
are the precomputed ring indices, the the example above rings = [1:20, 21:44, 45:72, ...]
. whichring
is a precomputed vector of ring indices for each grid point ij, i.e. whichring[ij]
gives the ring index j of grid point ij. For efficient looping see eachring
and eachgrid
. Fields are
nlat_half::Int64
architecture::Any
rings::Any
whichring::Any
SpeedyWeather.RingGrids.OctaminimalGaussianGrid
— TypeAn OctaminimalGaussianGrid
is a discretization of the sphere that uses Gaussian latitudes for each latitude ring. It is very similar to the OctahedralGaussianGrid
, with a few differences:
- It starts with 4 longitude points around the poles, so 4, 8, 12, 16, ... on ring 1, 2, 3, 4, ... reducing the number
of total number of grid points over the OctahedralGaussianGrid
, hence the name "octaminimal" (octahedral minimal).
- The first longitude points on every ring have an offset, such that the cell face between the first and last longitude point on every ring is at 0˚E (like the HEALPix grid)
Fields are
nlat_half::Int64
architecture::Any
rings::Any
whichring::Any
SpeedyWeather.RingGrids._scale_lat!
— Method_scale_lat!(
field::AbstractField,
v::AbstractVector
) -> AbstractField
Generic latitude scaling applied to field
in-place with latitude-like vector v
.
SpeedyWeather.RingGrids.anvil_average
— Methodanvil_average(a, b, c, d, Δab, Δcd, Δy) -> Any
The bilinear average of a, b, c, d which are values at grid points in an anvil-shaped configuration at location x, which is denoted by Δab, Δcd, Δy, the fraction of distances between a-b, c-d, and ab-cd, respectively. Note that a, c and b, d do not necessarily share the same longitude/x-coordinate. See schematic:
0..............1 # fraction of distance Δab between a, b
|< Δab >|
0^ a -------- o - b # anvil-shaped average of a, b, c, d at location x
.Δy |
. |
.v x
. |
1 c ------ o ---- d
|< Δcd >|
0...............1 # fraction of distance Δcd between c, d
^ fraction of distance Δy between a-b and c-d.
SpeedyWeather.RingGrids.average_on_poles
— Methodaverage_on_poles(
A::AbstractArray{NF<:AbstractFloat, 1},
rings
) -> Tuple{Any, Any}
Computes the average at the North and South pole from a given grid A
and it's precomputed ring indices rings
. The North pole average is an equally weighted average of all grid points on the northern-most ring. Similar for the South pole.
SpeedyWeather.RingGrids.average_on_poles
— Methodaverage_on_poles(
A::AbstractArray{NF<:Integer, 1},
rings
) -> Tuple{Any, Any}
Method for A::Abstract{T<:Integer}
which rounds the averaged values to return the same number format NF
.
SpeedyWeather.RingGrids.clenshaw_curtis_weights
— Methodclenshaw_curtis_weights(nlat_half::Integer) -> Any
The Clenshaw-Curtis weights for a Clenshaw grid (full or octahedral) of size nlathalf. Clenshaw-Curtis weights are of length nlat
, i.e. a vector for every latitude ring, pole to pole. `sum(clenshawcurtisweights(nlathalf))is always
2` as int0^π sin(x) dx = 2 (colatitudes), or equivalently int-pi/2^pi/2 cos(x) dx (latitudes).
Integration (and therefore the spectral transform) is exact (only rounding errors) when using Clenshaw grids provided that nlat >= 2(T + 1), meaning that a grid resolution of at least 128x64 (nlon x nlat) is sufficient for an exact transform with a T=31 spectral truncation.
SpeedyWeather.RingGrids.each_index_in_ring!
— Methodeach_index_in_ring!(
rings,
Grid::Type{<:OctahedralGaussianGrid},
nlat_half::Integer
)
precompute a Vector{UnitRange{Int}} to index grid points on every ring
j(elements of the vector) of
Gridat resolution
nlat_half. See
eachringand
eachgrid` for efficient looping over grid points.
SpeedyWeather.RingGrids.each_index_in_ring!
— Methodeach_index_in_ring!(
rings,
Grid::Type{<:OctaminimalGaussianGrid},
nlat_half::Integer
)
precompute a Vector{UnitRange{Int}} to index grid points on every ring
j(elements of the vector) of
Gridat resolution
nlat_half. See
eachringand
eachgrid` for efficient looping over grid points.
SpeedyWeather.RingGrids.each_index_in_ring
— Methodeach_index_in_ring(grid::AbstractGrid, j::Integer) -> Any
UnitRange to access data on grid grid
on ring j
.
SpeedyWeather.RingGrids.each_index_in_ring
— Methodeach_index_in_ring(
Grid::Type{<:AbstractFullGrid},
j::Integer,
nlat_half::Integer
) -> Any
UnitRange
for every grid point of full grid Grid
of resolution nlat_half
on ring j
(j=1
is closest ring around north pole, j=nlat
around south pole).
SpeedyWeather.RingGrids.eachgridpoint
— Methodeachgridpoint(
field1::AbstractField,
fields::AbstractField...;
horizontal_only,
kwargs...
) -> Base.OneTo
Iterator over all 2D grid points of a field (or fields), i.e. the horizontal dimension only. Intended use is like
for ij in eachgridpoint(field)
field[ij]
for a 2D field. For a 3D+ field, the iterator will only index and loop over the horizontal grid points. If horizontal_only
is set to true
(default), the function will only check whether the horizontal dimensions match.
SpeedyWeather.RingGrids.eachgridpoint
— Methodeachgridpoint(
grid1::AbstractGrid,
grids::AbstractGrid...
) -> Base.OneTo
Like eachgridpoint(::AbstractGrid)
but checks grids
match.
SpeedyWeather.RingGrids.eachgridpoint
— Methodeachgridpoint(grid::AbstractGrid) -> Base.OneTo
UnitRange to access each horizontal grid point on grid grid
. For a NxM
(N
horizontal grid points, M
vertical layers) OneTo(N)
is returned.
SpeedyWeather.RingGrids.eachlayer
— Methodeachlayer(
field1::AbstractField,
fields::AbstractField...;
vertical_only,
kwargs...
) -> Any
CartesianIndices for the 2nd to last dimension of a field (or fields), e.g. the vertical layer (and possibly a time dimension, etc). E.g. for a Nx2x3 field, with N
horizontal grid points k iterates over (1, 1) to (2, 3), meaning both the 2nd and 3rd dimension. To be used like
for k in eachlayer(field)
for (j, ring) in enumerate(eachring(field))
for ij in ring
field[ij, k]
With vertical_only=true
(default) only checks whether the non-horizontal dimensions of the fields match. E.g. one can loop over two fields of each n layers on different grids.
SpeedyWeather.RingGrids.eachring
— Methodeachring(
field1::AbstractField,
fields::AbstractField...;
horizontal_only,
kwargs...
) -> Any
Return an iterator over the rings of a field. These are precomputed ranges like [1:20, 21:44, ...] stored in field.grid.rings
. Every element are the unravellend indices ij
of all 2D grid points that lie on that ring. Intended use is like
for (j, ring) in enumerate(eachring(field))
for ij in ring
field[ij, k]
with j
being the ring index. j=1 around the north pole, j=nlathalf around the Equator or just north of it (for even nlathalf). Several fields can be passed on to check for matching grids, e.g. for a 2D and a 3D field. If horizontal_only
is set to true
, the function will only check the horizontal dimension.
SpeedyWeather.RingGrids.eachring
— Methodeachring(grid1::AbstractGrid, grids::AbstractGrid...) -> Any
Same as eachring(grid)
but performs a bounds check to assess that all grids
according to grids_match
(non-parametric grid type, nlat_half and length).
SpeedyWeather.RingGrids.eachring
— Methodeachring(grid::AbstractGrid) -> Any
Vector{UnitRange} rings
to loop over every ring of grid
and then each grid point per ring. To be used like
rings = eachring(grid)
for ring in rings
for ij in ring
field[ij]
Accesses precomputed grid.rings
.
SpeedyWeather.RingGrids.eachring
— Methodeachring(
Grid::Type{<:AbstractGrid},
nlat_half::Integer
) -> Any
Computes the ring indices i0:i1
for start and end of every longitudinal point on a given ring j
of Grid
at resolution nlat_half
. Used to loop over rings of a grid. These indices are also precomputed in every grid.rings
.
SpeedyWeather.RingGrids.equal_area_weights
— Methodequal_area_weights(
Grid::Type{<:AbstractGrid},
nlat_half::Integer
) -> Any
The equal-area weights used for the HEALPix grids (original or OctaHEALPix) of size nlathalf. The weights are of length nlat
, i.e. a vector for every latitude ring, pole to pole. `sum(equalareaweights(nlathalf))is always
2` as int0^π sin(x) dx = 2 (colatitudes), or equivalently int-pi/2^pi/2 cos(x) dx (latitudes). Integration (and therefore the spectral transform) is not exact with these grids but errors reduce for higher resolution.
SpeedyWeather.RingGrids.extrema_in
— Methodextrema_in(v::AbstractVector, a::Real, b::Real) -> Any
For every element vᵢ in v does a<=vi<=b hold?
SpeedyWeather.RingGrids.fields_match
— Methodfields_match(
A::AbstractField,
B::AbstractField;
horizontal_only,
vertical_only
) -> Any
True if both A
and B
are of the same nonparametric grid type (e.g. OctahedralGaussianArray, regardless type parameter T
or underyling array type ArrayType
) and of same resolution (nlat_half
) and total grid points (length
). Sizes of (4,)
and (4,1)
would match for example, but (8,1)
and (4,2)
would not (nlat_half
not identical).
SpeedyWeather.RingGrids.gaussian_weights
— Methodgaussian_weights(nlat_half::Integer) -> Any
The Gaussian weights for a Gaussian grid (full or octahedral) of size nlathalf. Gaussian weights are of length nlat
, i.e. a vector for every latitude ring, pole to pole. `sum(gaussianweights(nlathalf))is always
2` as int0^π sin(x) dx = 2 (colatitudes), or equivalently int_-pi/2^pi/2 cos(x) dx (latitudes).
Integration (and therefore the spectral transform) is exact (only rounding errors) when using Gaussian grids provided that nlat >= 3(T + 1)/2, meaning that a grid resolution of at least 96x48 (nlon x nlat) is sufficient for an exact transform with a T=31 spectral truncation.
SpeedyWeather.RingGrids.get_colat
— Methodget_colat(grid::AbstractGrid) -> Any
Colatitudes (radians) for each ring in grid
, north to south.
SpeedyWeather.RingGrids.get_gridcell_polygons
— Methodget_gridcell_polygons(
Grid::Type{<:AbstractGrid},
nlat_half::Integer;
add_nan
) -> Matrix{Tuple{Float64, Float64}}
Return a 5xN matrix polygons
(or grid cells) of NTuple{2, Float64}
where the first 4 rows are the vertices (E, S, W, N) of every grid points ij in 1:N, row 5 is duplicated north vertex to close the grid cell. Use keyword arguemnt add_nan=true
(default false
) to add a 6th row with (NaN, NaN) to separate grid cells when drawing them as a continuous line with vec(polygons)
.
SpeedyWeather.RingGrids.get_lat
— Methodget_lat(grid::AbstractGrid) -> Any
Latitude (radians) for each ring in grid
, north to south.
SpeedyWeather.RingGrids.get_latd
— Methodget_latd(grid::AbstractGrid) -> Any
Latitude (degrees) for each ring in grid
, north to south.
SpeedyWeather.RingGrids.get_latd
— Methodget_latd(_::Type{<:HEALPixGrid}, nlat_half::Integer) -> Any
Latitudes [90˚N to -90˚N] for the HEALPixGrid
with resolution parameter nlat_half
.
SpeedyWeather.RingGrids.get_lon
— Methodget_lon(grid::AbstractGrid) -> Any
Longitude (radians). Full grids only.
SpeedyWeather.RingGrids.get_loncolats
— Methodget_loncolats(grid::AbstractGrid) -> Tuple{Any, Any}
Longitudes (radians, 0-2π), colatitudes (degrees, 0 to π) for every (horizontal) grid point in grid
in ring order (0-360˚E then north to south).
SpeedyWeather.RingGrids.get_lond
— Methodget_lond(grid::AbstractGrid) -> Any
Longitude (degrees). Full grids only.
SpeedyWeather.RingGrids.get_lond_per_ring
— Methodget_lond_per_ring(
Grid::Type{<:HEALPixGrid},
nlat_half::Integer,
j::Integer
) -> Any
Longitudes [0˚E to 360˚E] for the HEALPixGrid
with resolution parameter nlat_half
on ring j
(north to south).
SpeedyWeather.RingGrids.get_londlatds
— Methodget_londlatds(grid::AbstractGrid) -> Tuple{Any, Any}
Longitudes (degrees, 0-360˚E), latitudes (degrees, 90˚N to -90˚N) for every (horizontal) grid point in grid
in ring order (0-360˚E then north to south).
SpeedyWeather.RingGrids.get_londlatds
— Methodget_londlatds(
Grid::Type{<:AbstractReducedGrid},
nlat_half::Integer
) -> Tuple{Any, Any}
Return vectors of londs
, latds
, of longitudes (degrees, 0-360˚E) and latitudes (degrees, -90˚ to 90˚N) for reduced grids for all grid points in order of the running index ij
.
SpeedyWeather.RingGrids.get_lonlats
— Methodget_lonlats(grid::AbstractGrid) -> Tuple{Any, Any}
Longitudes (radians, 0-2π), latitudes (degrees, π/2 to -π/2) for every (horizontal) grid point in grid
in ring order (0-360˚E then north to south).
SpeedyWeather.RingGrids.get_nlat
— Methodget_nlat(grid::AbstractGrid) -> Any
Get number of latitude rings, pole to pole.
SpeedyWeather.RingGrids.get_nlat_half
— Methodget_nlat_half(grid::AbstractGrid) -> Any
Resolution paraemeters nlat_half
of a grid
. Number of latitude rings on one hemisphere, Equator included.
SpeedyWeather.RingGrids.get_nlon_per_ring
— Methodget_nlon_per_ring(grid::AbstractGrid, j::Integer) -> Any
Number of longitude points per latitude ring j
.
SpeedyWeather.RingGrids.get_nlon_per_ring
— Methodget_nlon_per_ring(
Grid::Type{<:HEALPixGrid},
nlat_half::Integer,
j::Integer
) -> Any
Number of longitude points for ring j
on Grid
of resolution nlat_half
.
SpeedyWeather.RingGrids.get_nlons
— Methodget_nlons(
Grid::Type{<:AbstractGrid},
nlat_half::Integer;
both_hemispheres
) -> Any
Returns a vector nlons
for the number of longitude points per latitude ring, north to south. Provide grid Grid
and its resolution parameter nlat_half
. For keyword argument both_hemispheres=false
only the northern hemisphere (incl Equator) is returned.
SpeedyWeather.RingGrids.get_npoints
— Methodget_npoints(grid::AbstractGrid, args...) -> Any
Total number of grid points in all dimensions of grid
. Equivalent to length of the underlying data array.
SpeedyWeather.RingGrids.get_vertices
— Methodget_vertices(
Grid::Type{<:AbstractFullGrid},
nlat_half::Integer
) -> NTuple{4, Any}
Vertices for full grids, other definition than for reduced grids to prevent a diamond shape of the cells. Use default rectangular instead. Effectively rotating the vertices clockwise by 45˚, making east south-east etc.
SpeedyWeather.RingGrids.get_vertices
— Methodget_vertices(
Grid::Type{<:AbstractGrid},
nlat_half::Integer
) -> NTuple{4, Any}
Vertices are defined for every grid point on a ring grid through 4 points: east, south, west, north.
- east: longitude mid-point with the next grid point east
- south: longitude mid-point between the two closest grid points on one ring to the south
- west: longitude mid-point with the next grid point west
- north: longitude mid-point between the two closest grid points on one ring to the north
Example
o ----- n ------ o
o --- w --- c --- e --- o
o ----- s ------ o
with cell center c (the grid point), e, s, w, n the vertices and o the surrounding grid points. Returns 2xnpoints arrays for east, south, west, north each containing the longitude and latitude of the vertices.
SpeedyWeather.RingGrids.grid_cell_average!
— Methodgrid_cell_average!(
output::AbstractField,
input::AbstractField{T, N, ArrayType, Grid} where {T, N, ArrayType, Grid<:AbstractFullGrid}
) -> AbstractField
Averages all grid points in input
that are within one grid cell of output
with coslat-weighting. The output grid cell boundaries are assumed to be rectangles spanning half way to adjacent longitude and latitude points.
SpeedyWeather.RingGrids.grid_cell_average
— Methodgrid_cell_average(
Grid::Type{<:AbstractGrid},
nlat_half::Integer,
input::AbstractFullGrid
)
Averages all grid points in input
that are within one grid cell of output
with coslat-weighting. The output grid cell boundaries are assumed to be rectangles spanning half way to adjacent longitude and latitude points.
SpeedyWeather.RingGrids.grids_match
— Methodgrids_match(A::AbstractGrid, B::AbstractGrid) -> Any
True if both A
and B
are of the same nonparametric grid type (e.g. OctahedralGaussianArray, regardless type parameter T
or underyling array type ArrayType
) and of same resolution (nlat_half
) and total grid points (length
). Sizes of (4,)
and (4,1)
would match for example, but (8,1)
and (4,2)
would not (nlat_half
not identical).
SpeedyWeather.RingGrids.grids_match
— Methodgrids_match(A::AbstractGrid, Bs::AbstractGrid...) -> Any
True if all grids A, B, C, ...
provided as arguments match according to grids_match
wrt to A
(and therefore all).
SpeedyWeather.RingGrids.grids_match
— Methodgrids_match(
A::Type{<:AbstractGrid},
B::Type{<:AbstractGrid}
) -> Any
True if both A
and B
are of the same nonparametric grid type.
SpeedyWeather.RingGrids.isdecreasing
— Methodisdecreasing(x::AbstractVector) -> Bool
Check whether elements of a vector v
are strictly decreasing.
SpeedyWeather.RingGrids.isincreasing
— Methodisincreasing(x::AbstractVector) -> Bool
Check whether elements of a vector v
are strictly increasing.
SpeedyWeather.RingGrids.matrix_size
— Methodmatrix_size(grid::AbstractGrid) -> Tuple{Int64, Int64}
Size of the matrix of the horizontal grid if representable as such (not all grids).
SpeedyWeather.RingGrids.nlat_odd
— Methodnlat_odd(grid::AbstractGrid) -> Any
True for a grid
that has an odd number of latitude rings nlat
(both hemispheres).
SpeedyWeather.RingGrids.nonparametric_type
— Methodnonparametric_type(grid::AbstractGrid) -> Any
For any instance of AbstractGrid
type its n-dimensional type (*Grid{T, N, ...} returns *Array) but without any parameters {T, N, ArrayType}
SpeedyWeather.RingGrids.nside_healpix
— Methodnside_healpix(nlat_half::Integer) -> Any
The original Nside
resolution parameter of the HEALPix grids. The number of grid points on one side of each (square) face. While we use nlat_half
across all ring grids, this function translates this to Nside. Even nlat_half
only.
SpeedyWeather.RingGrids.rotate_matrix_indices_180
— Methodrotate_matrix_indices_180(
i::Integer,
j::Integer,
s::Integer
) -> Tuple{Any, Any}
Rotate indices i, j
of a square matrix of size s x s by 180˚.
SpeedyWeather.RingGrids.rotate_matrix_indices_270
— Methodrotate_matrix_indices_270(
i::Integer,
j::Integer,
s::Integer
) -> Tuple{Integer, Any}
Rotate indices i, j
of a square matrix of size s x s anti-clockwise by 270˚.
SpeedyWeather.RingGrids.rotate_matrix_indices_90
— Methodrotate_matrix_indices_90(
i::Integer,
j::Integer,
s::Integer
) -> Tuple{Any, Integer}
Rotate indices i, j
of a square matrix of size s x s anti-clockwise by 90˚.
SpeedyWeather.RingGrids.spherical_distance
— Methodspherical_distance(
Formula::Type{<:SpeedyWeather.RingGrids.AbstractSphericalDistance},
args...;
kwargs...
) -> Any
Spherical distance, or great-circle distance, between two points lonlat1
and lonlat2
using the Formula
(default Haversine
).
SpeedyWeather.RingGrids.whichring
— Methodwhichring(ij::Integer, rings) -> Int64
Obtain ring index j
from gridpoint ij
and rings
describing rind indices as obtained from eachring(::Grid)
SpeedyWeather.RingGrids.whichring
— Methodwhichring(
Grid::Type{<:AbstractGrid},
nlat_half,
rings
) -> Any
Vector of ring indices for every grid point in grid
.
SpeedyWeather.RingGrids.zonal_mean
— Methodzonal_mean(field::AbstractField) -> Any
Zonal mean of grid
, i.e. along its latitude rings.