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.

RingGrids defines and exports the following grids:

  • full grids: FullClenshawGrid, FullGaussianGrid, FullHEALPix, and FullOctaHEALPix
  • reduced grids: OctahedralGaussianGrid, OctahedralClenshawGrid, OctaHEALPixGrid and HEALPixGrid

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.

What is a ring?

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.

Creating data on a RingGrid

Every grid in RingGrids has a grid.data field, which is a vector containing the data on the grid. The grid points 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

using SpeedyWeather.RingGrids
map = randn(Float32, 8, 4)
8×4 Matrix{Float32}:
 -0.307356  -0.628441   0.853159  -0.437502
  0.289618  -0.594053   1.04397   -0.958282
 -1.57626    0.38211    0.265095   0.12992
  0.160524   0.229977  -0.89644    0.91226
 -0.78713   -1.05648   -0.231185  -0.57567
  0.773203   1.01776    1.22453    0.0500266
  0.399984  -0.20891   -0.265136   0.440208
  0.528773   0.594486  -0.134071  -1.31572
grid = FullGaussianGrid(map)
32-element, 4-ring FullGaussianGrid{Float32}:
 -0.3073562
  0.2896176
 -1.5762601
  0.16052403
 -0.7871302
  0.77320266
  0.39998412
  0.5287729
 -0.62844104
 -0.59405327
  ⋮
 -0.1340715
 -0.4375023
 -0.95828205
  0.12992017
  0.9122595
 -0.5756705
  0.050026633
  0.440208
 -1.3157198

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. To reobtain the data from a grid, you can access its data field which returns a normal Vector

grid.data
32-element Vector{Float32}:
 -0.3073562
  0.2896176
 -1.5762601
  0.16052403
 -0.7871302
  0.77320266
  0.39998412
  0.5287729
 -0.62844104
 -0.59405327
  ⋮
 -0.1340715
 -0.4375023
 -0.95828205
  0.12992017
  0.9122595
 -0.5756705
  0.050026633
  0.440208
 -1.3157198

Which can be reshaped to reobtain map from above. Alternatively you can Matrix(grid) to do this in one step

map == Matrix(FullGaussianGrid(map))
true

You can also use zeros, ones, rand, randn to create a grid, whereby nlat_half, i.e. the number of latitude rings on one hemisphere, Equator included, is used as a resolution parameter and here as a second argument.

nlat_half = 4
grid = randn(OctahedralGaussianGrid{Float16}, nlat_half)
208-element, 8-ring OctahedralGaussianGrid{Float16}:
 -0.754
  1.703
  1.31
  0.721
 -0.3057
  0.9336
  0.7324
 -0.556
  1.859
 -0.3723
  ⋮
  0.1691
 -0.1692
 -1.252
 -0.297
  0.1462
 -0.8193
 -0.7314
 -0.1678
  0.7573

and any element type T can be used for OctahedralGaussianGrid{T} and similar for other grid types.

Visualising RingGrid data

As only the full grids can be reshaped into a matrix, the underlying data structure of any AbstractGrid is a vector. As shown in the examples above, one can therefore inspect the data as if it was a vector. But as that data has, through its <:AbstractGrid type, all the geometric information available to plot it on a map, RingGrids also exports plot function, based on UnicodePlots' heatmap.

nlat_half = 24
grid = randn(OctahedralGaussianGrid, nlat_half)
RingGrids.plot(grid)
                   48-ring OctahedralGaussianGrid{Float64}                
       ┌────────────────────────────────────────────────────────────┐  3  
    90 ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ┌──┐
       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
       ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
       ▄▄▄▄▄▄▄ ▄▄
       ▄▄▄▄▄▄ ▄▄
       ▄▄ ▄▄
    ˚N ▄▄▄▄ ▄▄
       ▄▄▄▄▄▄▄▄▄▄▄ ▄▄
       ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
       ▄▄ ▄▄
       ▄▄▄▄▄▄▄▄▄ ▄▄
       ▄▄▄ ▄▄
       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
   -90 ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ └──┘
       └────────────────────────────────────────────────────────────┘ -4  
        0                           ˚E                           360      

(Note that to skip the RingGrids. in the last line you can do import SpeedyWeather.RingGrids: plot, import SpeedyWeather: plot or simply using SpeedyWeather. It's just that LowerTriangularMatrices also defines plot which otherwise causes naming conflicts.)

Indexing RingGrids

All RingGrids 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 grid.

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_latdlonds for latitudes, longitudes per grid point not per ring)

grid = randn(OctahedralClenshawGrid, 5)
latd = get_latd(grid)
9-element Vector{Float64}:
  72.0
  54.0
  36.0
  18.0
   0.0
 -18.0
 -36.0
 -54.0
 -72.0

Now we could calculate Coriolis and add it on the grid as follows

rotation = 7.29e-5                  # angular frequency of Earth's rotation [rad/s]
coriolis = 2rotation*sind.(latd)    # vector of coriolis parameters per latitude ring

rings = eachring(grid)
for (j, ring) in enumerate(rings)
    f = coriolis[j]
    for ij in ring
        grid[ij] += f
    end
end

eachring creates a 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 grid-points like so

for ij in eachgridpoint(grid)
    grid[ij]
end

or use eachindex instead.

Interpolation on RingGrids

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 grid to another or to request an interpolated value on some coordinates. Using our data on grid which is an OctahedralGaussianGrid from above we can use the interpolate function to get it onto a FullGaussianGrid (or any other grid for purpose)

grid = randn(OctahedralGaussianGrid{Float32}, 4)
208-element, 8-ring OctahedralGaussianGrid{Float32}:
  1.174728
 -0.7534969
 -0.7753278
  0.511157
 -0.71460944
  0.25120017
 -2.3406804
 -0.35716468
 -0.772278
  0.5819953
  ⋮
 -0.6395948
  0.5476171
  0.33001605
 -1.0630392
 -0.34425852
 -0.53595555
  0.89390457
  0.6355276
 -0.07796951
interpolate(FullGaussianGrid, grid)
128-element, 8-ring FullGaussianGrid{Float64}:
  1.1747280359268188
 -0.7589546144008636
 -0.13208544254302979
 -0.40816783905029297
  0.2512001693248749
 -1.844801425933838
 -0.5647213459014893
  0.24342697858810425
 -0.665973424911499
  0.5572797283530235
  ⋮
 -1.4168056845664978
  0.2053186297416687
 -0.3427918255329132
  0.43881656229496
 -0.7147753536701178
 -0.34425851702690125
 -0.1784905195236206
  0.7647160887718201
  0.1004047840833664

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 nlat_half directly as 2nd argument

interpolate(FullGaussianGrid, 6, grid)
288-element, 12-ring FullGaussianGrid{Float64}:
  0.741270366153732
 -0.35173068669866425
 -0.580230661286266
 -0.14763952737095445
  0.011974618545789517
 -0.43438669676182423
  0.11307585248109409
 -1.3561138409389228
 -0.7504779406310863
 -0.44192355575396736
  ⋮
  0.19407614292362302
 -0.19578875599080506
 -0.7460166207918405
 -0.3385807334018945
 -0.4472428789269502
  0.17942980030512967
  0.41575683274899655
  0.1661049382800165
 -0.28195445528070845

So we got from an 8-ring OctahedralGaussianGrid{Float16} to a 12-ring FullGaussianGrid{Float64}, so it did a conversion from Float16 to Float64 on the fly too, because the default precision is Float64 unless specified. interpolate(FullGaussianGrid{Float16}, 6, grid) would have interpolated onto a grid with element type Float16.

One can also interpolate onto a given coordinate ˚N, ˚E like so

interpolate(30.0, 10.0, grid)
-0.0038768826f0

we interpolated the data from grid 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], grid)
3-element Vector{Float32}:
 -0.0038768826
 -0.43135267
 -0.84990984

which returns the data on grid at 30˚N, 40˚N, 50˚N, and 10˚E respectively. Note how the interpolation here retains the element type of grid.

Performance for RingGrid interpolation

Every time an interpolation like interpolate(30.0, 10.0, grid) 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 vector
  • input grid
  • interpolator

The output vector 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. The input grid 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 the data is coming on 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 grid 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 grid. So in the case you want to interpolate from grid A to grid B many times, you can just reuse the same interpolator. If you want to change the coordinates of the output grid but its total number of points remain constants 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 an want to interpolate between them

grid_in = rand(HEALPixGrid, 4)
grid_out = zeros(FullClenshawGrid, 6)
interp = RingGrids.interpolator(grid_out, grid_in)
SpeedyWeather.RingGrids.AnvilInterpolator{Float64, HEALPixGrid{Float64}}
├ from: 7-ring HEALPixGrid{Float64}, 48 grid points
└ 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 of grid_in (and neither of grid_out which will be overwritten anyway). We can now do

interpolate!(grid_out, grid_in, interp)
grid_out
264-element, 11-ring FullClenshawGrid{Float64}:
 0.46284974997461376
 0.4442563826640538
 0.4256630153534939
 0.40706964804293394
 0.40833562410066215
 0.40960160015839037
 0.41086757621611864
 0.41213355227384685
 0.41339952833157506
 0.4146655043893033
 ⋮
 0.6860974896180504
 0.6969568523865083
 0.707816215154966
 0.7186755779234237
 0.7295349406918815
 0.7403943034603393
 0.7512536662287971
 0.685963074113578
 0.6206724819983591

which is identical to interpolate(grid_out, grid_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)

grid_out = zeros(FullClenshawGrid{Float16}, 6);
interpolate!(grid_out, grid_in, interp)
grid_out
264-element, 11-ring FullClenshawGrid{Float16}:
 0.463
 0.4443
 0.4258
 0.407
 0.4084
 0.4097
 0.411
 0.412
 0.4133
 0.4146
 ⋮
 0.686
 0.697
 0.708
 0.7188
 0.7295
 0.74
 0.7515
 0.686
 0.6206

and we have converted data from a HEALPixGrid{Float64} (Float64 is always default if not specified) to a FullClenshawGrid{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

grid_in = randn(OctahedralGaussianGrid{Float16}, 24)
grid_out = zeros(FullClenshawGrid{Float16}, 24)
interp = RingGrids.interpolator(Float32, grid_out, grid_in)
interpolate!(grid_out, grid_in, interp)
grid_out
4512-element, 47-ring FullClenshawGrid{Float16}:
 -0.8145
 -0.4185
 -0.02245
  0.3735
  0.7695
  1.099
  0.943
  0.7876
  0.6323
  0.4263
  ⋮
  0.2966
  0.1432
 -0.085
 -0.3132
 -0.5415
 -0.3872
 -0.11224
  0.1627
  0.4377

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
interp = AnvilInterpolator(Float32, HEALPixGrid, 24, npoints)
SpeedyWeather.RingGrids.AnvilInterpolator{Float32, HEALPixGrid}
├ from: 47-ring HEALPixGrid, 1728 grid points
└ onto: 10 points

with the first argument being the number format used during interpolation, then the input grid type, its resolution in terms of nlat_half and then the number of points to interpolate onto. 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.

latds = collect(0.0:5.0:45.0)
londs = collect(-10.0:2.0:8.0)

now we can update the locator inside our interpolator as follows

RingGrids.update_locator!(interp, latds, londs)

With data matching the input from above, a nlat_half=24 HEALPixGrid, and allocate 10-element output vector

output_vec = zeros(10)
grid_input = rand(HEALPixGrid, 24)

we can use the interpolator as follows

interpolate!(output_vec, grid_input, interp)
10-element Vector{Float64}:
 0.8175336880364958
 0.7243524067839728
 0.2655473345163441
 0.7351622407933004
 0.26297607257556316
 0.5262469718599543
 0.22056524992733012
 0.7377310165917967
 0.6184467595284137
 0.3279944905545917

which is the approximately the same as doing it directly without creating an interpolator first and updating its locator

interpolate(latds, londs, grid_input)
10-element Vector{Float64}:
 0.8175336896678589
 0.724352408208411
 0.2655473263347406
 0.7351622485374165
 0.2629760720289777
 0.5262469693654939
 0.22056524058069882
 0.7377310079071613
 0.6184467706761949
 0.32799449039797923

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

SpeedyWeather.RingGrids.AbstractFullGridType
abstract type AbstractFullGrid{T} <: AbstractGrid{T} end

An AbstractFullGrid is a horizontal grid with a constant number of longitude points across latitude rings. Different latitudes can be used, Gaussian latitudes, equi-angle latitdes, or others.

source
SpeedyWeather.RingGrids.AbstractGridType
abstract type AbstractGrid{T} <: AbstractVector{T} end

The abstract supertype for all spatial grids on the sphere supported by SpeedyWeather.jl. Every new grid has to be of the form

abstract type AbstractGridClass{T} <: AbstractGrid{T} end
struct MyNewGrid{T} <: AbstractGridClass{T}
    data::Vector{T}     # all grid points unravelled into a vector
    nlat_half::Int      # resolution: latitude rings on one hemisphere (Equator incl)
end

MyNewGrid should belong to a grid class like AbstractFullGrid, AbstractOctahedralGrid or AbstractHEALPixGrid (that already exist but you may introduce a new class of grids) that share certain features such as the number of longitude points per latitude ring and indexing, but may have different latitudes or offset rotations. Each new grid Grid (or grid class) then has to implement the following methods (as an example, see octahedral.jl)

Fundamental grid properties getnpoints # total number of grid points nlatodd # does the grid have an odd number of latitude rings? getnlat # total number of latitude rings getnlat_half # number of latitude rings on one hemisphere incl Equator

Indexing getnlonmax # maximum number of longitudes points (at the Equator) getnlonperring # number of longitudes on ring j eachindexinring # a unit range that indexes all longitude points on a ring

Coordinates getcolat # vector of colatitudes (radians) getcolatlon # vectors of colatitudes, longitudes (both radians)

Spectral truncation truncationorder # linear, quadratic, cubic = 1, 2, 3 for grid gettruncation # spectral truncation given a grid resolution get_resolution # grid resolution given a spectral truncation

Quadrature weights and solid angles getquadratureweights # = sinθ Δθ for grid points on ring j for meridional integration getsolidangle # = sinθ Δθ Δϕ, solid angle of grid points on ring j

source
SpeedyWeather.RingGrids.AbstractHEALPixGridType
abstract type AbstractHEALPixGrid{T} <: AbstractGrid{T} end

An AbstractHEALPixGrid is a horizontal grid similar to the standard HEALPixGrid, but different latitudes can be used, the default HEALPix latitudes or others.

source
SpeedyWeather.RingGrids.AbstractInterpolatorType
abstract type AbstractInterpolator{NF, G} 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.

source
SpeedyWeather.RingGrids.AbstractLocatorType
AbstractLocator{NF}

Supertype 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.

source
SpeedyWeather.RingGrids.AbstractOctaHEALPixGridType
abstract type AbstractOctaHEALPixGrid{T} <: AbstractGrid{T} end

An AbstractOctaHEALPixGrid is a horizontal grid similar to the standard OctahedralGrid, but the number of points in the ring closest to the Poles starts from 4 instead of 20, and the longitude of the first point in each ring is shifted as in HEALPixGrid. Also, different latitudes can be used.

source
SpeedyWeather.RingGrids.AbstractOctahedralGridType
abstract type AbstractOctahedralGrid{T} <: AbstractGrid{T} end

An AbstractOctahedralGrid is a horizontal grid with 16+4i longitude points on the latitude ring i starting with i=1 around the pole. Different latitudes can be used, Gaussian latitudes, equi-angle latitdes, or others.

source
SpeedyWeather.RingGrids.AnvilLocatorType
AnvilLocator{NF<:AbstractFloat} <: AbtractLocator

Contains 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.

source
SpeedyWeather.RingGrids.AnvilLocatorMethod
L = AnvilLocator(   ::Type{NF},         # number format used for the interpolation
                    npoints::Integer    # number of points to interpolate onto
                    ) where {NF<:AbstractFloat}

Zero 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.

source
SpeedyWeather.RingGrids.FullClenshawGridType
G = FullClenshawGrid{T}

A FullClenshawGrid is a regular latitude-longitude grid with an odd number of nlat equi-spaced latitudes, the central latitude ring is on the Equator. The same nlon longitudes for every latitude ring. The grid points are closer in zonal direction around the poles. The values of all grid points are stored in a vector field data that unravels the data 0 to 360˚, then ring by ring, which are sorted north to south.

source
SpeedyWeather.RingGrids.FullGaussianGridType
G = FullGaussianGrid{T}

A full Gaussian grid is a regular latitude-longitude grid that uses nlat Gaussian latitudes, and the same nlon longitudes for every latitude ring. The grid points are closer in zonal direction around the poles. The values of all grid points are stored in a vector field v that unravels the data 0 to 360˚, then ring by ring, which are sorted north to south.

source
SpeedyWeather.RingGrids.FullHEALPixGridType
G = FullHEALPixGrid{T}

A full HEALPix grid is a regular latitude-longitude grid that uses nlat latitudes from the HEALPix grid, and the same nlon longitudes for every latitude ring. The grid points are closer in zonal direction around the poles. The values of all grid points are stored in a vector field v that unravels the data 0 to 360˚, then ring by ring, which are sorted north to south.

source
SpeedyWeather.RingGrids.FullOctaHEALPixGridType
G = FullOctaHEALPixGrid{T}

A full OctaHEALPix grid is a regular latitude-longitude grid that uses nlat OctaHEALPix latitudes, and the same nlon longitudes for every latitude ring. The grid points are closer in zonal direction around the poles. The values of all grid points are stored in a vector field v that unravels the data 0 to 360˚, then ring by ring, which are sorted north to south.

source
SpeedyWeather.RingGrids.GridGeometryMethod
G = GridGeometry(   Grid::Type{<:AbstractGrid},
                    nlat_half::Integer)

Precomputed arrays describing the geometry of the Grid with resolution nlat_half. Contains latitudes and longitudes of grid points, their ring index j and their unravelled indices ij.

source
SpeedyWeather.RingGrids.HEALPixGridType
H = HEALPixGrid{T}

A HEALPix grid with 12 faces, each nsidexnside grid points, each covering the same area. The number of latitude rings on one hemisphere (incl Equator) nlat_half is used as resolution parameter. The values of all grid points are stored in a vector field v that unravels the data 0 to 360˚, then ring by ring, which are sorted north to south.

source
SpeedyWeather.RingGrids.OctaHEALPixGridType
H = OctaHEALPixGrid{T}

A OctaHEALPix grid with 4 base faces, each nlat_halfxnlat_half grid points, each covering the same area. The values of all grid points are stored in a vector field data that unravels the data 0 to 360˚, then ring by ring, which are sorted north to south.

source
SpeedyWeather.RingGrids.OctahedralClenshawGridType
G = OctahedralClenshawGrid{T}

An Octahedral Clenshaw grid that uses nlat equi-spaced latitudes. Like FullClenshawGrid, the central latitude ring is on the Equator. Like OctahedralGaussianGrid, the number of longitude points per latitude ring decreases towards the poles. Starting with 20 equi-spaced longitude points (starting at 0˚E) on the rings around the poles, each latitude ring towards the equator has consecuitively 4 more points, one for each face of the octahedron. E.g. 20, 24, 28, 32, ...nlon-4, nlon, nlon, nlon-4, ..., 32, 28, 24, 20. The maximum number of longitue points is nlon. The values of all grid points are stored in a vector field v that unravels the data 0 to 360˚, then ring by ring, which are sorted north to south.

source
SpeedyWeather.RingGrids.OctahedralGaussianGridType
G = OctahedralGaussianGrid{T}

An Octahedral Gaussian grid that uses nlat Gaussian latitudes, but a decreasing number of longitude points per latitude ring towards the poles. Starting with 20 equi-spaced longitude points (starting at 0˚E) on the rings around the poles, each latitude ring towards the equator has consecuitively 4 more points, one for each face of the octahedron. E.g. 20, 24, 28, 32, ...nlon-4, nlon, nlon, nlon-4, ..., 32, 28, 24, 20. The maximum number of longitue points is nlon. The values of all grid points are stored in a vector field v that unravels the data 0 to 360˚, then ring by ring, which are sorted north to south.

source
SpeedyWeather.RingGrids.Matrix!Method
Matrix!(M::AbstractMatrix,
        G::OctaHEALPixGrid;
        quadrant_rotation=(0, 1, 2, 3),
        matrix_quadrant=((2, 2), (1, 2), (1, 1), (2, 1)),
        )

Sorts the gridpoints in G into the matrix M without interpolation. Every quadrant of the grid G is rotated as specified in quadrant_rotation, 0 is no rotation, 1 is 90˚ clockwise, 2 is 180˚ etc. Grid quadrants are counted eastward starting from 0˚E. The grid quadrants are moved into the matrix quadrant (i, j) as specified. Defaults are equivalent to centered at 0˚E and a rotation such that the North Pole is at M's midpoint.

source
SpeedyWeather.RingGrids.Matrix!Method
Matrix!(M::AbstractMatrix,
        G::OctahedralClenshawGrid;
        quadrant_rotation=(0, 1, 2, 3),
        matrix_quadrant=((2, 2), (1, 2), (1, 1), (2, 1)),
        )

Sorts the gridpoints in G into the matrix M without interpolation. Every quadrant of the grid G is rotated as specified in quadrant_rotation, 0 is no rotation, 1 is 90˚ clockwise, 2 is 180˚ etc. Grid quadrants are counted eastward starting from 0˚E. The grid quadrants are moved into the matrix quadrant (i, j) as specified. Defaults are equivalent to centered at 0˚E and a rotation such that the North Pole is at M's midpoint.

source
SpeedyWeather.RingGrids.Matrix!Method
Matrix!(MGs::Tuple{AbstractMatrix{T}, OctaHEALPixGrid}...; kwargs...)

Like Matrix!(::AbstractMatrix, ::OctaHEALPixGrid) but for simultaneous processing of tuples ((M1, G1), (M2, G2), ...) with matrices Mi and grids Gi. All matrices and grids have to be of the same size respectively.

source
SpeedyWeather.RingGrids.Matrix!Method
Matrix!(MGs::Tuple{AbstractMatrix{T}, OctahedralClenshawGrid}...; kwargs...)

Like Matrix!(::AbstractMatrix, ::OctahedralClenshawGrid) but for simultaneous processing of tuples ((M1, G1), (M2, G2), ...) with matrices Mi and grids Gi. All matrices and grids have to be of the same size respectively.

source
SpeedyWeather.RingGrids._scale_lat!Method
_scale_lat!(
    A::SpeedyWeather.RingGrids.AbstractGrid{NF},
    v::AbstractVector
) -> SpeedyWeather.RingGrids.AbstractGrid

Generic latitude scaling applied to A in-place with latitude-like vector v.

source
SpeedyWeather.RingGrids.anvil_averageMethod
anvil_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.

source
SpeedyWeather.RingGrids.average_on_polesMethod
average_on_poles(
    A::SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},
    rings::Vector{<:UnitRange{<:Integer}}
) -> 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.

source
SpeedyWeather.RingGrids.average_on_polesMethod
average_on_poles(
    A::SpeedyWeather.RingGrids.AbstractGrid{NF<:Integer},
    rings::Vector{<:UnitRange{<:Integer}}
) -> Tuple{Any, Any}

Method for A::Abstract{T<:Integer} which rounds the averaged values to return the same number format NF.

source
SpeedyWeather.RingGrids.eachringMethod
eachring(grid::SpeedyWeather.RingGrids.AbstractGrid) -> Any

Vector{UnitRange} rings to loop over every ring of grid grid and then each grid point per ring. To be used like

rings = eachring(grid)
for ring in rings
    for ij in ring
        grid[ij]
source
SpeedyWeather.RingGrids.eachringMethod
eachring(
    grid1::SpeedyWeather.RingGrids.AbstractGrid,
    grids::Grid<:SpeedyWeather.RingGrids.AbstractGrid...
) -> Any

Same as eachring(grid) but performs a bounds check to assess that all grids in grids are of same size.

source
SpeedyWeather.RingGrids.get_nlonsMethod
get_nlons(
    Grid::Type{<:SpeedyWeather.RingGrids.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 both_hemisphere==false only the northern hemisphere (incl Equator) is returned.

source
SpeedyWeather.RingGrids.grid_cell_average!Method
grid_cell_average!(
    output::SpeedyWeather.RingGrids.AbstractGrid,
    input::SpeedyWeather.RingGrids.AbstractFullGrid
) -> SpeedyWeather.RingGrids.AbstractGrid

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.

source
SpeedyWeather.RingGrids.grid_cell_averageMethod
grid_cell_average(
    Grid::Type{<:SpeedyWeather.RingGrids.AbstractGrid},
    nlat_half::Integer,
    input::SpeedyWeather.RingGrids.AbstractFullGrid
) -> Any

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.

source
SpeedyWeather.RingGrids.whichringMethod
whichring(
    ij::Integer,
    rings::Vector{UnitRange{Int64}}
) -> Int64

Obtain ring index j from gridpoint ij and Vector{UnitRange} describing rind indices as obtained from eachring(::Grid)

source