Skip to content

Array dimensions ​

SpeedyWeather's two main array types, Field (see RingGrids) for gridded data and LowerTriangularArray (see LowerTriangularArrays) for spectral coefficients (see Spectral packing), unravel the horizontal into the first array dimension. Any additional array dimension can then represent whatever you like: the vertical, time (or time steps of the time integration), etc. To record what these additional dimensions actually mean, both types (optionally) carry a dimension tag from the ArrayDimensions module alongside their data. The following tags are defined

2D (horizontal only)3D + vertical3D + time4D + vertical + time
grid (Field)XY (default)XYZXYTXYZT
spectral (LowerTriangularArray)LM (default)LMZLMTLMZT

with X, Y for longitude, latitude and L, M for degree, order of the spherical harmonics (both pairs unravelled into a single array dimension), Z for the vertical and T for time. For a ColumnField (vertical first) there are also ZXY and ZXYT. The defaults XY and LM make no assumption about the meaning of any additional dimension.

All of these are singleton types: they hold no data, do not change the memory layout of the arrays, and, being known at compile time, come at no runtime cost.

Where to see the dimensions ​

The array summary includes the dimension tag in parentheses

julia
using SpeedyWeather
spectrum = Spectrum(truncation=6)
L = rand(ComplexF32, spectrum, ArrayDimensions.LMZ(), 3)
21×3 (LMZ) LowerTriangularArray{ComplexF32, 2, Array{...}}
  0.13094264f0 + 0.003427446f0im  …  0.42145312f0 + 0.81727576f0im
   0.6265588f0 + 0.03390503f0im       0.8508018f0 + 0.3599236f0im
  0.22127753f0 + 0.7082122f0im        0.3720677f0 + 0.5477887f0im
   0.9995791f0 + 0.8749445f0im        0.4373843f0 + 0.48399693f0im
   0.3559128f0 + 0.87890595f0im       0.5882576f0 + 0.98865867f0im
  0.74675363f0 + 0.48619664f0im   …   0.8932226f0 + 0.5001182f0im
  0.73095435f0 + 0.7209559f0im       0.09507096f0 + 0.38788122f0im
  0.12224829f0 + 0.013669312f0im      0.5366263f0 + 0.8367522f0im
   0.7889049f0 + 0.037780344f0im     0.21332127f0 + 0.1381771f0im
    0.224707f0 + 0.103366375f0im     0.42081356f0 + 0.6414977f0im
               ⋮                  ⋱  
 0.055250347f0 + 0.58227664f0im      0.19657093f0 + 0.24604046f0im
 0.019499421f0 + 0.4725185f0im        0.4406939f0 + 0.13544834f0im
   0.8573893f0 + 0.44994098f0im      0.91935575f0 + 0.053390205f0im
   0.5575018f0 + 0.7574827f0im    …  0.02743864f0 + 0.9434509f0im
  0.32145327f0 + 0.81136984f0im        0.567749f0 + 0.79354906f0im
   0.7013314f0 + 0.27223063f0im      0.82099134f0 + 0.25241804f0im
  0.35042822f0 + 0.5846753f0im         0.925532f0 + 0.29798055f0im
  0.29170024f0 + 0.40955484f0im        0.702987f0 + 0.8049832f0im
   0.6742155f0 + 0.48439467f0im   …  0.17433918f0 + 0.7850308f0im

here (LMZ) says that the 3 elements in the second array dimension are vertical layers. Without an explicit tag the default applies

julia
L2 = rand(ComplexF32, spectrum, 3)
21×3 (LM+) LowerTriangularArray{ComplexF32, 2, Array{...}}
  0.77295566f0 + 0.3274194f0im     …    0.9083884f0 + 0.91984385f0im
  0.28232908f0 + 0.9589227f0im          0.7502715f0 + 0.9028469f0im
   0.5705921f0 + 0.13786155f0im         0.7671057f0 + 0.46223605f0im
   0.9207352f0 + 0.7815521f0im          0.8781841f0 + 0.9165034f0im
  0.52571183f0 + 0.5370202f0im         0.22152078f0 + 0.49562955f0im
   0.8269586f0 + 0.7328111f0im     …  0.121486425f0 + 0.9926941f0im
  0.63485897f0 + 0.25335938f0im        0.77324855f0 + 0.4873532f0im
  0.34662062f0 + 0.36515874f0im        0.15595084f0 + 0.2870013f0im
 0.084940195f0 + 0.3243515f0im         0.34493828f0 + 0.8206761f0im
  0.38560146f0 + 0.11048567f0im        0.42553568f0 + 0.90756315f0im
               ⋮                   ⋱  
  0.22718251f0 + 0.5232618f0im         0.72408235f0 + 0.8781212f0im
  0.95877796f0 + 0.0062571764f0im       0.5612592f0 + 0.13232702f0im
   0.8532472f0 + 0.5083636f0im          0.2111181f0 + 0.8924874f0im
  0.27322704f0 + 0.4610983f0im     …    0.3934412f0 + 0.9888767f0im
  0.68443096f0 + 0.21553081f0im        0.43064904f0 + 0.28695577f0im
  0.11194742f0 + 0.8197524f0im        0.022118092f0 + 0.52121776f0im
  0.80579376f0 + 0.03248173f0im        0.08021414f0 + 0.7690302f0im
  0.25579852f0 + 0.19966167f0im        0.47073638f0 + 0.062223554f0im
  0.39349085f0 + 0.20599705f0im    …   0.28254932f0 + 0.15370429f0im

and the summary reads (LM+): the + denotes that the array has more dimensions than the tag describes, i.e. no assumption is made about what the second dimension means. The tag is also directly accessible as a field

julia
L.dims
SpeedyWeatherInternals.ArrayDimensions.LMZ()

All of the above works the same for Field, e.g.

julia
field = zeros(FullGaussianGrid(4), ArrayDimensions.XYZ(), 3)
128×3, 8-ring (XYZ) FullGaussianField{Float32, 2} as Array on CPU
 0.0f0  0.0f0  0.0f0
 0.0f0  0.0f0  0.0f0
 0.0f0  0.0f0  0.0f0
 0.0f0  0.0f0  0.0f0
 0.0f0  0.0f0  0.0f0
 0.0f0  0.0f0  0.0f0
 0.0f0  0.0f0  0.0f0
 0.0f0  0.0f0  0.0f0
 0.0f0  0.0f0  0.0f0
 0.0f0  0.0f0  0.0f0
 ⋮             
 0.0f0  0.0f0  0.0f0
 0.0f0  0.0f0  0.0f0
 0.0f0  0.0f0  0.0f0
 0.0f0  0.0f0  0.0f0
 0.0f0  0.0f0  0.0f0
 0.0f0  0.0f0  0.0f0
 0.0f0  0.0f0  0.0f0
 0.0f0  0.0f0  0.0f0
 0.0f0  0.0f0  0.0f0

What the dimensions are (not) used for ​

The dimension tags are bookkeeping only. They do not change how an array is indexed, computed on, or broadcast: multiplying an XYZ field with an XYT field will not return an XYZT field — broadcasting ignores the tags entirely (the result simply carries over the tag of one of its inputs). Instead, the tags allow manual decisions whenever the meaning of a dimension matters, through

julia
ArrayDimensions.hasvertical(L), ArrayDimensions.hastime(L)
(true, false)

or through dispatch on the tag types and their unions like ArrayDimensions.DimensionsWithTime and ArrayDimensions.DimensionsWithVertical.

Writing your own ...WithTime-style alias

These aliases are where clauses over the array type, e.g. Field{T, N, A, G, Dims} where {..., Dims <: DimensionsWithTime}. If you write one yourself, repeat all parameter bounds of the underlying type declaration. A where clause with looser bounds than the declaration is not a subtype of it, so the alias would silently fail to dispatch: a method on the alias and a fallback on the plain type end up unordered and Julia picks by definition order rather than by the tag. LowerTriangularArray declares ArrayType <: AbstractArray{T, N} and S <: AbstractSpectrum, so its aliases must spell those out; AbstractField declares its parameters unbounded, so its aliases need not.

The tags are preserved through similar, zero, views and broadcasting; indexing into a tagged dimension with an integer drops it accordingly, e.g. L[:, 1] of an LMZ-tagged array returns an LM-tagged one.

Within SpeedyWeather, the variables in simulation.variables (see The Variables struct) carry these tags; in particular the step dimension that prognostic and tendency variables have for the time integration is tagged as time T, so their summaries show, e.g., (LMZT) or (XYZT), see Step dimension. For more details on the two array types see Array dimensions of a Field and Array dimensions for LowerTriangularArray.