diff --git a/previews/PR387/barotropic/index.html b/previews/PR387/barotropic/index.html index 02df96940..77882368a 100644 --- a/previews/PR387/barotropic/index.html +++ b/previews/PR387/barotropic/index.html @@ -9,4 +9,4 @@ w_{i+1} &= u_{i-1} + 2\Delta tF(v_i) \\ u_i &= v_i + \frac{\nu\alpha}{2}(w_{i+1} - 2v_i + u_{i-1}) \\ v_{i+1} &= w_{i+1} - \frac{\nu(1-\alpha)}{2}(w_{i+1} - 2v_i + u_{i-1}) -\end{aligned}\]

with the Williams filter parameter $\alpha \in [0.5,1]$. For $\alpha=1$ we're back with the Robert-Asselin filter (the first two lines).

The Laplacian in the parentheses is often called a displacement, meaning that the filtered value is displaced (or corrected) in the direction of the two surrounding time steps. The Williams filter now also applies the same displacement, but in the opposite direction to the next time step $i+1$ as a correction step (line 3 above) for a once-filtered value $v_{i+1}$ which will then be twice-filtered by the Robert-Asselin filter on the next iteration. For more details see the referenced publications.

The initial Euler step (see Time integration, Table) is not filtered. Both the the Robert-Asselin and Williams filter are then switched on for all following leapfrog time steps.

References

+\end{aligned}\]

with the Williams filter parameter $\alpha \in [0.5,1]$. For $\alpha=1$ we're back with the Robert-Asselin filter (the first two lines).

The Laplacian in the parentheses is often called a displacement, meaning that the filtered value is displaced (or corrected) in the direction of the two surrounding time steps. The Williams filter now also applies the same displacement, but in the opposite direction to the next time step $i+1$ as a correction step (line 3 above) for a once-filtered value $v_{i+1}$ which will then be twice-filtered by the Robert-Asselin filter on the next iteration. For more details see the referenced publications.

The initial Euler step (see Time integration, Table) is not filtered. Both the the Robert-Asselin and Williams filter are then switched on for all following leapfrog time steps.

References

diff --git a/previews/PR387/extending/index.html b/previews/PR387/extending/index.html index aa42948ce..3c40d6d84 100644 --- a/previews/PR387/extending/index.html +++ b/previews/PR387/extending/index.html @@ -131,23 +131,23 @@ model = BarotropicModel(;spectral_grid,initial_conditions,forcing=stochastic_stirring) simulation = initialize!(model) run!(simulation)
                         Surface relative vorticity                          
-       ┌────────────────────────────────────────────────────────────┐5.0f-5  
+       ┌────────────────────────────────────────────────────────────┐4.0f-5  
     90 ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ┌──┐   
        ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄   
        ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄   
        ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄   
        ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄   
        ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄   
-       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄   
-    ˚N ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄   
-       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄   
-       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄   
-       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄   
-       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄   
-       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄   
-       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄   
-   -90 ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ └──┘   
+       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄   
+    ˚N ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄   
+       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄   
+       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄   
+       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄   
+       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄   
+       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄   
+       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄   
+   -90 ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ └──┘   
        └────────────────────────────────────────────────────────────┘-4.0f-5 
         0                           ˚E                           360         

Yay! As you can see the vorticity does something funky on the southern hemisphere but not on the northern, as we do not force there. Awesome! Adding new components other than forcing works surprisingly similar. We briefly discuss how to add a custom drag to illustrate the differences but there are not really many.

Custom drag

From the barotropic vorticity equation in Custom forcing: define we omitted the drag term $-r\zeta$ which however can be defined in a strikingly similar way. This section is just to outline some differences.

SpeedyWeather defines AbstractForcing and AbstractDrag, both are only conceptual supertypes, and in fact you could define a forcing as a subtype of AbstractDrag and vice versa. So for a drag, most straight-forwardly you would do

struct MyDrag <: AbstractDrag
     # parameters and arrays
-end

then define the initialize! function as before, but extend the method drag! instead of forcing!. The only detail that is important to know is that forcing! is called first, then drag! and then the other tendencies. So if you write into vor_tend like so vor_tend[1] = 1, you will overwrite the previous tendency. For the forcing that does not matter as it is the first one to be called, but for the drag you will want to do vor_tend[1] += 1 instead to accumulate the tendency. Otherwise you would undo the forcing term! Note that this conflict would be avoided if the forcing writes into vor_tend but the drag writes into u_tend_grid.

In general, these are the fields you can write into for new terms

These space restrictions exist because of the way how SpeedyWeather transforms between spaces to obtain tendencies.

+end

then define the initialize! function as before, but extend the method drag! instead of forcing!. The only detail that is important to know is that forcing! is called first, then drag! and then the other tendencies. So if you write into vor_tend like so vor_tend[1] = 1, you will overwrite the previous tendency. For the forcing that does not matter as it is the first one to be called, but for the drag you will want to do vor_tend[1] += 1 instead to accumulate the tendency. Otherwise you would undo the forcing term! Note that this conflict would be avoided if the forcing writes into vor_tend but the drag writes into u_tend_grid.

In general, these are the fields you can write into for new terms

These space restrictions exist because of the way how SpeedyWeather transforms between spaces to obtain tendencies.

diff --git a/previews/PR387/functions/index.html b/previews/PR387/functions/index.html index 0147b0ee9..f873a7524 100644 --- a/previews/PR387/functions/index.html +++ b/previews/PR387/functions/index.html @@ -1,102 +1,102 @@ -Function and type index · SpeedyWeather.jl

Function and type index

SpeedyWeather.BarotropicModelType

The BarotropicModel struct holds all other structs that contain precalculated constants, whether scalars or arrays that do not change throughout model integration.

  • spectral_grid::SpectralGrid: dictates resolution for many other components

  • planet::SpeedyWeather.AbstractPlanet: contains physical and orbital characteristics

  • atmosphere::SpeedyWeather.AbstractAtmosphere

  • forcing::SpeedyWeather.AbstractForcing{NF} where NF<:AbstractFloat

  • drag::SpeedyWeather.AbstractDrag{NF} where NF<:AbstractFloat

  • initial_conditions::SpeedyWeather.InitialConditions

  • time_stepping::SpeedyWeather.TimeStepper{NF} where NF<:AbstractFloat

  • spectral_transform::SpectralTransform

  • horizontal_diffusion::SpeedyWeather.HorizontalDiffusion{NF} where NF<:AbstractFloat

  • implicit::SpeedyWeather.AbstractImplicit{NF} where NF<:AbstractFloat

  • geometry::Geometry

  • constants::DynamicsConstants

  • device_setup::SpeedyWeather.DeviceSetup

  • output::SpeedyWeather.AbstractOutputWriter

  • feedback::SpeedyWeather.AbstractFeedback

source
SpeedyWeather.ClockType

Clock struct keeps track of the model time, how many days to integrate for and how many time steps this takes

  • time::Dates.DateTime: current model time

  • n_days::Float64: number of days to integrate for, set in run!(::Simulation)

  • n_timesteps::Int64: number of time steps to integrate for, set in initialize!(::Clock,::TimeStepper)

.

source
SpeedyWeather.ClockMethod
Clock(
+Function and type index · SpeedyWeather.jl

Function and type index

SpeedyWeather.BarotropicModelType

The BarotropicModel struct holds all other structs that contain precalculated constants, whether scalars or arrays that do not change throughout model integration.

  • spectral_grid::SpectralGrid: dictates resolution for many other components

  • planet::SpeedyWeather.AbstractPlanet: contains physical and orbital characteristics

  • atmosphere::SpeedyWeather.AbstractAtmosphere

  • forcing::SpeedyWeather.AbstractForcing{NF} where NF<:AbstractFloat

  • drag::SpeedyWeather.AbstractDrag{NF} where NF<:AbstractFloat

  • initial_conditions::SpeedyWeather.InitialConditions

  • time_stepping::SpeedyWeather.TimeStepper{NF} where NF<:AbstractFloat

  • spectral_transform::SpectralTransform

  • horizontal_diffusion::SpeedyWeather.HorizontalDiffusion{NF} where NF<:AbstractFloat

  • implicit::SpeedyWeather.AbstractImplicit{NF} where NF<:AbstractFloat

  • geometry::Geometry

  • constants::DynamicsConstants

  • device_setup::SpeedyWeather.DeviceSetup

  • output::SpeedyWeather.AbstractOutputWriter

  • feedback::SpeedyWeather.AbstractFeedback

source
SpeedyWeather.ClockType

Clock struct keeps track of the model time, how many days to integrate for and how many time steps this takes

  • time::Dates.DateTime: current model time

  • n_days::Float64: number of days to integrate for, set in run!(::Simulation)

  • n_timesteps::Int64: number of time steps to integrate for, set in initialize!(::Clock,::TimeStepper)

.

source
SpeedyWeather.ClockMethod
Clock(
     time_stepping::SpeedyWeather.TimeStepper;
     kwargs...
 ) -> SpeedyWeather.Clock
-

Create and initialize a clock from time_stepping

source
SpeedyWeather.ColumnVariablesType

Mutable struct that contains all prognostic (copies thereof) and diagnostic variables in a single column needed to evaluate the physical parametrizations. For now the struct is mutable as we will reuse the struct to iterate over horizontal grid points. Every column vector has nlev entries, from [1] at the top to [end] at the lowermost model level at the planetary boundary layer.

  • nlev::Int64

  • nband::Int64

  • n_stratosphere_levels::Int64

  • jring::Int64

  • lond::AbstractFloat

  • latd::AbstractFloat

  • land_fraction::AbstractFloat

  • u::Vector{NF} where NF<:AbstractFloat

  • v::Vector{NF} where NF<:AbstractFloat

  • temp::Vector{NF} where NF<:AbstractFloat

  • humid::Vector{NF} where NF<:AbstractFloat

  • ln_pres::Vector{NF} where NF<:AbstractFloat

  • pres::Vector{NF} where NF<:AbstractFloat

  • u_tend::Vector{NF} where NF<:AbstractFloat

  • v_tend::Vector{NF} where NF<:AbstractFloat

  • temp_tend::Vector{NF} where NF<:AbstractFloat

  • humid_tend::Vector{NF} where NF<:AbstractFloat

  • geopot::Vector{NF} where NF<:AbstractFloat

  • flux_u_upward::Vector{NF} where NF<:AbstractFloat

  • flux_u_downward::Vector{NF} where NF<:AbstractFloat

  • flux_v_upward::Vector{NF} where NF<:AbstractFloat

  • flux_v_downward::Vector{NF} where NF<:AbstractFloat

  • flux_temp_upward::Vector{NF} where NF<:AbstractFloat

  • flux_temp_downward::Vector{NF} where NF<:AbstractFloat

  • flux_humid_upward::Vector{NF} where NF<:AbstractFloat

  • flux_humid_downward::Vector{NF} where NF<:AbstractFloat

  • sat_humid::Vector{NF} where NF<:AbstractFloat

  • sat_vap_pres::Vector{NF} where NF<:AbstractFloat

  • dry_static_energy::Vector{NF} where NF<:AbstractFloat

  • moist_static_energy::Vector{NF} where NF<:AbstractFloat

  • humid_half::Vector{NF} where NF<:AbstractFloat

  • sat_humid_half::Vector{NF} where NF<:AbstractFloat

  • sat_moist_static_energy::Vector{NF} where NF<:AbstractFloat

  • dry_static_energy_half::Vector{NF} where NF<:AbstractFloat

  • sat_moist_static_energy_half::Vector{NF} where NF<:AbstractFloat

  • conditional_instability::Bool

  • activate_convection::Bool

  • cloud_top::Int64

  • excess_humidity::AbstractFloat

  • cloud_base_mass_flux::AbstractFloat

  • precip_convection::AbstractFloat

  • net_flux_humid::Vector{NF} where NF<:AbstractFloat

  • net_flux_dry_static_energy::Vector{NF} where NF<:AbstractFloat

  • entrainment_profile::Vector{NF} where NF<:AbstractFloat

  • precip_large_scale::AbstractFloat

  • wvi::Matrix{NF} where NF<:AbstractFloat

  • tau2::Matrix{NF} where NF<:AbstractFloat

  • dfabs::Vector{NF} where NF<:AbstractFloat

  • fsfcd::AbstractFloat

  • st4a::Matrix{NF} where NF<:AbstractFloat

  • flux::Vector{NF} where NF<:AbstractFloat

  • fsfcu::AbstractFloat

  • ts::AbstractFloat

  • fsfc::AbstractFloat

  • ftop::AbstractFloat

  • stratc::Vector{NF} where NF<:AbstractFloat

  • tyear::AbstractFloat

  • csol::AbstractFloat

  • topsr::AbstractFloat

  • fsol::AbstractFloat

  • ozupp::AbstractFloat

  • ozone::AbstractFloat

  • zenit::AbstractFloat

  • stratz::AbstractFloat

  • albsfc::AbstractFloat

  • ssrd::AbstractFloat

  • ssr::AbstractFloat

  • tsr::AbstractFloat

  • tend_t_rsw::Vector{NF} where NF<:AbstractFloat

  • norm_pres::AbstractFloat

  • icltop::Int64

  • cloudc::AbstractFloat

  • clstr::AbstractFloat

  • qcloud::AbstractFloat

  • fmask::AbstractFloat

  • rel_hum::Vector{NF} where NF<:AbstractFloat

  • grad_dry_static_energy::AbstractFloat

source
SpeedyWeather.DeviceSetupType
DeviceSetup{S<:AbstractDevice}

Holds information about the device the model is running on and workgroup size.

  • device::AbstractDevice: Device the model is running on
  • device_KA::KernelAbstractions.Device: Device for use with KernelAbstractions
  • n: workgroup size
source
SpeedyWeather.DynamicsConstantsType

Struct holding constants needed at runtime for the dynamical core in number format NF.

  • radius::AbstractFloat: Radius of Planet [m]

  • rotation::AbstractFloat: Angular frequency of Planet's rotation [s^-1]

  • gravity::AbstractFloat: Gravitational acceleration [m/s^2]

  • layer_thickness::AbstractFloat: shallow water layer thickness [m]

  • R_dry::AbstractFloat: specific gas constant for dry air [J/kg/K]

  • R_vapour::AbstractFloat: specific gas constant for water vapour [J/kg/K]

  • μ_virt_temp::AbstractFloat: used in Tv = T(1+μq) for virt temp Tv(T,q) calculation

  • cₚ::AbstractFloat: specific heat at constant pressure [J/K/kg]

  • κ::AbstractFloat: = R_dry/cₚ, gas const for air over heat capacity

  • water_density::AbstractFloat: water density [kg/m³]

  • f_coriolis::Vector{NF} where NF<:AbstractFloat: coriolis frequency [s^-1], scaled by radius as is vorticity = 2Ωsin(lat)radius

  • σ_lnp_A::Vector{NF} where NF<:AbstractFloat: σ-related factor A needed for adiabatic conversion term

  • σ_lnp_B::Vector{NF} where NF<:AbstractFloat: σ-related factor B needed for adiabatic conversion term

  • Δp_geopot_half::Vector{NF} where NF<:AbstractFloat: = R*(ln(pk+1) - ln(pk+1/2)), for half level geopotential

  • Δp_geopot_full::Vector{NF} where NF<:AbstractFloat: = R*(ln(pk+1/2) - ln(pk)), for full level geopotential

  • temp_ref_profile::Vector{NF} where NF<:AbstractFloat: reference temperature profile

source
SpeedyWeather.ColumnVariablesType

Mutable struct that contains all prognostic (copies thereof) and diagnostic variables in a single column needed to evaluate the physical parametrizations. For now the struct is mutable as we will reuse the struct to iterate over horizontal grid points. Every column vector has nlev entries, from [1] at the top to [end] at the lowermost model level at the planetary boundary layer.

  • nlev::Int64

  • nband::Int64

  • n_stratosphere_levels::Int64

  • jring::Int64

  • lond::AbstractFloat

  • latd::AbstractFloat

  • land_fraction::AbstractFloat

  • u::Vector{NF} where NF<:AbstractFloat

  • v::Vector{NF} where NF<:AbstractFloat

  • temp::Vector{NF} where NF<:AbstractFloat

  • humid::Vector{NF} where NF<:AbstractFloat

  • ln_pres::Vector{NF} where NF<:AbstractFloat

  • pres::Vector{NF} where NF<:AbstractFloat

  • u_tend::Vector{NF} where NF<:AbstractFloat

  • v_tend::Vector{NF} where NF<:AbstractFloat

  • temp_tend::Vector{NF} where NF<:AbstractFloat

  • humid_tend::Vector{NF} where NF<:AbstractFloat

  • geopot::Vector{NF} where NF<:AbstractFloat

  • flux_u_upward::Vector{NF} where NF<:AbstractFloat

  • flux_u_downward::Vector{NF} where NF<:AbstractFloat

  • flux_v_upward::Vector{NF} where NF<:AbstractFloat

  • flux_v_downward::Vector{NF} where NF<:AbstractFloat

  • flux_temp_upward::Vector{NF} where NF<:AbstractFloat

  • flux_temp_downward::Vector{NF} where NF<:AbstractFloat

  • flux_humid_upward::Vector{NF} where NF<:AbstractFloat

  • flux_humid_downward::Vector{NF} where NF<:AbstractFloat

  • sat_humid::Vector{NF} where NF<:AbstractFloat

  • sat_vap_pres::Vector{NF} where NF<:AbstractFloat

  • dry_static_energy::Vector{NF} where NF<:AbstractFloat

  • moist_static_energy::Vector{NF} where NF<:AbstractFloat

  • humid_half::Vector{NF} where NF<:AbstractFloat

  • sat_humid_half::Vector{NF} where NF<:AbstractFloat

  • sat_moist_static_energy::Vector{NF} where NF<:AbstractFloat

  • dry_static_energy_half::Vector{NF} where NF<:AbstractFloat

  • sat_moist_static_energy_half::Vector{NF} where NF<:AbstractFloat

  • conditional_instability::Bool

  • activate_convection::Bool

  • cloud_top::Int64

  • excess_humidity::AbstractFloat

  • cloud_base_mass_flux::AbstractFloat

  • precip_convection::AbstractFloat

  • net_flux_humid::Vector{NF} where NF<:AbstractFloat

  • net_flux_dry_static_energy::Vector{NF} where NF<:AbstractFloat

  • entrainment_profile::Vector{NF} where NF<:AbstractFloat

  • precip_large_scale::AbstractFloat

  • wvi::Matrix{NF} where NF<:AbstractFloat

  • tau2::Matrix{NF} where NF<:AbstractFloat

  • dfabs::Vector{NF} where NF<:AbstractFloat

  • fsfcd::AbstractFloat

  • st4a::Matrix{NF} where NF<:AbstractFloat

  • flux::Vector{NF} where NF<:AbstractFloat

  • fsfcu::AbstractFloat

  • ts::AbstractFloat

  • fsfc::AbstractFloat

  • ftop::AbstractFloat

  • stratc::Vector{NF} where NF<:AbstractFloat

  • tyear::AbstractFloat

  • csol::AbstractFloat

  • topsr::AbstractFloat

  • fsol::AbstractFloat

  • ozupp::AbstractFloat

  • ozone::AbstractFloat

  • zenit::AbstractFloat

  • stratz::AbstractFloat

  • albsfc::AbstractFloat

  • ssrd::AbstractFloat

  • ssr::AbstractFloat

  • tsr::AbstractFloat

  • tend_t_rsw::Vector{NF} where NF<:AbstractFloat

  • norm_pres::AbstractFloat

  • icltop::Int64

  • cloudc::AbstractFloat

  • clstr::AbstractFloat

  • qcloud::AbstractFloat

  • fmask::AbstractFloat

  • rel_hum::Vector{NF} where NF<:AbstractFloat

  • grad_dry_static_energy::AbstractFloat

source
SpeedyWeather.DeviceSetupType
DeviceSetup{S<:AbstractDevice}

Holds information about the device the model is running on and workgroup size.

  • device::AbstractDevice: Device the model is running on
  • device_KA::KernelAbstractions.Device: Device for use with KernelAbstractions
  • n: workgroup size
source
SpeedyWeather.DynamicsConstantsType

Struct holding constants needed at runtime for the dynamical core in number format NF.

  • radius::AbstractFloat: Radius of Planet [m]

  • rotation::AbstractFloat: Angular frequency of Planet's rotation [s^-1]

  • gravity::AbstractFloat: Gravitational acceleration [m/s^2]

  • layer_thickness::AbstractFloat: shallow water layer thickness [m]

  • R_dry::AbstractFloat: specific gas constant for dry air [J/kg/K]

  • R_vapour::AbstractFloat: specific gas constant for water vapour [J/kg/K]

  • μ_virt_temp::AbstractFloat: used in Tv = T(1+μq) for virt temp Tv(T,q) calculation

  • cₚ::AbstractFloat: specific heat at constant pressure [J/K/kg]

  • κ::AbstractFloat: = R_dry/cₚ, gas const for air over heat capacity

  • water_density::AbstractFloat: water density [kg/m³]

  • f_coriolis::Vector{NF} where NF<:AbstractFloat: coriolis frequency [s^-1], scaled by radius as is vorticity = 2Ωsin(lat)radius

  • σ_lnp_A::Vector{NF} where NF<:AbstractFloat: σ-related factor A needed for adiabatic conversion term

  • σ_lnp_B::Vector{NF} where NF<:AbstractFloat: σ-related factor B needed for adiabatic conversion term

  • Δp_geopot_half::Vector{NF} where NF<:AbstractFloat: = R*(ln(pk+1) - ln(pk+1/2)), for half level geopotential

  • Δp_geopot_full::Vector{NF} where NF<:AbstractFloat: = R*(ln(pk+1/2) - ln(pk)), for full level geopotential

  • temp_ref_profile::Vector{NF} where NF<:AbstractFloat: reference temperature profile

source
SpeedyWeather.DynamicsConstantsMethod
DynamicsConstants(
     spectral_grid::SpectralGrid,
     planet::SpeedyWeather.AbstractPlanet,
     atmosphere::SpeedyWeather.AbstractAtmosphere,
     geometry::Geometry
 ) -> Any
-

Generator function for a DynamicsConstants struct.

source
SpeedyWeather.EarthType

Create a struct Earth<:AbstractPlanet, with the following physical/orbital characteristics. Note that radius is not part of it as this should be chosen in SpectralGrid. Keyword arguments are

  • rotation::Float64: angular frequency of Earth's rotation [rad/s]

  • gravity::Float64: gravitational acceleration [m/s^2]

  • daily_cycle::Bool: switch on/off daily cycle

  • length_of_day::Float64: [hrs] in a day

  • seasonal_cycle::Bool: switch on/off seasonal cycle

  • length_of_year::Float64: [days] in a year

  • equinox::Dates.DateTime: time of spring equinox (year irrelevant)

  • axial_tilt::Float64: angle [˚] rotation axis tilt wrt to orbit

source
SpeedyWeather.EarthAtmosphereType

Create a struct EarthAtmosphere<:AbstractPlanet, with the following physical/chemical characteristics. Note that radius is not part of it as this should be chosen in SpectralGrid. Keyword arguments are

  • mol_mass_dry_air::Float64: molar mass of dry air [g/mol]

  • mol_mass_vapour::Float64: molar mass of water vapour [g/mol]

  • cₚ::Float64: specific heat at constant pressure [J/K/kg]

  • R_gas::Float64: universal gas constant [J/K/mol]

  • R_dry::Float64: specific gas constant for dry air [J/kg/K]

  • R_vapour::Float64: specific gas constant for water vapour [J/kg/K]

  • water_density::Float64: water density [kg/m³]

  • latent_heat_condensation::Float64: latent heat of condensation [J/g] for consistency with specific humidity [g/Kg], also called alhc

  • latent_heat_sublimation::Float64: latent heat of sublimation [J/g], also called alhs

  • stefan_boltzmann::Float64: stefan-Boltzmann constant [W/m²/K⁴]

  • lapse_rate::Float64: moist adiabatic temperature lapse rate $-dT/dz$ [K/km]

  • temp_ref::Float64: absolute temperature at surface $z=0$ [K]

  • temp_top::Float64: absolute temperature in stratosphere [K]

  • ΔT_stratosphere::Float64: for stratospheric lapse rate [K] after Jablonowski

  • σ_tropopause::Float64: start of the stratosphere in sigma coordinates

  • σ_boundary_layer::Float64: top of the planetary boundary layer in sigma coordinates

  • scale_height::Float64: scale height for pressure [km]

  • pres_ref::Float64: surface pressure [hPa]

  • scale_height_humid::Float64: scale height for specific humidity [km]

  • relhumid_ref::Float64: relative humidity of near-surface air [1]

  • water_pres_ref::Float64: saturation water vapour pressure [Pa]

  • layer_thickness::Float64: layer thickness for the shallow water model [km]

source
SpeedyWeather.EarthOrographyType

Earth's orography read from file, with smoothing.

  • path::String: path to the folder containing the orography file, pkg path default

  • file::String: filename of orography

  • file_Grid::Type{<:SpeedyWeather.RingGrids.AbstractGrid}: Grid the orography file comes on

  • scale::Float64: scale orography by a factor

  • smoothing::Bool: smooth the orography field?

  • smoothing_power::Float64: power of Laplacian for smoothing

  • smoothing_strength::Float64: highest degree l is multiplied by

  • smoothing_truncation::Int64: resolution of orography in spectral trunc

  • orography::SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat: height [m] on grid-point space.

  • geopot_surf::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: surface geopotential, height*gravity [m²/s²]

source
SpeedyWeather.EarthType

Create a struct Earth<:AbstractPlanet, with the following physical/orbital characteristics. Note that radius is not part of it as this should be chosen in SpectralGrid. Keyword arguments are

  • rotation::Float64: angular frequency of Earth's rotation [rad/s]

  • gravity::Float64: gravitational acceleration [m/s^2]

  • daily_cycle::Bool: switch on/off daily cycle

  • length_of_day::Float64: [hrs] in a day

  • seasonal_cycle::Bool: switch on/off seasonal cycle

  • length_of_year::Float64: [days] in a year

  • equinox::Dates.DateTime: time of spring equinox (year irrelevant)

  • axial_tilt::Float64: angle [˚] rotation axis tilt wrt to orbit

source
SpeedyWeather.EarthAtmosphereType

Create a struct EarthAtmosphere<:AbstractPlanet, with the following physical/chemical characteristics. Note that radius is not part of it as this should be chosen in SpectralGrid. Keyword arguments are

  • mol_mass_dry_air::Float64: molar mass of dry air [g/mol]

  • mol_mass_vapour::Float64: molar mass of water vapour [g/mol]

  • cₚ::Float64: specific heat at constant pressure [J/K/kg]

  • R_gas::Float64: universal gas constant [J/K/mol]

  • R_dry::Float64: specific gas constant for dry air [J/kg/K]

  • R_vapour::Float64: specific gas constant for water vapour [J/kg/K]

  • water_density::Float64: water density [kg/m³]

  • latent_heat_condensation::Float64: latent heat of condensation [J/g] for consistency with specific humidity [g/Kg], also called alhc

  • latent_heat_sublimation::Float64: latent heat of sublimation [J/g], also called alhs

  • stefan_boltzmann::Float64: stefan-Boltzmann constant [W/m²/K⁴]

  • lapse_rate::Float64: moist adiabatic temperature lapse rate $-dT/dz$ [K/km]

  • temp_ref::Float64: absolute temperature at surface $z=0$ [K]

  • temp_top::Float64: absolute temperature in stratosphere [K]

  • ΔT_stratosphere::Float64: for stratospheric lapse rate [K] after Jablonowski

  • σ_tropopause::Float64: start of the stratosphere in sigma coordinates

  • σ_boundary_layer::Float64: top of the planetary boundary layer in sigma coordinates

  • scale_height::Float64: scale height for pressure [km]

  • pres_ref::Float64: surface pressure [hPa]

  • scale_height_humid::Float64: scale height for specific humidity [km]

  • relhumid_ref::Float64: relative humidity of near-surface air [1]

  • water_pres_ref::Float64: saturation water vapour pressure [Pa]

  • layer_thickness::Float64: layer thickness for the shallow water model [km]

source
SpeedyWeather.EarthOrographyType

Earth's orography read from file, with smoothing.

  • path::String: path to the folder containing the orography file, pkg path default

  • file::String: filename of orography

  • file_Grid::Type{<:SpeedyWeather.RingGrids.AbstractGrid}: Grid the orography file comes on

  • scale::Float64: scale orography by a factor

  • smoothing::Bool: smooth the orography field?

  • smoothing_power::Float64: power of Laplacian for smoothing

  • smoothing_strength::Float64: highest degree l is multiplied by

  • smoothing_truncation::Int64: resolution of orography in spectral trunc

  • orography::SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat: height [m] on grid-point space.

  • geopot_surf::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: surface geopotential, height*gravity [m²/s²]

source
SpeedyWeather.EarthOrographyMethod
EarthOrography(
     spectral_grid::SpectralGrid;
     kwargs...
 ) -> Any
-

Generator function pulling the resolution information from spectral_grid.

source
SpeedyWeather.FeedbackType
Feedback() -> Feedback
 Feedback(verbose::Bool) -> Feedback
 Feedback(verbose::Bool, debug::Bool) -> Feedback
-

Generator function for a Feedback struct.

source
SpeedyWeather.FeedbackType

Feedback struct that contains options and object for command-line feedback like the progress meter.

  • verbose::Bool: print feedback to REPL?

  • debug::Bool: check for NaRs in the prognostic variables

  • output::Bool: write a progress.txt file? State synced with OutputWriter.output

  • id::Union{Int64, String}: identification of run, taken from ::OutputWriter

  • run_path::String: path to run folder, taken from ::OutputWriter

  • progress_meter::ProgressMeter.Progress: struct containing everything progress related

  • progress_txt::Union{Nothing, IOStream}: txt is a Nothing in case of no output

  • nars_detected::Bool: did Infs/NaNs occur in the simulation?

source
SpeedyWeather.GeometryType

Construct Geometry struct containing parameters and arrays describing an iso-latitude grid <:AbstractGrid and the vertical levels. Pass on SpectralGrid to calculate the following fields

  • spectral_grid::SpectralGrid: SpectralGrid that defines spectral and grid resolution

  • Grid::Type{<:SpeedyWeather.RingGrids.AbstractGrid}: grid of the dynamical core

  • nlat_half::Int64: resolution parameter nlat_half of Grid, # of latitudes on one hemisphere (incl Equator)

  • nlon_max::Int64: maximum number of longitudes (at/around Equator)

  • nlon::Int64: =nlon_max, same (used for compatibility), TODO: still needed?

  • nlat::Int64: number of latitude rings

  • nlev::Int64: number of vertical levels

  • npoints::Int64: total number of grid points

  • radius::AbstractFloat: Planet's radius [m]

  • colat::Vector{Float64}: array of colatitudes in radians (0...π)

  • latd::Vector{Float64}: array of latitudes in degrees (90˚...-90˚)

  • lond::Vector{Float64}: array of longitudes in degrees (0...360˚), empty for non-full grids

  • londs::Vector{NF} where NF<:AbstractFloat: longitude (-180˚...180˚) for each grid point in ring order

  • latds::Vector{NF} where NF<:AbstractFloat: latitude (-90˚...˚90) for each grid point in ring order

  • sinlat::Vector{NF} where NF<:AbstractFloat: sin of latitudes

  • coslat::Vector{NF} where NF<:AbstractFloat: cos of latitudes

  • coslat⁻¹::Vector{NF} where NF<:AbstractFloat: = 1/cos(lat)

  • coslat²::Vector{NF} where NF<:AbstractFloat: = cos²(lat)

  • coslat⁻²::Vector{NF} where NF<:AbstractFloat: # = 1/cos²(lat)

  • σ_levels_half::Vector{NF} where NF<:AbstractFloat: σ at half levels, σ_k+1/2

  • σ_levels_full::Vector{NF} where NF<:AbstractFloat: σ at full levels, σₖ

  • σ_levels_thick::Vector{NF} where NF<:AbstractFloat: σ level thicknesses, σₖ₊₁ - σₖ

  • ln_σ_levels_full::Vector{NF} where NF<:AbstractFloat: log of σ at full levels, include surface (σ=1) as last element

source
SpeedyWeather.GeometryMethod
Geometry(spectral_grid::SpectralGrid) -> Any
-

Generator function for Geometry struct based on spectral_grid.

source
SpeedyWeather.HeldSuarezType

Struct that defines the temperature relaxation from Held and Suarez, 1996 BAMS

  • nlat::Int64: number of latitude rings

  • nlev::Int64: number of vertical levels

  • σb::Float64: sigma coordinate below which faster surface relaxation is applied

  • relax_time_slow::Float64: time scale [hrs] for slow global relaxation

  • relax_time_fast::Float64: time scale [hrs] for faster tropical surface relaxation

  • Tmin::Float64: minimum equilibrium temperature [K]

  • Tmax::Float64: maximum equilibrium temperature [K]

  • ΔTy::Float64: meridional temperature gradient [K]

  • Δθz::Float64: vertical temperature gradient [K]

  • κ::Base.RefValue{NF} where NF<:AbstractFloat

  • p₀::Base.RefValue{NF} where NF<:AbstractFloat

  • temp_relax_freq::Matrix{NF} where NF<:AbstractFloat

  • temp_equil_a::Vector{NF} where NF<:AbstractFloat

  • temp_equil_b::Vector{NF} where NF<:AbstractFloat

source
SpeedyWeather.HeldSuarezMethod
HeldSuarez(SG::SpectralGrid; kwargs...) -> Any
-

create a HeldSuarez temperature relaxation with arrays allocated given spectral_grid

source
SpeedyWeather.HyperDiffusionType

Struct for horizontal hyper diffusion of vor, div, temp; implicitly in spectral space with a power of the Laplacian (default=4) and the strength controlled by time_scale. Options exist to scale the diffusion by resolution, and adaptive depending on the current vorticity maximum to increase diffusion in active layers. Furthermore the power can be decreased above the tapering_σ to power_stratosphere (default 2). For Barotropic, ShallowWater, the default non-adaptive constant-time scale hyper diffusion is used. Options are

  • trunc::Int64: spectral resolution

  • nlev::Int64: number of vertical levels

  • power::Float64: power of Laplacian

  • time_scale::Float64: diffusion time scales [hrs]

  • resolution_scaling::Float64: stronger diffusion with resolution? 0: constant with trunc, 1: (inverse) linear with trunc, etc

  • power_stratosphere::Float64: different power for tropopause/stratosphere

  • tapering_σ::Float64: linearly scale towards power_stratosphere above this σ

  • adaptive::Bool: adaptive = higher diffusion for layers with higher vorticity levels.

  • vor_max::Float64: above this (absolute) vorticity level [1/s], diffusion is increased

  • adaptive_strength::Float64: increase strength above vor_max by this factor times max(abs(vor))/vor_max

  • ∇²ⁿ_2D::Vector

  • ∇²ⁿ_2D_implicit::Vector

  • ∇²ⁿ::Array{Vector{NF}, 1} where NF

  • ∇²ⁿ_implicit::Array{Vector{NF}, 1} where NF

source
SpeedyWeather.FeedbackType

Feedback struct that contains options and object for command-line feedback like the progress meter.

  • verbose::Bool: print feedback to REPL?

  • debug::Bool: check for NaRs in the prognostic variables

  • output::Bool: write a progress.txt file? State synced with OutputWriter.output

  • id::Union{Int64, String}: identification of run, taken from ::OutputWriter

  • run_path::String: path to run folder, taken from ::OutputWriter

  • progress_meter::ProgressMeter.Progress: struct containing everything progress related

  • progress_txt::Union{Nothing, IOStream}: txt is a Nothing in case of no output

  • nars_detected::Bool: did Infs/NaNs occur in the simulation?

source
SpeedyWeather.GeometryType

Construct Geometry struct containing parameters and arrays describing an iso-latitude grid <:AbstractGrid and the vertical levels. Pass on SpectralGrid to calculate the following fields

  • spectral_grid::SpectralGrid: SpectralGrid that defines spectral and grid resolution

  • Grid::Type{<:SpeedyWeather.RingGrids.AbstractGrid}: grid of the dynamical core

  • nlat_half::Int64: resolution parameter nlat_half of Grid, # of latitudes on one hemisphere (incl Equator)

  • nlon_max::Int64: maximum number of longitudes (at/around Equator)

  • nlon::Int64: =nlon_max, same (used for compatibility), TODO: still needed?

  • nlat::Int64: number of latitude rings

  • nlev::Int64: number of vertical levels

  • npoints::Int64: total number of grid points

  • radius::AbstractFloat: Planet's radius [m]

  • colat::Vector{Float64}: array of colatitudes in radians (0...π)

  • latd::Vector{Float64}: array of latitudes in degrees (90˚...-90˚)

  • lond::Vector{Float64}: array of longitudes in degrees (0...360˚), empty for non-full grids

  • londs::Vector{NF} where NF<:AbstractFloat: longitude (-180˚...180˚) for each grid point in ring order

  • latds::Vector{NF} where NF<:AbstractFloat: latitude (-90˚...˚90) for each grid point in ring order

  • sinlat::Vector{NF} where NF<:AbstractFloat: sin of latitudes

  • coslat::Vector{NF} where NF<:AbstractFloat: cos of latitudes

  • coslat⁻¹::Vector{NF} where NF<:AbstractFloat: = 1/cos(lat)

  • coslat²::Vector{NF} where NF<:AbstractFloat: = cos²(lat)

  • coslat⁻²::Vector{NF} where NF<:AbstractFloat: # = 1/cos²(lat)

  • σ_levels_half::Vector{NF} where NF<:AbstractFloat: σ at half levels, σ_k+1/2

  • σ_levels_full::Vector{NF} where NF<:AbstractFloat: σ at full levels, σₖ

  • σ_levels_thick::Vector{NF} where NF<:AbstractFloat: σ level thicknesses, σₖ₊₁ - σₖ

  • ln_σ_levels_full::Vector{NF} where NF<:AbstractFloat: log of σ at full levels, include surface (σ=1) as last element

source
SpeedyWeather.GeometryMethod
Geometry(spectral_grid::SpectralGrid) -> Any
+

Generator function for Geometry struct based on spectral_grid.

source
SpeedyWeather.HeldSuarezType

Struct that defines the temperature relaxation from Held and Suarez, 1996 BAMS

  • nlat::Int64: number of latitude rings

  • nlev::Int64: number of vertical levels

  • σb::Float64: sigma coordinate below which faster surface relaxation is applied

  • relax_time_slow::Float64: time scale [hrs] for slow global relaxation

  • relax_time_fast::Float64: time scale [hrs] for faster tropical surface relaxation

  • Tmin::Float64: minimum equilibrium temperature [K]

  • Tmax::Float64: maximum equilibrium temperature [K]

  • ΔTy::Float64: meridional temperature gradient [K]

  • Δθz::Float64: vertical temperature gradient [K]

  • κ::Base.RefValue{NF} where NF<:AbstractFloat

  • p₀::Base.RefValue{NF} where NF<:AbstractFloat

  • temp_relax_freq::Matrix{NF} where NF<:AbstractFloat

  • temp_equil_a::Vector{NF} where NF<:AbstractFloat

  • temp_equil_b::Vector{NF} where NF<:AbstractFloat

source
SpeedyWeather.HeldSuarezMethod
HeldSuarez(SG::SpectralGrid; kwargs...) -> Any
+

create a HeldSuarez temperature relaxation with arrays allocated given spectral_grid

source
SpeedyWeather.HyperDiffusionType

Struct for horizontal hyper diffusion of vor, div, temp; implicitly in spectral space with a power of the Laplacian (default=4) and the strength controlled by time_scale. Options exist to scale the diffusion by resolution, and adaptive depending on the current vorticity maximum to increase diffusion in active layers. Furthermore the power can be decreased above the tapering_σ to power_stratosphere (default 2). For Barotropic, ShallowWater, the default non-adaptive constant-time scale hyper diffusion is used. Options are

  • trunc::Int64: spectral resolution

  • nlev::Int64: number of vertical levels

  • power::Float64: power of Laplacian

  • time_scale::Float64: diffusion time scales [hrs]

  • resolution_scaling::Float64: stronger diffusion with resolution? 0: constant with trunc, 1: (inverse) linear with trunc, etc

  • power_stratosphere::Float64: different power for tropopause/stratosphere

  • tapering_σ::Float64: linearly scale towards power_stratosphere above this σ

  • adaptive::Bool: adaptive = higher diffusion for layers with higher vorticity levels.

  • vor_max::Float64: above this (absolute) vorticity level [1/s], diffusion is increased

  • adaptive_strength::Float64: increase strength above vor_max by this factor times max(abs(vor))/vor_max

  • ∇²ⁿ_2D::Vector

  • ∇²ⁿ_2D_implicit::Vector

  • ∇²ⁿ::Array{Vector{NF}, 1} where NF

  • ∇²ⁿ_implicit::Array{Vector{NF}, 1} where NF

source
SpeedyWeather.HyperDiffusionMethod
HyperDiffusion(
     spectral_grid::SpectralGrid;
     kwargs...
 ) -> Any
-

Generator function based on the resolutin in spectral_grid. Passes on keyword arguments.

source
SpeedyWeather.ImplicitPrimitiveEqType

Struct that holds various precomputed arrays for the semi-implicit correction to prevent gravity waves from amplifying in the primitive equation model.

  • trunc::Int64: spectral resolution

  • nlev::Int64: number of vertical levels

  • α::Float64: time-step coefficient: 0=explicit, 0.5=centred implicit, 1=backward implicit

  • temp_profile::Vector{NF} where NF<:AbstractFloat: vertical temperature profile, obtained from diagn

  • ξ::Base.RefValue{NF} where NF<:AbstractFloat: time step 2α*Δt packed in RefValue for mutability

  • R::Matrix{NF} where NF<:AbstractFloat: divergence: operator for the geopotential calculation

  • U::Vector{NF} where NF<:AbstractFloat: divergence: the -RdTₖ∇² term excl the eigenvalues from ∇² for divergence

  • L::Matrix{NF} where NF<:AbstractFloat: temperature: operator for the TₖD + κTₖDlnps/Dt term

  • W::Vector{NF} where NF<:AbstractFloat: pressure: vertical averaging of the -D̄ term in the log surface pres equation

  • L0::Vector{NF} where NF<:AbstractFloat: components to construct L, 1/ 2Δσ

  • L1::Matrix{NF} where NF<:AbstractFloat: vert advection term in the temperature equation (below+above)

  • L2::Vector{NF} where NF<:AbstractFloat: factor in front of the divsumabove term

  • L3::Matrix{NF} where NF<:AbstractFloat: sumabove operator itself

  • L4::Vector{NF} where NF<:AbstractFloat: factor in front of div term in Dlnps/Dt

  • S::Matrix{NF} where NF<:AbstractFloat: for every l the matrix to be inverted

  • S⁻¹::Array{NF, 3} where NF<:AbstractFloat: combined inverted operator: S = 1 - ξ²(RL + UW)

source
SpeedyWeather.ImplicitPrimitiveEqType

Struct that holds various precomputed arrays for the semi-implicit correction to prevent gravity waves from amplifying in the primitive equation model.

  • trunc::Int64: spectral resolution

  • nlev::Int64: number of vertical levels

  • α::Float64: time-step coefficient: 0=explicit, 0.5=centred implicit, 1=backward implicit

  • temp_profile::Vector{NF} where NF<:AbstractFloat: vertical temperature profile, obtained from diagn

  • ξ::Base.RefValue{NF} where NF<:AbstractFloat: time step 2α*Δt packed in RefValue for mutability

  • R::Matrix{NF} where NF<:AbstractFloat: divergence: operator for the geopotential calculation

  • U::Vector{NF} where NF<:AbstractFloat: divergence: the -RdTₖ∇² term excl the eigenvalues from ∇² for divergence

  • L::Matrix{NF} where NF<:AbstractFloat: temperature: operator for the TₖD + κTₖDlnps/Dt term

  • W::Vector{NF} where NF<:AbstractFloat: pressure: vertical averaging of the -D̄ term in the log surface pres equation

  • L0::Vector{NF} where NF<:AbstractFloat: components to construct L, 1/ 2Δσ

  • L1::Matrix{NF} where NF<:AbstractFloat: vert advection term in the temperature equation (below+above)

  • L2::Vector{NF} where NF<:AbstractFloat: factor in front of the divsumabove term

  • L3::Matrix{NF} where NF<:AbstractFloat: sumabove operator itself

  • L4::Vector{NF} where NF<:AbstractFloat: factor in front of div term in Dlnps/Dt

  • S::Matrix{NF} where NF<:AbstractFloat: for every l the matrix to be inverted

  • S⁻¹::Array{NF, 3} where NF<:AbstractFloat: combined inverted operator: S = 1 - ξ²(RL + UW)

source
SpeedyWeather.ImplicitShallowWaterType

Struct that holds various precomputed arrays for the semi-implicit correction to prevent gravity waves from amplifying in the shallow water model.

  • trunc::Int64

  • α::Float64: coefficient for semi-implicit computations to filter gravity waves

  • H::Base.RefValue{NF} where NF<:AbstractFloat

  • ξH::Base.RefValue{NF} where NF<:AbstractFloat

  • g∇²::Vector{NF} where NF<:AbstractFloat

  • ξg∇²::Vector{NF} where NF<:AbstractFloat

  • S⁻¹::Vector{NF} where NF<:AbstractFloat

source
SpeedyWeather.ImplicitShallowWaterType

Struct that holds various precomputed arrays for the semi-implicit correction to prevent gravity waves from amplifying in the shallow water model.

  • trunc::Int64

  • α::Float64: coefficient for semi-implicit computations to filter gravity waves

  • H::Base.RefValue{NF} where NF<:AbstractFloat

  • ξH::Base.RefValue{NF} where NF<:AbstractFloat

  • g∇²::Vector{NF} where NF<:AbstractFloat

  • ξg∇²::Vector{NF} where NF<:AbstractFloat

  • S⁻¹::Vector{NF} where NF<:AbstractFloat

source
SpeedyWeather.JablonowskiRelaxationMethod
JablonowskiRelaxation(SG::SpectralGrid; kwargs...) -> Any
-

create a JablonowskiRelaxation temperature relaxation with arrays allocated given spectral_grid

source
SpeedyWeather.JetStreamForcingType

Forcing term for the Barotropic or ShallowWaterModel with an idealised jet stream similar to the initial conditions from Galewsky, 2004, but mirrored for both hemispheres.

  • nlat::Int64: Number of latitude rings

  • latitude::Float64: jet latitude [˚N]

  • width::Float64: jet width [˚], default ≈ 19.29˚

  • speed::Float64: jet speed scale [m/s]

  • time_scale::Float64: time scale [days]

  • amplitude::Vector: precomputed amplitude vector [m/s²]

source
SpeedyWeather.KeepbitsType

Number of mantissa bits to keep for each prognostic variable when compressed for netCDF and .jld2 data output.

  • u::Int64

  • v::Int64

  • vor::Int64

  • div::Int64

  • temp::Int64

  • pres::Int64

  • humid::Int64

  • precip_cond::Int64

  • precip_conv::Int64

source
SpeedyWeather.LandSeaMaskType

Land-sea mask, fractional, read from file.

  • path::String: path to the folder containing the land-sea mask file, pkg path default

  • file::String: filename of land sea mask

  • file_Grid::Type{<:SpeedyWeather.RingGrids.AbstractGrid}: Grid the land-sea mask file comes on

  • land_sea_mask::SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat: Land-sea mask [1] on grid-point space. Land=1, sea=0, land-area fraction in between.

source
SpeedyWeather.LandSeaMaskMethod
LandSeaMask(spectral_grid::SpectralGrid; kwargs...) -> Any
-

Generator function pulling the resolution information from spectral_grid.

source
SpeedyWeather.LeapfrogType

Leapfrog time stepping defined by the following fields

  • trunc::Int64: spectral resolution (max degree of spherical harmonics)

  • Δt_at_T31::Float64: time step in minutes for T31, scale linearly to trunc

  • radius::Any: radius of sphere [m], used for scaling

  • robert_filter::Any: Robert (1966) time filter coefficeint to suppress comput. mode

  • williams_filter::Any: Williams time filter (Amezcua 2011) coefficient for 3rd order acc

  • Δt_sec::Int64: time step Δt [s] at specified resolution

  • Δt::Any: time step Δt [s/m] at specified resolution, scaled by 1/radius

  • Δt_hrs::Float64: convert time step Δt from minutes to hours

source
SpeedyWeather.LeapfrogMethod
Leapfrog(spectral_grid::SpectralGrid; kwargs...) -> Any
-

Generator function for a Leapfrog struct using spectral_grid for the resolution information.

source
SpeedyWeather.LinearDragType

Linear boundary layer drag Following Held and Suarez, 1996 BAMS

  • σb::Float64

  • time_scale::Float64

  • nlev::Int64

  • drag_coefs::Vector{NF} where NF<:AbstractFloat

source
SpeedyWeather.MagnusCoefsType

Parameters for computing saturation vapour pressure using the August-Roche-Magnus formula,

eᵢ(T) = e₀ * exp(Cᵢ * (T - T₀) / (T - Tᵢ)),

where T is in Kelvin and i = 1,2 for saturation with respect to water and ice, respectively.

  • e₀::AbstractFloat: Saturation vapour pressure at 0°C [Pa]

  • T₀::AbstractFloat: 0°C in Kelvin

  • T₁::AbstractFloat

  • T₂::AbstractFloat

  • C₁::AbstractFloat

  • C₂::AbstractFloat

source
SpeedyWeather.NoOrographyType

Orography with zero height in orography and zero surface geopotential geopot_surf.

  • orography::SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat: height [m] on grid-point space.

  • geopot_surf::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: surface geopotential, height*gravity [m²/s²]

source
SpeedyWeather.NoOrographyMethod
NoOrography(spectral_grid::SpectralGrid) -> NoOrography
-

Generator function pulling the resolution information from spectral_grid.

source
SpeedyWeather.OutputWriterType

NetCDF output writer. Contains all output options and auxiliary fields for output interpolation. To be initialised with OutputWriter(::SpectralGrid,::Type{<:ModelSetup},kwargs...) to pass on the resolution information and the model type which chooses which variables to output. Options include

  • spectral_grid::SpectralGrid

  • output::Bool

  • path::String: [OPTION] path to output folder, run_???? will be created within

  • id::String: [OPTION] run identification number/string

  • run_path::String

  • filename::String: [OPTION] name of the output netcdf file

  • write_restart::Bool: [OPTION] also write restart file if output==true?

  • pkg_version::VersionNumber

  • startdate::Dates.DateTime

  • output_dt::Float64: [OPTION] output frequency, time step [hrs]

  • output_dt_sec::Int64: actual output time step [sec]

  • output_vars::Vector{Symbol}: [OPTION] which variables to output, u, v, vor, div, pres, temp, humid

  • missing_value::Union{Float32, Float64}: [OPTION] missing value to be used in netcdf output

  • compression_level::Int64: [OPTION] lossless compression level; 1=low but fast, 9=high but slow

  • shuffle::Bool: [OPTION] shuffle/bittranspose filter for compression

  • keepbits::SpeedyWeather.Keepbits: [OPTION] mantissa bits to keep for every variable

  • output_every_n_steps::Int64

  • timestep_counter::Int64

  • output_counter::Int64

  • netcdf_file::Union{Nothing, NCDatasets.NCDataset}

  • input_Grid::Type{<:SpeedyWeather.RingGrids.AbstractGrid}

  • as_matrix::Bool: [OPTION] sort grid points into a matrix (interpolation-free), for OctahedralClenshawGrid, OctaHEALPixGrid only

  • quadrant_rotation::NTuple{4, Int64}

  • matrix_quadrant::NTuple{4, Tuple{Int64, Int64}}

  • output_Grid::Type{<:SpeedyWeather.RingGrids.AbstractFullGrid}: [OPTION] the grid used for output, full grids only

  • nlat_half::Int64: [OPTION] the resolution of the output grid, default: same nlat_half as in the dynamical core

  • nlon::Int64

  • nlat::Int64

  • npoints::Int64

  • nlev::Int64

  • interpolator::SpeedyWeather.RingGrids.AbstractInterpolator

  • u::Matrix{NF} where NF<:Union{Float32, Float64}

  • v::Matrix{NF} where NF<:Union{Float32, Float64}

  • vor::Matrix{NF} where NF<:Union{Float32, Float64}

  • div::Matrix{NF} where NF<:Union{Float32, Float64}

  • temp::Matrix{NF} where NF<:Union{Float32, Float64}

  • pres::Matrix{NF} where NF<:Union{Float32, Float64}

  • humid::Matrix{NF} where NF<:Union{Float32, Float64}

  • precip_cond::Matrix{NF} where NF<:Union{Float32, Float64}

  • precip_conv::Matrix{NF} where NF<:Union{Float32, Float64}

source
SpeedyWeather.PrimitiveDryModelType

The PrimitiveDryModel struct holds all other structs that contain precalculated constants, whether scalars or arrays that do not change throughout model integration.

  • spectral_grid::SpectralGrid: dictates resolution for many other components

  • planet::SpeedyWeather.AbstractPlanet: contains physical and orbital characteristics

  • atmosphere::SpeedyWeather.AbstractAtmosphere

  • initial_conditions::SpeedyWeather.InitialConditions

  • orography::SpeedyWeather.AbstractOrography{NF} where NF<:AbstractFloat

  • land_sea_mask::SpeedyWeather.AbstractLandSeaMask{NF} where NF<:AbstractFloat

  • physics::Bool

  • boundary_layer_drag::SpeedyWeather.BoundaryLayerDrag{NF} where NF<:AbstractFloat

  • temperature_relaxation::SpeedyWeather.TemperatureRelaxation{NF} where NF<:AbstractFloat

  • static_energy_diffusion::SpeedyWeather.VerticalDiffusion{NF} where NF<:AbstractFloat

  • vertical_advection::SpeedyWeather.VerticalAdvection{NF} where NF<:AbstractFloat

  • time_stepping::SpeedyWeather.TimeStepper{NF} where NF<:AbstractFloat

  • spectral_transform::SpectralTransform

  • horizontal_diffusion::SpeedyWeather.HorizontalDiffusion{NF} where NF<:AbstractFloat

  • implicit::SpeedyWeather.AbstractImplicit{NF} where NF<:AbstractFloat

  • geometry::Geometry

  • constants::DynamicsConstants

  • device_setup::SpeedyWeather.DeviceSetup

  • output::SpeedyWeather.AbstractOutputWriter

  • feedback::SpeedyWeather.AbstractFeedback

source
SpeedyWeather.PrimitiveWetModelType

The PrimitiveDryModel struct holds all other structs that contain precalculated constants, whether scalars or arrays that do not change throughout model integration.

  • spectral_grid::SpectralGrid: dictates resolution for many other components

  • planet::SpeedyWeather.AbstractPlanet: contains physical and orbital characteristics

  • atmosphere::SpeedyWeather.AbstractAtmosphere

  • initial_conditions::SpeedyWeather.InitialConditions

  • orography::SpeedyWeather.AbstractOrography{NF} where NF<:AbstractFloat

  • land_sea_mask::SpeedyWeather.AbstractLandSeaMask{NF} where NF<:AbstractFloat

  • physics::Bool

  • thermodynamics::SpeedyWeather.Thermodynamics{NF} where NF<:AbstractFloat

  • boundary_layer_drag::SpeedyWeather.BoundaryLayerDrag{NF} where NF<:AbstractFloat

  • temperature_relaxation::SpeedyWeather.TemperatureRelaxation{NF} where NF<:AbstractFloat

  • static_energy_diffusion::SpeedyWeather.VerticalDiffusion{NF} where NF<:AbstractFloat

  • large_scale_condensation::SpeedyWeather.AbstractCondensation{NF} where NF<:AbstractFloat

  • vertical_advection::SpeedyWeather.VerticalAdvection{NF} where NF<:AbstractFloat

  • time_stepping::SpeedyWeather.TimeStepper{NF} where NF<:AbstractFloat

  • spectral_transform::SpectralTransform

  • horizontal_diffusion::SpeedyWeather.HorizontalDiffusion{NF} where NF<:AbstractFloat

  • implicit::SpeedyWeather.AbstractImplicit{NF} where NF<:AbstractFloat

  • geometry::Geometry

  • constants::DynamicsConstants

  • device_setup::SpeedyWeather.DeviceSetup

  • output::SpeedyWeather.AbstractOutputWriter

  • feedback::SpeedyWeather.AbstractFeedback

source
SpeedyWeather.PrognosticLayerTimestepsType

Collect the n time steps of PrognosticVariablesLayer of an n-step time integration (leapfrog=2) into a single struct.

  • timesteps::Array{SpeedyWeather.PrognosticVariablesLayer{NF}, 1} where NF<:AbstractFloat

.

source
SpeedyWeather.PrognosticSurfaceTimestepsType

Collect the n time steps of PrognosticVariablesSurface of an n-step time integration (leapfrog=2) into a single struct.

  • timesteps::Array{SpeedyWeather.PrognosticVariablesSurface{NF}, 1} where NF<:AbstractFloat

.

source
SpeedyWeather.PrognosticVariablesLayerType

A layer of the prognostic variables in spectral space.

  • trunc::Int64: Spectral resolution as max degree of spherical harmonics

  • vor::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: Vorticity of horizontal wind field [1/s]

  • div::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: Divergence of horizontal wind field [1/s]

  • temp::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: Absolute temperature [K]

  • humid::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: Specific humidity [kg/kg]

source
SpeedyWeather.PrognosticVariablesSurfaceType

The spectral and gridded prognostic variables at the surface.

  • trunc::Int64: Spectral resolution as max degree of spherical harmonics

  • pres::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: log of surface pressure [log(Pa)] for PrimitiveEquation, interface displacement [m] for ShallowWaterModel

source
SpeedyWeather.RandomWavesType

Parameters for random initial conditions for the interface displacement η in the shallow water equations.

  • A::Float64

  • lmin::Int64

  • lmax::Int64

source
SpeedyWeather.ShallowWaterModelType

The ShallowWaterModel struct holds all other structs that contain precalculated constants, whether scalars or arrays that do not change throughout model integration.

  • spectral_grid::SpectralGrid: dictates resolution for many other components

  • planet::SpeedyWeather.AbstractPlanet: contains physical and orbital characteristics

  • atmosphere::SpeedyWeather.AbstractAtmosphere

  • forcing::SpeedyWeather.AbstractForcing{NF} where NF<:AbstractFloat

  • drag::SpeedyWeather.AbstractDrag{NF} where NF<:AbstractFloat

  • initial_conditions::SpeedyWeather.InitialConditions

  • orography::SpeedyWeather.AbstractOrography{NF} where NF<:AbstractFloat

  • time_stepping::SpeedyWeather.TimeStepper{NF} where NF<:AbstractFloat

  • spectral_transform::SpectralTransform

  • horizontal_diffusion::SpeedyWeather.HorizontalDiffusion{NF} where NF<:AbstractFloat

  • implicit::SpeedyWeather.AbstractImplicit{NF} where NF<:AbstractFloat

  • geometry::Geometry

  • constants::DynamicsConstants

  • device_setup::SpeedyWeather.DeviceSetup

  • output::SpeedyWeather.AbstractOutputWriter

  • feedback::SpeedyWeather.AbstractFeedback

source
SpeedyWeather.SimulationType

Simulation is a container struct to be used with run!(::Simulation). It contains

  • prognostic_variables::PrognosticVariables: define the current state of the model

  • diagnostic_variables::DiagnosticVariables: contain the tendencies and auxiliary arrays to compute them

  • model::SpeedyWeather.ModelSetup: all parameters, constant at runtime

source
SpeedyWeather.SpectralGridType

Defines the horizontal spectral resolution and corresponding grid and the vertical coordinate for SpeedyWeather.jl. Options are

  • NF::Type{<:AbstractFloat}: number format used throughout the model

  • trunc::Int64: horizontal resolution as the maximum degree of spherical harmonics

  • Grid::Type{<:SpeedyWeather.RingGrids.AbstractGrid}: horizontal grid used for calculations in grid-point space

  • dealiasing::Float64: how to match spectral with grid resolution: dealiasing factor, 1=linear, 2=quadratic, 3=cubic grid

  • radius::Float64: radius of the sphere [m]

  • nlat_half::Int64: number of latitude rings on one hemisphere (Equator incl)

  • npoints::Int64: total number of grid points in the horizontal

  • nlev::Int64: number of vertical levels

  • vertical_coordinates::SpeedyWeather.VerticalCoordinates: coordinates used to discretize the vertical

nlat_half and npoints should not be chosen but are derived from trunc, Grid and dealiasing.

source
SpeedyWeather.SpeedyCondensationType

Large scale condensation as in Fortran SPEEDY with default values from therein.

  • nlev::Int64: number of vertical levels

  • threshold_boundary_layer::Float64: Relative humidity threshold for boundary layer

  • threshold_range::Float64: Vertical range of relative humidity threshold

  • threshold_max::Float64: Maximum relative humidity threshold [1]

  • time_scale::Float64: Relaxation time for humidity [hrs]

  • n_stratosphere_levels::Base.RefValue{Int64}

  • humid_tend_max::Vector{NF} where NF<:AbstractFloat

  • relative_threshold::Vector{NF} where NF<:AbstractFloat

source
SpeedyWeather.JablonowskiRelaxationMethod
JablonowskiRelaxation(SG::SpectralGrid; kwargs...) -> Any
+

create a JablonowskiRelaxation temperature relaxation with arrays allocated given spectral_grid

source
SpeedyWeather.JetStreamForcingType

Forcing term for the Barotropic or ShallowWaterModel with an idealised jet stream similar to the initial conditions from Galewsky, 2004, but mirrored for both hemispheres.

  • nlat::Int64: Number of latitude rings

  • latitude::Float64: jet latitude [˚N]

  • width::Float64: jet width [˚], default ≈ 19.29˚

  • speed::Float64: jet speed scale [m/s]

  • time_scale::Float64: time scale [days]

  • amplitude::Vector: precomputed amplitude vector [m/s²]

source
SpeedyWeather.KeepbitsType

Number of mantissa bits to keep for each prognostic variable when compressed for netCDF and .jld2 data output.

  • u::Int64

  • v::Int64

  • vor::Int64

  • div::Int64

  • temp::Int64

  • pres::Int64

  • humid::Int64

  • precip_cond::Int64

  • precip_conv::Int64

source
SpeedyWeather.LandSeaMaskType

Land-sea mask, fractional, read from file.

  • path::String: path to the folder containing the land-sea mask file, pkg path default

  • file::String: filename of land sea mask

  • file_Grid::Type{<:SpeedyWeather.RingGrids.AbstractGrid}: Grid the land-sea mask file comes on

  • land_sea_mask::SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat: Land-sea mask [1] on grid-point space. Land=1, sea=0, land-area fraction in between.

source
SpeedyWeather.LandSeaMaskMethod
LandSeaMask(spectral_grid::SpectralGrid; kwargs...) -> Any
+

Generator function pulling the resolution information from spectral_grid.

source
SpeedyWeather.LeapfrogType

Leapfrog time stepping defined by the following fields

  • trunc::Int64: spectral resolution (max degree of spherical harmonics)

  • Δt_at_T31::Float64: time step in minutes for T31, scale linearly to trunc

  • radius::Any: radius of sphere [m], used for scaling

  • robert_filter::Any: Robert (1966) time filter coefficeint to suppress comput. mode

  • williams_filter::Any: Williams time filter (Amezcua 2011) coefficient for 3rd order acc

  • Δt_sec::Int64: time step Δt [s] at specified resolution

  • Δt::Any: time step Δt [s/m] at specified resolution, scaled by 1/radius

  • Δt_hrs::Float64: convert time step Δt from minutes to hours

source
SpeedyWeather.LeapfrogMethod
Leapfrog(spectral_grid::SpectralGrid; kwargs...) -> Any
+

Generator function for a Leapfrog struct using spectral_grid for the resolution information.

source
SpeedyWeather.LinearDragType

Linear boundary layer drag Following Held and Suarez, 1996 BAMS

  • σb::Float64

  • time_scale::Float64

  • nlev::Int64

  • drag_coefs::Vector{NF} where NF<:AbstractFloat

source
SpeedyWeather.MagnusCoefsType

Parameters for computing saturation vapour pressure using the August-Roche-Magnus formula,

eᵢ(T) = e₀ * exp(Cᵢ * (T - T₀) / (T - Tᵢ)),

where T is in Kelvin and i = 1,2 for saturation with respect to water and ice, respectively.

  • e₀::AbstractFloat: Saturation vapour pressure at 0°C [Pa]

  • T₀::AbstractFloat: 0°C in Kelvin

  • T₁::AbstractFloat

  • T₂::AbstractFloat

  • C₁::AbstractFloat

  • C₂::AbstractFloat

source
SpeedyWeather.NoOrographyType

Orography with zero height in orography and zero surface geopotential geopot_surf.

  • orography::SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat: height [m] on grid-point space.

  • geopot_surf::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: surface geopotential, height*gravity [m²/s²]

source
SpeedyWeather.NoOrographyMethod
NoOrography(spectral_grid::SpectralGrid) -> NoOrography
+

Generator function pulling the resolution information from spectral_grid.

source
SpeedyWeather.OutputWriterType

NetCDF output writer. Contains all output options and auxiliary fields for output interpolation. To be initialised with OutputWriter(::SpectralGrid,::Type{<:ModelSetup},kwargs...) to pass on the resolution information and the model type which chooses which variables to output. Options include

  • spectral_grid::SpectralGrid

  • output::Bool

  • path::String: [OPTION] path to output folder, run_???? will be created within

  • id::String: [OPTION] run identification number/string

  • run_path::String

  • filename::String: [OPTION] name of the output netcdf file

  • write_restart::Bool: [OPTION] also write restart file if output==true?

  • pkg_version::VersionNumber

  • startdate::Dates.DateTime

  • output_dt::Float64: [OPTION] output frequency, time step [hrs]

  • output_dt_sec::Int64: actual output time step [sec]

  • output_vars::Vector{Symbol}: [OPTION] which variables to output, u, v, vor, div, pres, temp, humid

  • missing_value::Union{Float32, Float64}: [OPTION] missing value to be used in netcdf output

  • compression_level::Int64: [OPTION] lossless compression level; 1=low but fast, 9=high but slow

  • shuffle::Bool: [OPTION] shuffle/bittranspose filter for compression

  • keepbits::SpeedyWeather.Keepbits: [OPTION] mantissa bits to keep for every variable

  • output_every_n_steps::Int64

  • timestep_counter::Int64

  • output_counter::Int64

  • netcdf_file::Union{Nothing, NCDatasets.NCDataset}

  • input_Grid::Type{<:SpeedyWeather.RingGrids.AbstractGrid}

  • as_matrix::Bool: [OPTION] sort grid points into a matrix (interpolation-free), for OctahedralClenshawGrid, OctaHEALPixGrid only

  • quadrant_rotation::NTuple{4, Int64}

  • matrix_quadrant::NTuple{4, Tuple{Int64, Int64}}

  • output_Grid::Type{<:SpeedyWeather.RingGrids.AbstractFullGrid}: [OPTION] the grid used for output, full grids only

  • nlat_half::Int64: [OPTION] the resolution of the output grid, default: same nlat_half as in the dynamical core

  • nlon::Int64

  • nlat::Int64

  • npoints::Int64

  • nlev::Int64

  • interpolator::SpeedyWeather.RingGrids.AbstractInterpolator

  • u::Matrix{NF} where NF<:Union{Float32, Float64}

  • v::Matrix{NF} where NF<:Union{Float32, Float64}

  • vor::Matrix{NF} where NF<:Union{Float32, Float64}

  • div::Matrix{NF} where NF<:Union{Float32, Float64}

  • temp::Matrix{NF} where NF<:Union{Float32, Float64}

  • pres::Matrix{NF} where NF<:Union{Float32, Float64}

  • humid::Matrix{NF} where NF<:Union{Float32, Float64}

  • precip_cond::Matrix{NF} where NF<:Union{Float32, Float64}

  • precip_conv::Matrix{NF} where NF<:Union{Float32, Float64}

source
SpeedyWeather.PrimitiveDryModelType

The PrimitiveDryModel struct holds all other structs that contain precalculated constants, whether scalars or arrays that do not change throughout model integration.

  • spectral_grid::SpectralGrid: dictates resolution for many other components

  • planet::SpeedyWeather.AbstractPlanet: contains physical and orbital characteristics

  • atmosphere::SpeedyWeather.AbstractAtmosphere

  • initial_conditions::SpeedyWeather.InitialConditions

  • orography::SpeedyWeather.AbstractOrography{NF} where NF<:AbstractFloat

  • land_sea_mask::SpeedyWeather.AbstractLandSeaMask{NF} where NF<:AbstractFloat

  • physics::Bool

  • boundary_layer_drag::SpeedyWeather.BoundaryLayerDrag{NF} where NF<:AbstractFloat

  • temperature_relaxation::SpeedyWeather.TemperatureRelaxation{NF} where NF<:AbstractFloat

  • static_energy_diffusion::SpeedyWeather.VerticalDiffusion{NF} where NF<:AbstractFloat

  • vertical_advection::SpeedyWeather.VerticalAdvection{NF} where NF<:AbstractFloat

  • time_stepping::SpeedyWeather.TimeStepper{NF} where NF<:AbstractFloat

  • spectral_transform::SpectralTransform

  • horizontal_diffusion::SpeedyWeather.HorizontalDiffusion{NF} where NF<:AbstractFloat

  • implicit::SpeedyWeather.AbstractImplicit{NF} where NF<:AbstractFloat

  • geometry::Geometry

  • constants::DynamicsConstants

  • device_setup::SpeedyWeather.DeviceSetup

  • output::SpeedyWeather.AbstractOutputWriter

  • feedback::SpeedyWeather.AbstractFeedback

source
SpeedyWeather.PrimitiveWetModelType

The PrimitiveDryModel struct holds all other structs that contain precalculated constants, whether scalars or arrays that do not change throughout model integration.

  • spectral_grid::SpectralGrid: dictates resolution for many other components

  • planet::SpeedyWeather.AbstractPlanet: contains physical and orbital characteristics

  • atmosphere::SpeedyWeather.AbstractAtmosphere

  • initial_conditions::SpeedyWeather.InitialConditions

  • orography::SpeedyWeather.AbstractOrography{NF} where NF<:AbstractFloat

  • land_sea_mask::SpeedyWeather.AbstractLandSeaMask{NF} where NF<:AbstractFloat

  • physics::Bool

  • thermodynamics::SpeedyWeather.Thermodynamics{NF} where NF<:AbstractFloat

  • boundary_layer_drag::SpeedyWeather.BoundaryLayerDrag{NF} where NF<:AbstractFloat

  • temperature_relaxation::SpeedyWeather.TemperatureRelaxation{NF} where NF<:AbstractFloat

  • static_energy_diffusion::SpeedyWeather.VerticalDiffusion{NF} where NF<:AbstractFloat

  • large_scale_condensation::SpeedyWeather.AbstractCondensation{NF} where NF<:AbstractFloat

  • vertical_advection::SpeedyWeather.VerticalAdvection{NF} where NF<:AbstractFloat

  • time_stepping::SpeedyWeather.TimeStepper{NF} where NF<:AbstractFloat

  • spectral_transform::SpectralTransform

  • horizontal_diffusion::SpeedyWeather.HorizontalDiffusion{NF} where NF<:AbstractFloat

  • implicit::SpeedyWeather.AbstractImplicit{NF} where NF<:AbstractFloat

  • geometry::Geometry

  • constants::DynamicsConstants

  • device_setup::SpeedyWeather.DeviceSetup

  • output::SpeedyWeather.AbstractOutputWriter

  • feedback::SpeedyWeather.AbstractFeedback

source
SpeedyWeather.PrognosticLayerTimestepsType

Collect the n time steps of PrognosticVariablesLayer of an n-step time integration (leapfrog=2) into a single struct.

  • timesteps::Array{SpeedyWeather.PrognosticVariablesLayer{NF}, 1} where NF<:AbstractFloat

.

source
SpeedyWeather.PrognosticSurfaceTimestepsType

Collect the n time steps of PrognosticVariablesSurface of an n-step time integration (leapfrog=2) into a single struct.

  • timesteps::Array{SpeedyWeather.PrognosticVariablesSurface{NF}, 1} where NF<:AbstractFloat

.

source
SpeedyWeather.PrognosticVariablesLayerType

A layer of the prognostic variables in spectral space.

  • trunc::Int64: Spectral resolution as max degree of spherical harmonics

  • vor::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: Vorticity of horizontal wind field [1/s]

  • div::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: Divergence of horizontal wind field [1/s]

  • temp::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: Absolute temperature [K]

  • humid::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: Specific humidity [kg/kg]

source
SpeedyWeather.PrognosticVariablesSurfaceType

The spectral and gridded prognostic variables at the surface.

  • trunc::Int64: Spectral resolution as max degree of spherical harmonics

  • pres::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: log of surface pressure [log(Pa)] for PrimitiveEquation, interface displacement [m] for ShallowWaterModel

source
SpeedyWeather.RandomWavesType

Parameters for random initial conditions for the interface displacement η in the shallow water equations.

  • A::Float64

  • lmin::Int64

  • lmax::Int64

source
SpeedyWeather.ShallowWaterModelType

The ShallowWaterModel struct holds all other structs that contain precalculated constants, whether scalars or arrays that do not change throughout model integration.

  • spectral_grid::SpectralGrid: dictates resolution for many other components

  • planet::SpeedyWeather.AbstractPlanet: contains physical and orbital characteristics

  • atmosphere::SpeedyWeather.AbstractAtmosphere

  • forcing::SpeedyWeather.AbstractForcing{NF} where NF<:AbstractFloat

  • drag::SpeedyWeather.AbstractDrag{NF} where NF<:AbstractFloat

  • initial_conditions::SpeedyWeather.InitialConditions

  • orography::SpeedyWeather.AbstractOrography{NF} where NF<:AbstractFloat

  • time_stepping::SpeedyWeather.TimeStepper{NF} where NF<:AbstractFloat

  • spectral_transform::SpectralTransform

  • horizontal_diffusion::SpeedyWeather.HorizontalDiffusion{NF} where NF<:AbstractFloat

  • implicit::SpeedyWeather.AbstractImplicit{NF} where NF<:AbstractFloat

  • geometry::Geometry

  • constants::DynamicsConstants

  • device_setup::SpeedyWeather.DeviceSetup

  • output::SpeedyWeather.AbstractOutputWriter

  • feedback::SpeedyWeather.AbstractFeedback

source
SpeedyWeather.SimulationType

Simulation is a container struct to be used with run!(::Simulation). It contains

  • prognostic_variables::PrognosticVariables: define the current state of the model

  • diagnostic_variables::DiagnosticVariables: contain the tendencies and auxiliary arrays to compute them

  • model::SpeedyWeather.ModelSetup: all parameters, constant at runtime

source
SpeedyWeather.SpectralGridType

Defines the horizontal spectral resolution and corresponding grid and the vertical coordinate for SpeedyWeather.jl. Options are

  • NF::Type{<:AbstractFloat}: number format used throughout the model

  • trunc::Int64: horizontal resolution as the maximum degree of spherical harmonics

  • Grid::Type{<:SpeedyWeather.RingGrids.AbstractGrid}: horizontal grid used for calculations in grid-point space

  • dealiasing::Float64: how to match spectral with grid resolution: dealiasing factor, 1=linear, 2=quadratic, 3=cubic grid

  • radius::Float64: radius of the sphere [m]

  • nlat_half::Int64: number of latitude rings on one hemisphere (Equator incl)

  • npoints::Int64: total number of grid points in the horizontal

  • nlev::Int64: number of vertical levels

  • vertical_coordinates::SpeedyWeather.VerticalCoordinates: coordinates used to discretize the vertical

nlat_half and npoints should not be chosen but are derived from trunc, Grid and dealiasing.

source
SpeedyWeather.SpeedyCondensationType

Large scale condensation as in Fortran SPEEDY with default values from therein.

  • nlev::Int64: number of vertical levels

  • threshold_boundary_layer::Float64: Relative humidity threshold for boundary layer

  • threshold_range::Float64: Vertical range of relative humidity threshold

  • threshold_max::Float64: Maximum relative humidity threshold [1]

  • time_scale::Float64: Relaxation time for humidity [hrs]

  • n_stratosphere_levels::Base.RefValue{Int64}

  • humid_tend_max::Vector{NF} where NF<:AbstractFloat

  • relative_threshold::Vector{NF} where NF<:AbstractFloat

source
SpeedyWeather.SpeedyTransforms.SpectralTransformMethod
SpectralTransform(
     spectral_grid::SpectralGrid;
     recompute_legendre,
     one_more_degree,
     kwargs...
 ) -> SpectralTransform
-

Generator function for a SpectralTransform struct pulling in parameters from a SpectralGrid struct.

source
SpeedyWeather.StartFromFileType

Restart from a previous SpeedyWeather.jl simulation via the restart file restart.jld2 Applies interpolation in the horizontal but not in the vertical. restart.jld2 is identified by

  • path::String: path for restart file

  • id::Union{Int64, String}: run_id of restart file in run_????/restart.jld2

source
SpeedyWeather.StartWithRandomVorticityType

Start with random vorticity as initial conditions

  • power::Float64: Power of the spectral distribution k^power

  • amplitude::Float64: (approximate) amplitude in [1/s], used as standard deviation of spherical harmonic coefficients

source
SpeedyWeather.StaticEnergyDiffusionType

Diffusion of dry static energy: A relaxation towards a reference gradient of static energy wrt to geopotential, see Fortran SPEEDY documentation.

  • time_scale::Float64: time scale [hrs] for strength

  • static_energy_lapse_rate::Float64: [1] ∂SE/∂Φ, vertical gradient of static energy SE with geopotential Φ

  • Fstar::Base.RefValue{NF} where NF<:AbstractFloat

source
SpeedyWeather.TendenciesType
Tendencies{Grid<:AbstractGrid,NF<:AbstractFloat}

Struct holding the tendencies of the prognostic spectral variables for a given layer.

source
SpeedyWeather.ZonalJetType

Create a struct that contains all parameters for the Galewsky et al, 2004 zonal jet intitial conditions for the shallow water model. Default values as in Galewsky.

  • latitude::Float64: jet latitude [˚N]

  • width::Float64: jet width [˚], default ≈ 19.29˚

  • umax::Float64: jet maximum velocity [m/s]

  • perturb_lat::Float64: perturbation latitude [˚N], position in jet by default

  • perturb_lon::Float64: perturbation longitude [˚E]

  • perturb_xwidth::Float64: perturbation zonal extent [˚], default ≈ 19.1˚

  • perturb_ywidth::Float64: perturbation meridinoal extent [˚], default ≈ 3.8˚

  • perturb_height::Float64: perturbation amplitude [m]

source
SpeedyWeather.ZonalRidgeType

Zonal ridge orography after Jablonowski and Williamson, 2006.

  • η₀::Float64: conversion from σ to Jablonowski's ηᵥ-coordinates

  • u₀::Float64: max amplitude of zonal wind [m/s] that scales orography height

  • orography::SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat: height [m] on grid-point space.

  • geopot_surf::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: surface geopotential, height*gravity [m²/s²]

source
SpeedyWeather.ZonalRidgeMethod
ZonalRidge(spectral_grid::SpectralGrid; kwargs...) -> Any
-

Generator function pulling the resolution information from spectral_grid.

source
SpeedyWeather.ZonalWindType

Create a struct that contains all parameters for the Jablonowski and Williamson, 2006 intitial conditions for the primitive equation model. Default values as in Jablonowski.

  • η₀::Float64: conversion from σ to Jablonowski's ηᵥ-coordinates

  • u₀::Float64: max amplitude of zonal wind [m/s]

  • perturb_lat::Float64: perturbation centred at [˚N]

  • perturb_lon::Float64: perturbation centred at [˚E]

  • perturb_uₚ::Float64: perturbation strength [m/s]

  • perturb_radius::Float64: radius of Gaussian perturbation in units of Earth's radius [1]

  • ΔT::Float64: temperature difference used for stratospheric lapse rate [K], Jablonowski uses ΔT = 4.8e5 [K]

  • Tmin::Float64: minimum temperature [K] of profile

  • pressure_on_orography::Bool: initialize pressure given the atmosphere.lapse_rate on orography?

source
Base.copy!Method
copy!(progn_new::PrognosticVariables, progn_old::PrognosticVariables)

Copies entries of progn_old into progn_new. Only copies those variables that are present in the model of both progn_new and progn_old.

source
SpeedyWeather.DeviceMethod
Device()

Return default used device for internal purposes, either CPUDevice or GPUDevice if a GPU is available.

source
SpeedyWeather.DeviceArrayMethod
DeviceArray(device::AbstractDevice, x)

Adapts x to a CuArray when device<:GPUDevice is used, otherwise a regular Array. Uses adapt, thus also can return SubArrays etc.

source
SpeedyWeather.DeviceArrayNotAdaptMethod
DeviceArrayNotAdapt(device::AbstractDevice, x)

Returns a CuArray when device<:GPUDevice is used, otherwise a regular Array. Doesn't uses adapt, therefore always returns CuArray/Array

source
SpeedyWeather.StartFromFileType

Restart from a previous SpeedyWeather.jl simulation via the restart file restart.jld2 Applies interpolation in the horizontal but not in the vertical. restart.jld2 is identified by

  • path::String: path for restart file

  • id::Union{Int64, String}: run_id of restart file in run_????/restart.jld2

source
SpeedyWeather.StartWithRandomVorticityType

Start with random vorticity as initial conditions

  • power::Float64: Power of the spectral distribution k^power

  • amplitude::Float64: (approximate) amplitude in [1/s], used as standard deviation of spherical harmonic coefficients

source
SpeedyWeather.StaticEnergyDiffusionType

Diffusion of dry static energy: A relaxation towards a reference gradient of static energy wrt to geopotential, see Fortran SPEEDY documentation.

  • time_scale::Float64: time scale [hrs] for strength

  • static_energy_lapse_rate::Float64: [1] ∂SE/∂Φ, vertical gradient of static energy SE with geopotential Φ

  • Fstar::Base.RefValue{NF} where NF<:AbstractFloat

source
SpeedyWeather.TendenciesType
Tendencies{Grid<:AbstractGrid,NF<:AbstractFloat}

Struct holding the tendencies of the prognostic spectral variables for a given layer.

source
SpeedyWeather.ZonalJetType

Create a struct that contains all parameters for the Galewsky et al, 2004 zonal jet intitial conditions for the shallow water model. Default values as in Galewsky.

  • latitude::Float64: jet latitude [˚N]

  • width::Float64: jet width [˚], default ≈ 19.29˚

  • umax::Float64: jet maximum velocity [m/s]

  • perturb_lat::Float64: perturbation latitude [˚N], position in jet by default

  • perturb_lon::Float64: perturbation longitude [˚E]

  • perturb_xwidth::Float64: perturbation zonal extent [˚], default ≈ 19.1˚

  • perturb_ywidth::Float64: perturbation meridinoal extent [˚], default ≈ 3.8˚

  • perturb_height::Float64: perturbation amplitude [m]

source
SpeedyWeather.ZonalRidgeType

Zonal ridge orography after Jablonowski and Williamson, 2006.

  • η₀::Float64: conversion from σ to Jablonowski's ηᵥ-coordinates

  • u₀::Float64: max amplitude of zonal wind [m/s] that scales orography height

  • orography::SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat: height [m] on grid-point space.

  • geopot_surf::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: surface geopotential, height*gravity [m²/s²]

source
SpeedyWeather.ZonalRidgeMethod
ZonalRidge(spectral_grid::SpectralGrid; kwargs...) -> Any
+

Generator function pulling the resolution information from spectral_grid.

source
SpeedyWeather.ZonalWindType

Create a struct that contains all parameters for the Jablonowski and Williamson, 2006 intitial conditions for the primitive equation model. Default values as in Jablonowski.

  • η₀::Float64: conversion from σ to Jablonowski's ηᵥ-coordinates

  • u₀::Float64: max amplitude of zonal wind [m/s]

  • perturb_lat::Float64: perturbation centred at [˚N]

  • perturb_lon::Float64: perturbation centred at [˚E]

  • perturb_uₚ::Float64: perturbation strength [m/s]

  • perturb_radius::Float64: radius of Gaussian perturbation in units of Earth's radius [1]

  • ΔT::Float64: temperature difference used for stratospheric lapse rate [K], Jablonowski uses ΔT = 4.8e5 [K]

  • Tmin::Float64: minimum temperature [K] of profile

  • pressure_on_orography::Bool: initialize pressure given the atmosphere.lapse_rate on orography?

source
Base.copy!Method
copy!(progn_new::PrognosticVariables, progn_old::PrognosticVariables)

Copies entries of progn_old into progn_new. Only copies those variables that are present in the model of both progn_new and progn_old.

source
SpeedyWeather.DeviceMethod
Device()

Return default used device for internal purposes, either CPUDevice or GPUDevice if a GPU is available.

source
SpeedyWeather.DeviceArrayMethod
DeviceArray(device::AbstractDevice, x)

Adapts x to a CuArray when device<:GPUDevice is used, otherwise a regular Array. Uses adapt, thus also can return SubArrays etc.

source
SpeedyWeather.DeviceArrayNotAdaptMethod
DeviceArrayNotAdapt(device::AbstractDevice, x)

Returns a CuArray when device<:GPUDevice is used, otherwise a regular Array. Doesn't uses adapt, therefore always returns CuArray/Array

source
SpeedyWeather.SpeedyTransforms.gridded!Method
gridded!(
     diagn::DiagnosticVariables,
     progn::PrognosticVariables,
     lf::Int64,
     model::SpeedyWeather.ModelSetup
 )
-

Propagate the spectral state of progn to diagn using time step/leapfrog index lf. Function barrier that calls gridded! for the respective model.

source
SpeedyWeather.SpeedyTransforms.gridded!Method
gridded!(
     diagn::SpeedyWeather.DiagnosticVariablesLayer,
     progn::SpeedyWeather.PrognosticLayerTimesteps,
     lf::Int64,
     model::Barotropic
 )
-

Propagate the spectral state of the prognostic variables progn to the diagnostic variables in diagn for the barotropic vorticity model. Updates grid vorticity, spectral stream function and spectral and grid velocities u,v.

source
SpeedyWeather.SpeedyTransforms.gridded!Method
gridded!(
+

Propagate the spectral state of the prognostic variables progn to the diagnostic variables in diagn for the barotropic vorticity model. Updates grid vorticity, spectral stream function and spectral and grid velocities u,v.

source
SpeedyWeather.SpeedyTransforms.gridded!Method
gridded!(
     diagn::SpeedyWeather.DiagnosticVariablesLayer,
     progn::SpeedyWeather.PrognosticLayerTimesteps,
     lf::Int64,
     model::PrimitiveEquation
 )
-

Propagate the spectral state of the prognostic variables progn to the diagnostic variables in diagn for primitive equation models. Updates grid vorticity, grid divergence, grid temperature, pressure (pres_grid) and the velocities u,v.

source
SpeedyWeather.SpeedyTransforms.gridded!Method
gridded!(
+

Propagate the spectral state of the prognostic variables progn to the diagnostic variables in diagn for primitive equation models. Updates grid vorticity, grid divergence, grid temperature, pressure (pres_grid) and the velocities u,v.

source
SpeedyWeather.SpeedyTransforms.gridded!Method
gridded!(
     diagn::SpeedyWeather.DiagnosticVariablesLayer,
     progn::SpeedyWeather.PrognosticLayerTimesteps,
     lf::Int64,
     model::ShallowWater
 )
-

Propagate the spectral state of the prognostic variables progn to the diagnostic variables in diagn for the shallow water model. Updates grid vorticity, grid divergence, grid interface displacement (pres_grid) and the velocities u,v.

source
SpeedyWeather.bernoulli_potential!Method
bernoulli_potential!(
+

Propagate the spectral state of the prognostic variables progn to the diagnostic variables in diagn for the shallow water model. Updates grid vorticity, grid divergence, grid interface displacement (pres_grid) and the velocities u,v.

source
SpeedyWeather.bernoulli_potential!Method
bernoulli_potential!(
     diagn::SpeedyWeather.DiagnosticVariablesLayer{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF},
     S::SpectralTransform
 ) -> LowerTriangularMatrix{Complex{NF}} where NF
-

Computes the Laplace operator ∇² of the Bernoulli potential B in spectral space.

  1. computes the kinetic energy KE = ½(u²+v²) on the grid
  2. transforms KE to spectral space
  3. adds geopotential for the Bernoulli potential in spectral space
  4. takes the Laplace operator.

This version is used for both ShallowWater and PrimitiveEquation, only the geopotential calculation in geopotential! differs.

source
SpeedyWeather.boundary_layer_drag!Method
boundary_layer_drag!(
+

Computes the Laplace operator ∇² of the Bernoulli potential B in spectral space.

  1. computes the kinetic energy KE = ½(u²+v²) on the grid
  2. transforms KE to spectral space
  3. adds geopotential for the Bernoulli potential in spectral space
  4. takes the Laplace operator.

This version is used for both ShallowWater and PrimitiveEquation, only the geopotential calculation in geopotential! differs.

source
SpeedyWeather.boundary_layer_drag!Method
boundary_layer_drag!(
     column::ColumnVariables,
     scheme::LinearDrag
 )
-

Compute tendency for boundary layer drag of a column and add to its tendencies fields

source
SpeedyWeather.coriolisMethod
coriolis(
     grid::SpeedyWeather.RingGrids.AbstractGrid;
     rotation
 ) -> Any
-

Return the Coriolis parameter f on a grid like grid on a planet of ratation [1/s]. Default rotation of Earth.

source
SpeedyWeather.coriolisMethod
coriolis(
+

Return the Coriolis parameter f on a grid like grid on a planet of ratation [1/s]. Default rotation of Earth.

source
SpeedyWeather.coriolisMethod
coriolis(
     grid::SpeedyWeather.RingGrids.AbstractGrid;
     rotation
 ) -> Any
-

Return the Coriolis parameter f on the grid Grid of resolution nlat_half on a planet of ratation [1/s]. Default rotation of Earth.

source
SpeedyWeather.create_output_folderMethod
create_output_folder(
+

Return the Coriolis parameter f on the grid Grid of resolution nlat_half on a planet of ratation [1/s]. Default rotation of Earth.

source
SpeedyWeather.create_output_folderMethod
create_output_folder(
     path::String,
     id::Union{Int64, String}
 ) -> String
-

Creates a new folder run_* with the identification id. Also returns the full path run_path of that folder.

source
SpeedyWeather.default_sigma_coordinatesMethod
default_sigma_coordinates(nlev::Integer) -> Any
-

Vertical sigma coordinates defined by their nlev+1 half levels σ_levels_half. Sigma coordinates are fraction of surface pressure (p/p0) and are sorted from top (stratosphere) to bottom (surface). The first half level is at 0 the last at 1. Evaluate a generalised logistic function with coefficients in P for the distribution of values in between. Default coefficients follow the L31 configuration historically used at ECMWF.

source
SpeedyWeather.drag!Method
drag!(
+

Creates a new folder run_* with the identification id. Also returns the full path run_path of that folder.

source
SpeedyWeather.default_sigma_coordinatesMethod
default_sigma_coordinates(nlev::Integer) -> Any
+

Vertical sigma coordinates defined by their nlev+1 half levels σ_levels_half. Sigma coordinates are fraction of surface pressure (p/p0) and are sorted from top (stratosphere) to bottom (surface). The first half level is at 0 the last at 1. Evaluate a generalised logistic function with coefficients in P for the distribution of values in between. Default coefficients follow the L31 configuration historically used at ECMWF.

source
SpeedyWeather.drag!Method
drag!(
     diagn::SpeedyWeather.DiagnosticVariablesLayer,
     drag::QuadraticDrag{NF},
     C::DynamicsConstants
 )
-

Quadratic drag for the momentum equations.

F = -c_D/H*|(u,v)|*(u,v)

with c_D the non-dimensional drag coefficient as defined in drag::QuadraticDrag. Layer thickness H is taken from the Atmosphere via DynamicsConstants, as defined for the ShallowWaterModel.

source
SpeedyWeather.dry_static_energy!Method
dry_static_energy!(
+

Quadratic drag for the momentum equations.

F = -c_D/H*|(u,v)|*(u,v)

with c_D the non-dimensional drag coefficient as defined in drag::QuadraticDrag. Layer thickness H is taken from the Atmosphere via DynamicsConstants, as defined for the ShallowWaterModel.

source
SpeedyWeather.dry_static_energy!Method
dry_static_energy!(
     column::ColumnVariables,
     constants::DynamicsConstants
 )
-

Compute the dry static energy SE = cₚT + Φ (latent heat times temperature plus geopotential) for the column.

source
SpeedyWeather.dynamics_tendencies!Function
dynamics_tendencies!(
     diagn::DiagnosticVariables,
     progn::PrognosticVariables,
     model::PrimitiveEquation
@@ -107,25 +107,25 @@
     model::PrimitiveEquation,
     lf::Int64
 ) -> Any
-

Calculate all tendencies for the PrimitiveEquation model (wet or dry).

source
SpeedyWeather.dynamics_tendencies!Method
dynamics_tendencies!(
     diagn::SpeedyWeather.DiagnosticVariablesLayer,
     time::Dates.DateTime,
     model::Barotropic
 )
-

Calculate all tendencies for the BarotropicModel.

source
SpeedyWeather.dynamics_tendencies!Method
dynamics_tendencies!(
     diagn::SpeedyWeather.DiagnosticVariablesLayer,
     surface::SpeedyWeather.SurfaceVariables,
     pres::LowerTriangularMatrix,
     time::Dates.DateTime,
     model::ShallowWater
 )
-

Calculate all tendencies for the ShallowWaterModel.

source
SpeedyWeather.first_timesteps!Method
first_timesteps!(
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::SpeedyWeather.ModelSetup,
     output::SpeedyWeather.AbstractOutputWriter
 ) -> typeof(time)
-

Performs the first two initial time steps (Euler forward, unfiltered leapfrog) to populate the prognostic variables with two time steps (t=0,Δt) that can then be used in the normal leap frogging.

source
SpeedyWeather.flux_divergence!Method
flux_divergence!(
+

Performs the first two initial time steps (Euler forward, unfiltered leapfrog) to populate the prognostic variables with two time steps (t=0,Δt) that can then be used in the normal leap frogging.

source
SpeedyWeather.flux_divergence!Method
flux_divergence!(
     A_tend::LowerTriangularMatrix{Complex{NF}},
     A_grid::SpeedyWeather.RingGrids.AbstractGrid{NF},
     diagn::SpeedyWeather.DiagnosticVariablesLayer{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF},
@@ -134,27 +134,27 @@
     add,
     flipsign
 )
-

Computes ∇⋅((u,v)*A) with the option to add/overwrite A_tend and to flip_sign of the flux divergence by doing so.

  • A_tend = ∇⋅((u,v)*A) for add=false, flip_sign=false
  • A_tend = -∇⋅((u,v)*A) for add=false, flip_sign=true
  • A_tend += ∇⋅((u,v)*A) for add=true, flip_sign=false
  • A_tend -= ∇⋅((u,v)*A) for add=true, flip_sign=true
source
SpeedyWeather.fluxes_to_tendencies!Method
fluxes_to_tendencies!(
+

Computes ∇⋅((u,v)*A) with the option to add/overwrite A_tend and to flip_sign of the flux divergence by doing so.

  • A_tend = ∇⋅((u,v)*A) for add=false, flip_sign=false
  • A_tend = -∇⋅((u,v)*A) for add=false, flip_sign=true
  • A_tend += ∇⋅((u,v)*A) for add=true, flip_sign=false
  • A_tend -= ∇⋅((u,v)*A) for add=true, flip_sign=true
source
SpeedyWeather.fluxes_to_tendencies!Method
fluxes_to_tendencies!(
     column::ColumnVariables,
     geometry::Geometry,
     constants::DynamicsConstants
 )
-

Convert the fluxes on half levels to tendencies on full levels.

source
SpeedyWeather.forcing!Method
forcing!(
     diagn::SpeedyWeather.DiagnosticVariablesLayer,
     forcing::JetStreamForcing
 )
-

Set for every latitude ring the tendency to the precomputed forcing in the momentum equations following the JetStreamForcing. The forcing is precomputed in initialize!(::JetStreamForcing,::ModelSetup).

source
SpeedyWeather.geopotential!Method
geopotential!(
+

Set for every latitude ring the tendency to the precomputed forcing in the momentum equations following the JetStreamForcing. The forcing is precomputed in initialize!(::JetStreamForcing,::ModelSetup).

source
SpeedyWeather.geopotential!Method
geopotential!(
     diagn::DiagnosticVariables,
     O::SpeedyWeather.AbstractOrography,
     C::DynamicsConstants
 )
-

Compute spectral geopotential geopot from spectral temperature temp and spectral surface geopotential geopot_surf (orography*gravity).

source
SpeedyWeather.geopotential!Method
geopotential!(
+

Compute spectral geopotential geopot from spectral temperature temp and spectral surface geopotential geopot_surf (orography*gravity).

source
SpeedyWeather.geopotential!Method
geopotential!(
     diagn::SpeedyWeather.DiagnosticVariablesLayer,
     pres::LowerTriangularMatrix,
     C::DynamicsConstants
 ) -> Any
-

calculates the geopotential in the ShallowWaterModel as g*η, i.e. gravity times the interface displacement (field pres)

source
SpeedyWeather.geopotential!Method
geopotential!(temp::Vector, C::DynamicsConstants) -> Vector
-

Calculate the geopotential based on temp in a single column. This exclues the surface geopotential that would need to be added to the returned vector. Function not used in the dynamical core but for post-processing and analysis.

source
SpeedyWeather.get_column!Method
get_column!(
+

calculates the geopotential in the ShallowWaterModel as g*η, i.e. gravity times the interface displacement (field pres)

source
SpeedyWeather.geopotential!Method
geopotential!(temp::Vector, C::DynamicsConstants) -> Vector
+

Calculate the geopotential based on temp in a single column. This exclues the surface geopotential that would need to be added to the returned vector. Function not used in the dynamical core but for post-processing and analysis.

source
SpeedyWeather.get_column!Method
get_column!(
     C::ColumnVariables,
     D::DiagnosticVariables,
     ij::Integer,
@@ -162,32 +162,21 @@
     G::Geometry,
     L::SpeedyWeather.AbstractLandSeaMask
 )
-

Update C::ColumnVariables by copying the prognostic variables from D::DiagnosticVariables at gridpoint index ij. Provide G::Geometry for coordinate information.

source
SpeedyWeather.get_run_idMethod
get_run_id(path::String, id::String) -> String
-

Checks existing run_???? folders in path to determine a 4-digit id number by counting up. E.g. if folder run_0001 exists it will return the string "0002". Does not create a folder for the returned run id.

source
SpeedyWeather.get_thermodynamics!Method
get_thermodynamics!(
+

Update C::ColumnVariables by copying the prognostic variables from D::DiagnosticVariables at gridpoint index ij. Provide G::Geometry for coordinate information.

source
SpeedyWeather.get_run_idMethod
get_run_id(path::String, id::String) -> String
+

Checks existing run_???? folders in path to determine a 4-digit id number by counting up. E.g. if folder run_0001 exists it will return the string "0002". Does not create a folder for the returned run id.

source
SpeedyWeather.get_thermodynamics!Method
get_thermodynamics!(
     column::ColumnVariables,
     model::PrimitiveWet
 )
-

Calculate thermodynamic quantities like saturation vapour pressure, saturation specific humidity, dry static energy, moist static energy and saturation moist static energy from the prognostic column variables.

source
SpeedyWeather.get_varMethod
get_var(progn::PrognosticVariables, var_name::Symbol; lf::Integer=1)

Returns the prognostic variable var_name at leapfrog index lf as a Vector{LowerTriangularMatrices}.

source
SpeedyWeather.hasMethod
has(
+

Calculate thermodynamic quantities like saturation vapour pressure, saturation specific humidity, dry static energy, moist static energy and saturation moist static energy from the prognostic column variables.

source
SpeedyWeather.get_varMethod
get_var(progn::PrognosticVariables, var_name::Symbol; lf::Integer=1)

Returns the prognostic variable var_name at leapfrog index lf as a Vector{LowerTriangularMatrices}.

source
SpeedyWeather.hasMethod
has(
     M::Type{<:SpeedyWeather.ModelSetup},
     var_name::Symbol
 ) -> Bool
-

Returns true if the model M has a prognostic variable var_name, false otherwise. The default fallback is that all variables are included.

source
SpeedyWeather.horizontal_diffusion!Function
horizontal_diffusion!(
-    diagn::SpeedyWeather.DiagnosticVariablesLayer,
-    progn::SpeedyWeather.PrognosticLayerTimesteps,
-    model::Barotropic
-)
-horizontal_diffusion!(
-    diagn::SpeedyWeather.DiagnosticVariablesLayer,
-    progn::SpeedyWeather.PrognosticLayerTimesteps,
-    model::Barotropic,
-    lf::Int64
-)
-

Apply horizontal diffusion to vorticity in the Barotropic models.

source
SpeedyWeather.horizontal_diffusion!Function
horizontal_diffusion!(
+

Returns true if the model M has a prognostic variable var_name, false otherwise. The default fallback is that all variables are included.

source
SpeedyWeather.horizontal_diffusion!Function
horizontal_diffusion!(
     progn::SpeedyWeather.PrognosticLayerTimesteps,
     diagn::SpeedyWeather.DiagnosticVariablesLayer,
     model::ShallowWater
@@ -198,7 +187,7 @@
     model::ShallowWater,
     lf::Int64
 )
-

Apply horizontal diffusion to vorticity and diffusion in the ShallowWater models.

source
SpeedyWeather.horizontal_diffusion!Function
horizontal_diffusion!(
     progn::SpeedyWeather.PrognosticLayerTimesteps,
     diagn::SpeedyWeather.DiagnosticVariablesLayer,
     model::PrimitiveEquation
@@ -209,25 +198,36 @@
     model::PrimitiveEquation,
     lf::Int64
 ) -> Union{Nothing, Bool}
-

Apply horizontal diffusion applied to vorticity, diffusion and temperature in the PrimitiveEquation models. Uses the constant diffusion for temperature but possibly adaptive diffusion for vorticity and divergence.

source
SpeedyWeather.horizontal_diffusion!Method
horizontal_diffusion!(
+

Apply horizontal diffusion applied to vorticity, diffusion and temperature in the PrimitiveEquation models. Uses the constant diffusion for temperature but possibly adaptive diffusion for vorticity and divergence.

source
SpeedyWeather.horizontal_diffusion!Function
horizontal_diffusion!(
+    diagn::SpeedyWeather.DiagnosticVariablesLayer,
+    progn::SpeedyWeather.PrognosticLayerTimesteps,
+    model::Barotropic
+)
+horizontal_diffusion!(
+    diagn::SpeedyWeather.DiagnosticVariablesLayer,
+    progn::SpeedyWeather.PrognosticLayerTimesteps,
+    model::Barotropic,
+    lf::Int64
+)
+

Apply horizontal diffusion to vorticity in the Barotropic models.

source
SpeedyWeather.horizontal_diffusion!Method
horizontal_diffusion!(
     tendency::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},
     A::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},
     ∇²ⁿ_expl::AbstractArray{NF<:AbstractFloat, 1},
     ∇²ⁿ_impl::AbstractArray{NF<:AbstractFloat, 1}
 )
-

Apply horizontal diffusion to a 2D field A in spectral space by updating its tendency tendency with an implicitly calculated diffusion term. The implicit diffusion of the next time step is split into an explicit part ∇²ⁿ_expl and an implicit part ∇²ⁿ_impl, such that both can be calculated in a single forward step by using A as well as its tendency tendency.

source
SpeedyWeather.implicit_correction!Method
implicit_correction!(
+

Apply horizontal diffusion to a 2D field A in spectral space by updating its tendency tendency with an implicitly calculated diffusion term. The implicit diffusion of the next time step is split into an explicit part ∇²ⁿ_expl and an implicit part ∇²ⁿ_impl, such that both can be calculated in a single forward step by using A as well as its tendency tendency.

source
SpeedyWeather.implicit_correction!Method
implicit_correction!(
     diagn::DiagnosticVariables,
     implicit::ImplicitPrimitiveEq,
     progn::PrognosticVariables
 ) -> Any
-

Apply the implicit corrections to dampen gravity waves in the primitive equation models.

source
SpeedyWeather.implicit_correction!Method
implicit_correction!(
     diagn::SpeedyWeather.DiagnosticVariablesLayer{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF},
     progn::SpeedyWeather.PrognosticLayerTimesteps{NF},
     diagn_surface::SpeedyWeather.SurfaceVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF},
     progn_surface::SpeedyWeather.PrognosticSurfaceTimesteps{NF},
     implicit::ImplicitShallowWater
 )
-

Apply correction to the tendencies in diagn to prevent the gravity waves from amplifying. The correction is implicitly evaluated using the parameter implicit.α to switch between forward, centered implicit or backward evaluation of the gravity wave terms.

source
SpeedyWeather.initialize!Function
initialize!(
+

Apply correction to the tendencies in diagn to prevent the gravity waves from amplifying. The correction is implicitly evaluated using the parameter implicit.α to switch between forward, centered implicit or backward evaluation of the gravity wave terms.

source
SpeedyWeather.initialize!Function
initialize!(
     scheme::HyperDiffusion,
     k::Int64,
     G::Geometry,
@@ -240,82 +240,82 @@
     L::SpeedyWeather.TimeStepper,
     vor_max::Real
 )
-

Precomputes the hyper diffusion terms in scheme for layer k based on the model time step in L, the vertical level sigma level in G, and the current (absolute) vorticity maximum level vor_max

source
SpeedyWeather.initialize!Method
initialize!(model::Barotropic) -> SpeedyWeather.Simulation
-

Calls all initialize! functions for components of model, except for model.output and model.feedback which are always called at in time_stepping!.

source
SpeedyWeather.initialize!Method
initialize!(
+

Precomputes the hyper diffusion terms in scheme for layer k based on the model time step in L, the vertical level sigma level in G, and the current (absolute) vorticity maximum level vor_max

source
SpeedyWeather.initialize!Method
initialize!(model::Barotropic) -> SpeedyWeather.Simulation
+

Calls all initialize! functions for components of model, except for model.output and model.feedback which are always called at in time_stepping!.

source
SpeedyWeather.initialize!Method
initialize!(
     orog::EarthOrography,
     P::SpeedyWeather.AbstractPlanet,
     S::SpectralTransform,
     G::Geometry
 ) -> LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat
-

Initialize the arrays orography,geopot_surf in orog by reading the orography field from file.

source
SpeedyWeather.initialize!Method
initialize!(
     feedback::Feedback,
     clock::SpeedyWeather.Clock,
     model::SpeedyWeather.ModelSetup
 ) -> Union{Nothing, IOStream}
-

Initializes the a Feedback struct.

source
SpeedyWeather.initialize!Method
initialize!(scheme::HeldSuarez, model::PrimitiveEquation)
-

initialize the HeldSuarez temperature relaxation by precomputing terms for the equilibrium temperature Teq.

source
SpeedyWeather.initialize!Method
initialize!(scheme::HeldSuarez, model::PrimitiveEquation)
+

initialize the HeldSuarez temperature relaxation by precomputing terms for the equilibrium temperature Teq.

source
SpeedyWeather.initialize!Method
initialize!(
     scheme::HyperDiffusion,
     diagn::SpeedyWeather.DiagnosticVariablesLayer,
     G::Geometry,
     L::SpeedyWeather.TimeStepper
 )
-

Pre-function to other initialize!(::HyperDiffusion) initialisors that calculates the (absolute) vorticity maximum for the layer of diagn.

source
SpeedyWeather.initialize!Method
initialize!(
+

Pre-function to other initialize!(::HyperDiffusion) initialisors that calculates the (absolute) vorticity maximum for the layer of diagn.

source
SpeedyWeather.initialize!Method
initialize!(
     scheme::HyperDiffusion,
     model::SpeedyWeather.ModelSetup
 )
-

Precomputes the hyper diffusion terms in scheme based on the model time step, and possibly with a changing strength/power in the vertical.

source
SpeedyWeather.initialize!Method
initialize!(
+

Precomputes the hyper diffusion terms in scheme based on the model time step, and possibly with a changing strength/power in the vertical.

source
SpeedyWeather.initialize!Method
initialize!(
     scheme::HyperDiffusion,
     L::SpeedyWeather.TimeStepper
 )
-

Precomputes the 2D hyper diffusion terms in scheme based on the model time step.

source
SpeedyWeather.initialize!Method
initialize!(
     implicit::ImplicitPrimitiveEq,
     dt::Real,
     diagn::DiagnosticVariables,
     geometry::Geometry,
     constants::DynamicsConstants
 )
-

Initialize the implicit terms for the PrimitiveEquation models.

source
SpeedyWeather.initialize!Method
initialize!(
     implicit::ImplicitShallowWater,
     dt::Real,
     constants::DynamicsConstants
 )
-

Update the implicit terms in implicit for the shallow water model as they depend on the time step dt.

source
SpeedyWeather.initialize!Method
initialize!(
+

Update the implicit terms in implicit for the shallow water model as they depend on the time step dt.

source
SpeedyWeather.initialize!Method
initialize!(
     scheme::JablonowskiRelaxation,
     model::PrimitiveEquation
 )
-

initialize the JablonowskiRelaxation temperature relaxation by precomputing terms for the equilibrium temperature Teq and the frequency (strength of relaxation).

source
SpeedyWeather.initialize!Method
initialize!(scheme::LinearDrag, model::PrimitiveEquation)
-

Precomputes the drag coefficients for this BoundaryLayerDrag scheme.

source
SpeedyWeather.initialize!Method
initialize!(
+

initialize the JablonowskiRelaxation temperature relaxation by precomputing terms for the equilibrium temperature Teq and the frequency (strength of relaxation).

source
SpeedyWeather.initialize!Method
initialize!(scheme::LinearDrag, model::PrimitiveEquation)
+

Precomputes the drag coefficients for this BoundaryLayerDrag scheme.

source
SpeedyWeather.initialize!Method
initialize!(
     scheme::NoTemperatureRelaxation,
     model::PrimitiveEquation
 )
-

just passes, does not need any initialization.

source
SpeedyWeather.initialize!Method
initialize!(model::PrimitiveDry) -> SpeedyWeather.Simulation
-

Calls all initialize! functions for components of model, except for model.output and model.feedback which are always called at in time_stepping! and model.implicit which is done in first_timesteps!.

source
SpeedyWeather.initialize!Method
initialize!(model::PrimitiveWet) -> SpeedyWeather.Simulation
-

Calls all initialize! functions for components of model, except for model.output and model.feedback which are always called at in time_stepping! and model.implicit which is done in first_timesteps!.

source
SpeedyWeather.initialize!Method
initialize!(model::PrimitiveDry) -> SpeedyWeather.Simulation
+

Calls all initialize! functions for components of model, except for model.output and model.feedback which are always called at in time_stepping! and model.implicit which is done in first_timesteps!.

source
SpeedyWeather.initialize!Method
initialize!(model::PrimitiveWet) -> SpeedyWeather.Simulation
+

Calls all initialize! functions for components of model, except for model.output and model.feedback which are always called at in time_stepping! and model.implicit which is done in first_timesteps!.

source
SpeedyWeather.initialize!Method
initialize!(
     progn_new::PrognosticVariables,
     initial_conditions::StartFromFile,
     model::SpeedyWeather.ModelSetup
 ) -> PrognosticVariables
-

Restart from a previous SpeedyWeather.jl simulation via the restart file restart.jld2 Applies interpolation in the horizontal but not in the vertical.

source
SpeedyWeather.initialize!Method
initialize!(
+

Restart from a previous SpeedyWeather.jl simulation via the restart file restart.jld2 Applies interpolation in the horizontal but not in the vertical.

source
SpeedyWeather.initialize!Method
initialize!(
     progn::PrognosticVariables,
     initial_conditions::ZonalJet,
     model::ShallowWater
 ) -> LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat
-

Initial conditions from Galewsky, 2004, Tellus

source
SpeedyWeather.initialize!Method
initialize!(model::ShallowWater) -> SpeedyWeather.Simulation
-

Calls all initialize! functions for components of model, except for model.output and model.feedback which are always called at in time_stepping! and model.implicit which is done in first_timesteps!.

source
SpeedyWeather.initialize!Method
initialize!(model::ShallowWater) -> SpeedyWeather.Simulation
+

Calls all initialize! functions for components of model, except for model.output and model.feedback which are always called at in time_stepping! and model.implicit which is done in first_timesteps!.

source
SpeedyWeather.initialize!Method
initialize!(
     clock::SpeedyWeather.Clock,
     time_stepping::SpeedyWeather.TimeStepper
 ) -> SpeedyWeather.Clock
-

Initialize the clock with the time step Δt in the time_stepping.

source
SpeedyWeather.initialize!Method
initialize!(
     orog::ZonalRidge,
     P::SpeedyWeather.AbstractPlanet,
     S::SpectralTransform,
     G::Geometry
 ) -> LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat
-

Initialize the arrays orography,geopot_surf in orog following Jablonowski and Williamson, 2006.

source
SpeedyWeather.initialize!Method
initialize!(
     output::OutputWriter{output_NF, Model},
     feedback::SpeedyWeather.AbstractFeedback,
     time_stepping::SpeedyWeather.TimeStepper,
@@ -323,37 +323,37 @@
     diagn::DiagnosticVariables,
     model
 )
-

Creates a netcdf file on disk and the corresponding netcdf_file object preallocated with output variables and dimensions. write_output! then writes consecuitive time steps into this file.

source
SpeedyWeather.initialize!Method
initialize!(
+

Creates a netcdf file on disk and the corresponding netcdf_file object preallocated with output variables and dimensions. write_output! then writes consecuitive time steps into this file.

source
SpeedyWeather.initialize!Method
initialize!(
     progn::PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF},
     initial_conditions::SpeedyWeather.RandomWaves,
     model::ShallowWater
 )
-

Random initial conditions for the interface displacement η in the shallow water equations. The flow (u,v) is zero initially. This kicks off gravity waves that will interact with orography.

source
SpeedyWeather.initialize!Method
initialize!(
+

Random initial conditions for the interface displacement η in the shallow water equations. The flow (u,v) is zero initially. This kicks off gravity waves that will interact with orography.

source
SpeedyWeather.initialize!Method
initialize!(
     progn::PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF},
     initial_conditions::StartWithRandomVorticity,
     model::SpeedyWeather.ModelSetup
 )
-

Start with random vorticity as initial conditions

source
SpeedyWeather.initialize!Method
initialize!(
     progn::PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF},
     initial_conditions::ZonalWind,
     model::PrimitiveEquation
 )
-

Initial conditions from Jablonowski and Williamson, 2006, QJR Meteorol. Soc

source
SpeedyWeather.initialize!Method
initialize!(
     scheme::SpeedyWeather.StaticEnergyDiffusion{NF},
     model::PrimitiveEquation
 ) -> Any
-

Initialize dry static energy diffusion.

source
SpeedyWeather.initialize_geopotentialMethod
initialize_geopotential(
     σ_levels_full::Vector,
     σ_levels_half::Vector,
     R_dry::Real
 ) -> Tuple{Vector{Float64}, Vector{Float64}}
-

Precomputes constants for the vertical integration of the geopotential, defined as

Φ_{k+1/2} = Φ_{k+1} + R*T_{k+1}*(ln(p_{k+1}) - ln(p_{k+1/2})) (half levels) Φ_k = Φ_{k+1/2} + R*T_k*(ln(p_{k+1/2}) - ln(p_k)) (full levels)

Same formula but k → k-1/2.

source
SpeedyWeather.large_scale_condensation!Method
large_scale_condensation!(
+

Precomputes constants for the vertical integration of the geopotential, defined as

Φ_{k+1/2} = Φ_{k+1} + R*T_{k+1}*(ln(p_{k+1}) - ln(p_{k+1/2})) (half levels) Φ_k = Φ_{k+1/2} + R*T_k*(ln(p_{k+1/2}) - ln(p_k)) (full levels)

Same formula but k → k-1/2.

source
SpeedyWeather.large_scale_condensation!Method
large_scale_condensation!(
     column::ColumnVariables{NF},
     scheme::SpeedyCondensation,
     geometry::Geometry,
@@ -361,7 +361,7 @@
     atmosphere::SpeedyWeather.AbstractAtmosphere,
     time_stepping::SpeedyWeather.TimeStepper
 )
-

Large-scale condensation for a column by relaxation back to a reference relative humidity if larger than that. Calculates the tendencies for specific humidity and temperature and integrates the large-scale precipitation vertically for output.

source
SpeedyWeather.launch_kernel!Method
launch_kernel!(device_setup::DeviceSetup, kernel!, ndrange, kernel_args...)

Launches the kernel! on the device_setup with ndrange computations over the kernel and arguments kernel_args

source
SpeedyWeather.leapfrog!Method
leapfrog!(
+

Large-scale condensation for a column by relaxation back to a reference relative humidity if larger than that. Calculates the tendencies for specific humidity and temperature and integrates the large-scale precipitation vertically for output.

source
SpeedyWeather.launch_kernel!Method
launch_kernel!(device_setup::DeviceSetup, kernel!, ndrange, kernel_args...)

Launches the kernel! on the device_setup with ndrange computations over the kernel and arguments kernel_args

source
SpeedyWeather.leapfrog!Method
leapfrog!(
     A_old::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},
     A_new::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},
     tendency::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},
@@ -369,140 +369,140 @@
     lf::Int64,
     L::Leapfrog{NF<:AbstractFloat}
 )
-

Performs one leapfrog time step with (lf=2) or without (lf=1) Robert+Williams filter (see Williams (2009), Montly Weather Review, Eq. 7-9).

source
SpeedyWeather.linear_pressure_gradient!Method
linear_pressure_gradient!(
     diagn::SpeedyWeather.DiagnosticVariablesLayer,
     surface::SpeedyWeather.PrognosticSurfaceTimesteps,
     lf::Int64,
     C::DynamicsConstants,
     I::ImplicitPrimitiveEq
 ) -> LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat
-

Add the linear contribution of the pressure gradient to the geopotential. The pressure gradient in the divergence equation takes the form

-∇⋅(Rd*Tᵥ*∇lnpₛ) = -∇⋅(Rd*Tᵥ'*∇lnpₛ) - ∇²(Rd*Tₖ*lnpₛ)

So that the second term inside the Laplace operator can be added to the geopotential. Rd is the gas constant, Tᵥ the virtual temperature and Tᵥ' its anomaly wrt to the average or reference temperature Tₖ, lnpₛ is the logarithm of surface pressure.

source
SpeedyWeather.linear_virtual_temperature!Method
linear_virtual_temperature!(
+

Add the linear contribution of the pressure gradient to the geopotential. The pressure gradient in the divergence equation takes the form

-∇⋅(Rd*Tᵥ*∇lnpₛ) = -∇⋅(Rd*Tᵥ'*∇lnpₛ) - ∇²(Rd*Tₖ*lnpₛ)

So that the second term inside the Laplace operator can be added to the geopotential. Rd is the gas constant, Tᵥ the virtual temperature and Tᵥ' its anomaly wrt to the average or reference temperature Tₖ, lnpₛ is the logarithm of surface pressure.

source
SpeedyWeather.linear_virtual_temperature!Method
linear_virtual_temperature!(
     diagn::SpeedyWeather.DiagnosticVariablesLayer,
     progn::SpeedyWeather.PrognosticLayerTimesteps,
     constants::DynamicsConstants,
     lf::Int64
 ) -> Any
-

Calculates a linearised virtual temperature Tᵥ as

Tᵥ = T + Tₖμq

With absolute temperature T, layer-average temperarture Tₖ (computed in temperature_average!), specific humidity q and

μ = (1-ξ)/ξ, ξ = R_dry/R_vapour.

in spectral space.

source
SpeedyWeather.linear_virtual_temperature!Method
linear_virtual_temperature!(
+

Calculates a linearised virtual temperature Tᵥ as

Tᵥ = T + Tₖμq

With absolute temperature T, layer-average temperarture Tₖ (computed in temperature_average!), specific humidity q and

μ = (1-ξ)/ξ, ξ = R_dry/R_vapour.

in spectral space.

source
SpeedyWeather.linear_virtual_temperature!Method
linear_virtual_temperature!(
     diagn::SpeedyWeather.DiagnosticVariablesLayer,
     progn::SpeedyWeather.PrognosticLayerTimesteps,
     model::PrimitiveDry,
     lf::Integer
 ) -> LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat
-

Linear virtual temperature for model::PrimitiveDry: Just copy over arrays from temp to temp_virt at timestep lf in spectral space as humidity is zero in this model.

source
SpeedyWeather.load_trajectoryMethod
load_trajectory(
+

Linear virtual temperature for model::PrimitiveDry: Just copy over arrays from temp to temp_virt at timestep lf in spectral space as humidity is zero in this model.

source
SpeedyWeather.load_trajectoryMethod
load_trajectory(
     var_name::Union{String, Symbol},
     model::SpeedyWeather.ModelSetup
 ) -> Any
-

Loads a var_name trajectory of the model M that has been saved in a netCDF file during the time stepping.

source
SpeedyWeather.moist_static_energy!Method
moist_static_energy!(
     column::ColumnVariables,
     thermodynamics::SpeedyWeather.Thermodynamics
 )
-

Compute the moist static energy

MSE = SE + Lc*Q = cₚT + Φ + Lc*Q

with the static energy SE, the latent heat of condensation Lc, the geopotential Φ. As well as the saturation moist static energy which replaces Q with Q_sat

source
SpeedyWeather.nansMethod
A = nans(T,dims...)

Allocate array A with NaNs of type T. Similar to zeros(T,dims...).

source
SpeedyWeather.nar_detection!Method
nar_detection!(
+

Compute the moist static energy

MSE = SE + Lc*Q = cₚT + Φ + Lc*Q

with the static energy SE, the latent heat of condensation Lc, the geopotential Φ. As well as the saturation moist static energy which replaces Q with Q_sat

source
SpeedyWeather.nansMethod
A = nans(T,dims...)

Allocate array A with NaNs of type T. Similar to zeros(T,dims...).

source
SpeedyWeather.nar_detection!Method
nar_detection!(
     feedback::Feedback,
     progn::PrognosticVariables
 ) -> Union{Nothing, Bool}
-

Detect NaR (Not-a-Real) in the prognostic variables.

source
SpeedyWeather.parameterization_tendencies!Method
parameterization_tendencies!(
     diagn::DiagnosticVariables,
     time::Dates.DateTime,
     model::PrimitiveEquation
 ) -> Any
-

Compute tendencies for u,v,temp,humid from physical parametrizations. Extract for each vertical atmospheric column the prognostic variables (stored in diagn as they are grid-point transformed), loop over all grid-points, compute all parametrizations on a single-column basis, then write the tendencies back into a horizontal field of tendencies.

source
SpeedyWeather.pressure_on_orography!Method
pressure_on_orography!(
+

Compute tendencies for u,v,temp,humid from physical parametrizations. Extract for each vertical atmospheric column the prognostic variables (stored in diagn as they are grid-point transformed), loop over all grid-points, compute all parametrizations on a single-column basis, then write the tendencies back into a horizontal field of tendencies.

source
SpeedyWeather.pressure_on_orography!Method
pressure_on_orography!(
     progn::PrognosticVariables,
     model::PrimitiveEquation
 )
-

Initialize surface pressure on orography by integrating the hydrostatic equation with the reference temperature lapse rate.

source
SpeedyWeather.readable_secsMethod
readable_secs(secs::Real) -> Dates.CompoundPeriod
+

Initialize surface pressure on orography by integrating the hydrostatic equation with the reference temperature lapse rate.

source
SpeedyWeather.readable_secsMethod
readable_secs(secs::Real) -> Dates.CompoundPeriod
 

Returns Dates.CompoundPeriod rounding to either (days, hours), (hours, minutes), (minutes, seconds), or seconds with 1 decimal place accuracy for >10s and two for less. E.g.

julia> readable_secs(12345)
-3 hours, 26 minutes
source
SpeedyWeather.remaining_timeMethod
remaining_time(p::ProgressMeter.Progress) -> String
-

Estimates the remaining time from a ProgresssMeter.Progress. Adapted from ProgressMeter.jl

source
SpeedyWeather.reset_column!Method
reset_column!(column::ColumnVariables{NF})
-

Set the accumulators (tendencies but also vertical sums and similar) back to zero for column to be reused at other grid points.

source
SpeedyWeather.remaining_timeMethod
remaining_time(p::ProgressMeter.Progress) -> String
+

Estimates the remaining time from a ProgresssMeter.Progress. Adapted from ProgressMeter.jl

source
SpeedyWeather.reset_column!Method
reset_column!(column::ColumnVariables{NF})
+

Set the accumulators (tendencies but also vertical sums and similar) back to zero for column to be reused at other grid points.

source
SpeedyWeather.run!Method
run!(
     simulation::SpeedyWeather.Simulation;
     initialize,
     n_days,
     startdate,
     output
 ) -> PrognosticVariables
-

Run a SpeedyWeather.jl simulation. The simulation.model is assumed to be initialized, otherwise use initialize=true as keyword argument.

source
SpeedyWeather.saturation_humidity!Method
saturation_humidity!(
+

Run a SpeedyWeather.jl simulation. The simulation.model is assumed to be initialized, otherwise use initialize=true as keyword argument.

source
SpeedyWeather.saturation_humidity!Method
saturation_humidity!(
     column::ColumnVariables,
     thermodynamics::SpeedyWeather.Thermodynamics
 )
-

Compute (1) the saturation vapour pressure as a function of temperature using the August-Roche-Magnus formula,

eᵢ(T) = e₀ * exp(Cᵢ * (T - T₀) / (T - Tᵢ)),

where T is in Kelvin and i = 1,2 for saturation with respect to water and ice, respectively. And (2) the saturation specific humidity according to the formula,

0.622 * e / (p - (1 - 0.622) * e),

where e is the saturation vapour pressure, p is the pressure, and 0.622 is the ratio of the molecular weight of water to dry air.

source
SpeedyWeather.scale!Method
scale!(progn::PrognosticVariables, scale::Real) -> Real
-

Scales the prognostic variables vorticity and divergence with the Earth's radius which is used in the dynamical core.

source
SpeedyWeather.scale!Method
scale!(
+

Compute (1) the saturation vapour pressure as a function of temperature using the August-Roche-Magnus formula,

eᵢ(T) = e₀ * exp(Cᵢ * (T - T₀) / (T - Tᵢ)),

where T is in Kelvin and i = 1,2 for saturation with respect to water and ice, respectively. And (2) the saturation specific humidity according to the formula,

0.622 * e / (p - (1 - 0.622) * e),

where e is the saturation vapour pressure, p is the pressure, and 0.622 is the ratio of the molecular weight of water to dry air.

source
SpeedyWeather.scale!Method
scale!(progn::PrognosticVariables, scale::Real) -> Real
+

Scales the prognostic variables vorticity and divergence with the Earth's radius which is used in the dynamical core.

source
SpeedyWeather.scale!Method
scale!(
     progn::PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF},
     var::Symbol,
     scale::Real
 )
-

Scale the variable var inside progn with scalar scale.

source
SpeedyWeather.set_pressure!Method
set_pressure!(progn::PrognosticVariables{NF}, 
               pressure::AbstractMatrix, 
               Grid::Type{<:AbstractGrid}, 
-              lf::Integer=1) where NF

Sets the prognostic variable with the surface pressure in grid space at leapfrog index lf.

source
SpeedyWeather.set_pressure!Method
set_pressure!(progn::PrognosticVariables{NF}, 
+              lf::Integer=1) where NF

Sets the prognostic variable with the surface pressure in grid space at leapfrog index lf.

source
SpeedyWeather.set_pressure!Method
set_pressure!(progn::PrognosticVariables{NF}, 
               pressure::LowerTriangularMatrix;
-              lf::Integer=1) where NF

Sets the prognostic variable with the surface pressure in spectral space at leapfrog index lf.

source
SpeedyWeather.set_pressure!Method
set_pressure!(progn::PrognosticVariables{NF}, 
+              lf::Integer=1) where NF

Sets the prognostic variable with the surface pressure in spectral space at leapfrog index lf.

source
SpeedyWeather.set_pressure!Method
set_pressure!(progn::PrognosticVariables{NF}, 
               pressure::AbstractGrid, 
               M::ModelSetup;
-              lf::Integer=1) where NF

Sets the prognostic variable with the surface pressure in grid space at leapfrog index lf.

source
SpeedyWeather.set_pressure!Method
set_pressure!(progn::PrognosticVariables{NF}, 
+              lf::Integer=1) where NF

Sets the prognostic variable with the surface pressure in grid space at leapfrog index lf.

source
SpeedyWeather.set_pressure!Method
set_pressure!(progn::PrognosticVariables{NF}, 
               pressure::AbstractGrid, 
-              lf::Integer=1) where NF

Sets the prognostic variable with the surface pressure in grid space at leapfrog index lf.

source
SpeedyWeather.set_var!Method
function set_var!(progn::PrognosticVariables{NF}, 
+              lf::Integer=1) where NF

Sets the prognostic variable with the surface pressure in grid space at leapfrog index lf.

source
SpeedyWeather.set_var!Method
function set_var!(progn::PrognosticVariables{NF}, 
                   varname::Symbol, 
                   s::Number;
-                  lf::Integer=1) where NF

Sets all values of prognostic variable varname at leapfrog index lf to the scalar s.

source
SpeedyWeather.set_var!Method
set_var!(progn::PrognosticVariables{NF}, 
+                  lf::Integer=1) where NF

Sets all values of prognostic variable varname at leapfrog index lf to the scalar s.

source
SpeedyWeather.set_var!Method
set_var!(progn::PrognosticVariables{NF}, 
          varname::Symbol, 
          var::Vector{<:AbstractMatrix}, 
          Grid::Type{<:AbstractGrid}=FullGaussianGrid;
-         lf::Integer=1) where NF

Sets the prognostic variable with the name varname in all layers at leapfrog index lf with values given in var a vector with all information for all layers in grid space.

source
SpeedyWeather.set_var!Method
set_var!(progn::PrognosticVariables{NF},        
+         lf::Integer=1) where NF

Sets the prognostic variable with the name varname in all layers at leapfrog index lf with values given in var a vector with all information for all layers in grid space.

source
SpeedyWeather.set_var!Method
set_var!(progn::PrognosticVariables{NF},        
          varname::Symbol, 
          var::Vector{<:LowerTriangularMatrix};
-         lf::Integer=1) where NF

Sets the prognostic variable with the name varname in all layers at leapfrog index lf with values given in var a vector with all information for all layers in spectral space.

source
SpeedyWeather.set_var!Method
set_var!(progn::PrognosticVariables{NF}, 
+         lf::Integer=1) where NF

Sets the prognostic variable with the name varname in all layers at leapfrog index lf with values given in var a vector with all information for all layers in spectral space.

source
SpeedyWeather.set_var!Method
set_var!(progn::PrognosticVariables{NF}, 
          varname::Symbol, 
          var::Vector{<:AbstractGrid}, 
          M::ModelSetup;
-         lf::Integer=1) where NF

Sets the prognostic variable with the name varname in all layers at leapfrog index lf with values given in var a vector with all information for all layers in grid space.

source
SpeedyWeather.set_var!Method
set_var!(progn::PrognosticVariables{NF},        
+         lf::Integer=1) where NF

Sets the prognostic variable with the name varname in all layers at leapfrog index lf with values given in var a vector with all information for all layers in grid space.

source
SpeedyWeather.set_var!Method
set_var!(progn::PrognosticVariables{NF},        
          varname::Symbol, 
          var::Vector{<:AbstractGrid};
-         lf::Integer=1) where NF

Sets the prognostic variable with the name varname in all layers at leapfrog index lf with values given in var a vector with all information for all layers in grid space.

source
SpeedyWeather.speedstringMethod
speedstring(sec_per_iter, dt_in_sec) -> String
-

define a ProgressMeter.speedstring method that also takes a time step dt_in_sec to translate sec/iteration to days/days-like speeds.

source
SpeedyWeather.static_energy_diffusion!Method
static_energy_diffusion!(
+         lf::Integer=1) where NF

Sets the prognostic variable with the name varname in all layers at leapfrog index lf with values given in var a vector with all information for all layers in grid space.

source
SpeedyWeather.speedstringMethod
speedstring(sec_per_iter, dt_in_sec) -> String
+

define a ProgressMeter.speedstring method that also takes a time step dt_in_sec to translate sec/iteration to days/days-like speeds.

source
SpeedyWeather.surface_pressure_tendency!Method
surface_pressure_tendency!( Prog::PrognosticVariables,
                             Diag::DiagnosticVariables,
                             lf::Int,
-                            M::PrimitiveEquation)

Computes the tendency of the logarithm of surface pressure as

-(ū*px + v̄*py) - D̄

with ū,v̄ being the vertically averaged velocities; px, py the gradients of the logarithm of surface pressure ln(p_s) and D̄ the vertically averaged divergence.

  1. Calculate ∇ln(p_s) in spectral space, convert to grid.
  2. Multiply ū,v̄ with ∇ln(p_s) in grid-point space, convert to spectral.
  3. D̄ is subtracted in spectral space.
  4. Set tendency of the l=m=0 mode to 0 for better mass conservation.
source
SpeedyWeather.temperature_average!Method
temperature_average!(
+                            M::PrimitiveEquation)

Computes the tendency of the logarithm of surface pressure as

-(ū*px + v̄*py) - D̄

with ū,v̄ being the vertically averaged velocities; px, py the gradients of the logarithm of surface pressure ln(p_s) and D̄ the vertically averaged divergence.

  1. Calculate ∇ln(p_s) in spectral space, convert to grid.
  2. Multiply ū,v̄ with ∇ln(p_s) in grid-point space, convert to spectral.
  3. D̄ is subtracted in spectral space.
  4. Set tendency of the l=m=0 mode to 0 for better mass conservation.
source
SpeedyWeather.temperature_average!Method
temperature_average!(
     diagn::SpeedyWeather.DiagnosticVariablesLayer,
     temp::LowerTriangularMatrix,
     S::SpectralTransform
 ) -> Any
-

Calculates the average temperature of a layer from the l=m=0 harmonic and stores the result in diagn.temp_average

source
SpeedyWeather.temperature_relaxation!Method
temperature_relaxation!(
     column::ColumnVariables,
     scheme::JablonowskiRelaxation
 )
-

Apply HeldSuarez-like temperature relaxation to the Jablonowski and Williamson vertical profile.

source
SpeedyWeather.temperature_tendency!Method
temperature_tendency!(
     diagn::SpeedyWeather.DiagnosticVariablesLayer,
     C::DynamicsConstants,
     G::Geometry,
     S::SpectralTransform,
     I::ImplicitPrimitiveEq
 )
-

Compute the temperature tendency

∂T/∂t += -∇⋅((u,v)*T') + T'D + κTᵥ*Dlnp/Dt

+= because the tendencies already contain parameterizations and vertical advection. T' is the anomaly with respect to the reference/average temperature. Tᵥ is the virtual temperature used in the adiabatic term κTᵥ*Dlnp/Dt.

source
SpeedyWeather.temperature_tendency!Method
temperature_tendency!(
+

Compute the temperature tendency

∂T/∂t += -∇⋅((u,v)*T') + T'D + κTᵥ*Dlnp/Dt

+= because the tendencies already contain parameterizations and vertical advection. T' is the anomaly with respect to the reference/average temperature. Tᵥ is the virtual temperature used in the adiabatic term κTᵥ*Dlnp/Dt.

source
SpeedyWeather.time_stepping!Method
time_stepping!(
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::SpeedyWeather.ModelSetup
 ) -> PrognosticVariables
-

Main time loop that that initializes output and feedback, loops over all time steps and calls the output and feedback functions.

source
SpeedyWeather.timestep!Function
timestep!(
+

Main time loop that that initializes output and feedback, loops over all time steps and calls the output and feedback functions.

source
SpeedyWeather.timestep!Function
timestep!(
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     dt::Real,
@@ -523,7 +523,7 @@
     lf1::Int64,
     lf2::Int64
 )
-

Calculate a single time step for the model <: Barotropic.

source
SpeedyWeather.timestep!Method
timestep!(
     progn::PrognosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},
     diagn::DiagnosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},
     dt::Real,
@@ -544,7 +544,7 @@
     lf1::Int64,
     lf2::Int64
 ) -> Any
-

Calculate a single time step for the model<:PrimitiveEquation

source
SpeedyWeather.timestep!Method
timestep!(
     progn::PrognosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},
     diagn::DiagnosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},
     dt::Real,
@@ -565,19 +565,19 @@
     lf1::Int64,
     lf2::Int64
 ) -> Union{Nothing, SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat}
-

Calculate a single time step for the model <: ShallowWater.

source
SpeedyWeather.unscale!Method
unscale!(variable::AbstractArray, scale::Real) -> Any
-

Undo the radius-scaling for any variable. Method used for netcdf output.

source
SpeedyWeather.unscale!Method
unscale!(progn::PrognosticVariables) -> Int64
-

Undo the radius-scaling of vorticity and divergence from scale!(progn,scale::Real).

source
SpeedyWeather.vertical_integration!Method
vertical_integration!(Diag::DiagnosticVariables,G::Geometry)

Calculates the vertically averaged (weighted by the thickness of the σ level) velocities (*coslat) and divergence. E.g.

u_mean = ∑_k=1^nlev Δσ_k * u_k

u,v are averaged in grid-point space, divergence in spectral space.

source
SpeedyWeather.unscale!Method
unscale!(variable::AbstractArray, scale::Real) -> Any
+

Undo the radius-scaling for any variable. Method used for netcdf output.

source
SpeedyWeather.unscale!Method
unscale!(progn::PrognosticVariables) -> Int64
+

Undo the radius-scaling of vorticity and divergence from scale!(progn,scale::Real).

source
SpeedyWeather.vertical_integration!Method
vertical_integration!(Diag::DiagnosticVariables,G::Geometry)

Calculates the vertically averaged (weighted by the thickness of the σ level) velocities (*coslat) and divergence. E.g.

u_mean = ∑_k=1^nlev Δσ_k * u_k

u,v are averaged in grid-point space, divergence in spectral space.

source
SpeedyWeather.virtual_temperature!Method
virtual_temperature!(
     diagn::SpeedyWeather.DiagnosticVariablesLayer,
     temp::LowerTriangularMatrix,
     constants::DynamicsConstants
 )
-

Calculates the virtual temperature Tᵥ as

Tᵥ = T(1+μq)

With absolute temperature T, specific humidity q and

μ = (1-ξ)/ξ, ξ = R_dry/R_vapour.

in grid-point space.

source
SpeedyWeather.virtual_temperature!Method
virtual_temperature!(
+

Calculates the virtual temperature Tᵥ as

Tᵥ = T(1+μq)

With absolute temperature T, specific humidity q and

μ = (1-ξ)/ξ, ξ = R_dry/R_vapour.

in grid-point space.

source
SpeedyWeather.virtual_temperature!Method
virtual_temperature!(
     diagn::SpeedyWeather.DiagnosticVariablesLayer,
     temp::LowerTriangularMatrix,
     model::PrimitiveDry
 ) -> SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat
-

Virtual temperature in grid-point space: For the PrimitiveDry temperature and virtual temperature are the same (humidity=0). Just copy over the arrays.

source
SpeedyWeather.volume_flux_divergence!Method
volume_flux_divergence!(
+

Virtual temperature in grid-point space: For the PrimitiveDry temperature and virtual temperature are the same (humidity=0). Just copy over the arrays.

source
SpeedyWeather.volume_flux_divergence!Method
volume_flux_divergence!(
     diagn::SpeedyWeather.DiagnosticVariablesLayer,
     surface::SpeedyWeather.SurfaceVariables,
     orog::SpeedyWeather.AbstractOrography,
@@ -585,7 +585,7 @@
     G::Geometry,
     S::SpectralTransform
 )
-

Computes the (negative) divergence of the volume fluxes uh,vh for the continuity equation, -∇⋅(uh,vh).

source
SpeedyWeather.vordiv_tendencies!Method
vordiv_tendencies!(
     diagn::SpeedyWeather.DiagnosticVariablesLayer,
     surf::SpeedyWeather.SurfaceVariables,
     C::DynamicsConstants,
@@ -594,20 +594,20 @@
 )
 

Tendencies for vorticity and divergence. Excluding Bernoulli potential with geopotential and linear pressure gradient inside the Laplace operator, which are added later in spectral space.

u_tend +=  v*(f+ζ) - RTᵥ'*∇lnp_x
 v_tend += -u*(f+ζ) - RTᵥ'*∇lnp_y

+= because the tendencies already contain the parameterizations and vertical advection. f is coriolis, ζ relative vorticity, R the gas constant Tᵥ' the virtual temperature anomaly, ∇lnp the gradient of surface pressure and _x and _y its zonal/meridional components. The tendencies are then curled/dived to get the tendencies for vorticity/divergence in spectral space

∂ζ/∂t = ∇×(u_tend,v_tend)
-∂D/∂t = ∇⋅(u_tend,v_tend) + ...

+ ... because there's more terms added later for divergence.

source
SpeedyWeather.vordiv_tendencies!Method
vordiv_tendencies!(
     diagn::SpeedyWeather.DiagnosticVariablesLayer,
     surf::SpeedyWeather.SurfaceVariables,
     model::PrimitiveEquation
 )
-

Function barrier to unpack model.

source
SpeedyWeather.vorticity_flux!Method
vorticity_flux!(
     diagn::SpeedyWeather.DiagnosticVariablesLayer,
     model::Barotropic
 )
-

Vorticity flux tendency in the barotropic vorticity equation

∂ζ/∂t = ∇×(u_tend,v_tend)

with

u_tend = Fᵤ + v*(ζ+f) v_tend = Fᵥ - u*(ζ+f)

with Fᵤ,Fᵥ the forcing from forcing! already in u_tend_grid/v_tend_grid and vorticity ζ, coriolis f.

source
SpeedyWeather.vorticity_flux!Method
vorticity_flux!(
+

Vorticity flux tendency in the barotropic vorticity equation

∂ζ/∂t = ∇×(u_tend,v_tend)

with

u_tend = Fᵤ + v*(ζ+f) v_tend = Fᵥ - u*(ζ+f)

with Fᵤ,Fᵥ the forcing from forcing! already in u_tend_grid/v_tend_grid and vorticity ζ, coriolis f.

source
SpeedyWeather.vorticity_flux!Method
vorticity_flux!(
     diagn::SpeedyWeather.DiagnosticVariablesLayer,
     model::ShallowWater
 )
-

Vorticity flux tendency in the shallow water equations

∂ζ/∂t = ∇×(u_tend,v_tend) ∂D/∂t = ∇⋅(u_tend,v_tend)

with

u_tend = Fᵤ + v*(ζ+f) v_tend = Fᵥ - u*(ζ+f)

with Fᵤ,Fᵥ the forcing from forcing! already in u_tend_grid/v_tend_grid and vorticity ζ, coriolis f.

source
SpeedyWeather.vorticity_flux_curldiv!Method
vorticity_flux_curldiv!(
+

Vorticity flux tendency in the shallow water equations

∂ζ/∂t = ∇×(u_tend,v_tend) ∂D/∂t = ∇⋅(u_tend,v_tend)

with

u_tend = Fᵤ + v*(ζ+f) v_tend = Fᵥ - u*(ζ+f)

with Fᵤ,Fᵥ the forcing from forcing! already in u_tend_grid/v_tend_grid and vorticity ζ, coriolis f.

source
SpeedyWeather.vorticity_flux_curldiv!Method
vorticity_flux_curldiv!(
     diagn::SpeedyWeather.DiagnosticVariablesLayer,
     C::DynamicsConstants,
     G::Geometry,
@@ -615,38 +615,38 @@
     div,
     add
 )
-

Compute the vorticity advection as the curl/div of the vorticity fluxes

∂ζ/∂t = ∇×(u_tend,v_tend) ∂D/∂t = ∇⋅(u_tend,v_tend)

with

u_tend = Fᵤ + v*(ζ+f) v_tend = Fᵥ - u*(ζ+f)

with Fᵤ,Fᵥ from u_tend_grid/v_tend_grid that are assumed to be alread set in forcing!. Set div=false for the BarotropicModel which doesn't require the divergence tendency.

source
SpeedyWeather.workgroup_sizeMethod
workgroup_size(dev::AbstractDevice)

Returns a workgroup size depending on dev. WIP: Will be expanded in the future to also include grid information.

source
SpeedyWeather.write_column_tendencies!Method
write_column_tendencies!(
+

Compute the vorticity advection as the curl/div of the vorticity fluxes

∂ζ/∂t = ∇×(u_tend,v_tend) ∂D/∂t = ∇⋅(u_tend,v_tend)

with

u_tend = Fᵤ + v*(ζ+f) v_tend = Fᵥ - u*(ζ+f)

with Fᵤ,Fᵥ from u_tend_grid/v_tend_grid that are assumed to be alread set in forcing!. Set div=false for the BarotropicModel which doesn't require the divergence tendency.

source
SpeedyWeather.workgroup_sizeMethod
workgroup_size(dev::AbstractDevice)

Returns a workgroup size depending on dev. WIP: Will be expanded in the future to also include grid information.

source
SpeedyWeather.write_column_tendencies!Method
write_column_tendencies!(
     D::DiagnosticVariables,
     C::ColumnVariables,
     ij::Int64
 )
-

Write the parametrization tendencies from C::ColumnVariables into the horizontal fields of tendencies stored in D::DiagnosticVariables at gridpoint index ij.

source
SpeedyWeather.write_netcdf_time!Method
write_netcdf_time!(
+

Write the parametrization tendencies from C::ColumnVariables into the horizontal fields of tendencies stored in D::DiagnosticVariables at gridpoint index ij.

source
SpeedyWeather.write_netcdf_time!Method
write_netcdf_time!(
     output::OutputWriter,
     time::Dates.DateTime
 )
-

Write the current time time::DateTime to the netCDF file in output::OutputWriter.

source
SpeedyWeather.write_netcdf_variables!Method
write_netcdf_variables!(
     output::OutputWriter,
     diagn::DiagnosticVariables{NF, Grid, Model}
 )
-

Write diagnostic variables from diagn to the netCDF file in output::OutputWriter.

source
SpeedyWeather.write_output!Method
write_output!(
     outputter::OutputWriter,
     time::Dates.DateTime,
     diagn::DiagnosticVariables
 )
-

Writes the variables from diagn of time step i at time time into outputter.netcdf_file. Simply escapes for no netcdf output of if output shouldn't be written on this time step. Interpolates onto output grid and resolution as specified in outputter, converts to output number format, truncates the mantissa for higher compression and applies lossless compression.

source
SpeedyWeather.write_restart_fileMethod
write_restart_file(
+

Writes the variables from diagn of time step i at time time into outputter.netcdf_file. Simply escapes for no netcdf output of if output shouldn't be written on this time step. Interpolates onto output grid and resolution as specified in outputter, converts to output number format, truncates the mantissa for higher compression and applies lossless compression.

source
SpeedyWeather.write_restart_fileMethod
write_restart_file(
     progn::PrognosticVariables,
     output::OutputWriter
 ) -> Union{Nothing, String}
-

A restart file restart.jld2 with the prognostic variables is written to the output folder (or current path) that can be used to restart the model. restart.jld2 will then be used as initial conditions. The prognostic variables are bitrounded for compression and the 2nd leapfrog time step is discarded. Variables in restart file are unscaled.

source
SpeedyWeather.zero_tendencies!Method
zero_tendencies!(
+

A restart file restart.jld2 with the prognostic variables is written to the output folder (or current path) that can be used to restart the model. restart.jld2 will then be used as initial conditions. The prognostic variables are bitrounded for compression and the 2nd leapfrog time step is discarded. Variables in restart file are unscaled.

source
+

Set the tendencies in diagn to zero.

source
diff --git a/previews/PR387/grids/index.html b/previews/PR387/grids/index.html index d1eac69a2..9e6848124 100644 --- a/previews/PR387/grids/index.html +++ b/previews/PR387/grids/index.html @@ -4,4 +4,4 @@ ├ Spectral: T31 LowerTriangularMatrix{Complex{Float32}}, radius = 6.371e6 m ├ Grid: 48-ring FullGaussianGrid{Float32}, 4608 grid points ├ Resolution: 333km (average), 417km × 413km (Equator) -└ Vertical: 8-level SigmaCoordinates

The life of every SpeedyWeather.jl simulation starts with a SpectralGrid object which defines the resolution in spectral and in grid-point space. The generator SpectralGrid() can take as a keyword argument Grid which can be any of the grids described below. The resolution of the grid, however, is not directly chosen, but determined from the spectral resolution trunc and the dealiasing factor. More in SpectralGrid and Matching spectral and grid resolution.

RingGrids is a module too!

While RingGrids is the underlying module that SpeedyWeather.jl uses for data structs on the sphere, the module can also be used independently of SpeedyWeather, for example to interpolate between data on different grids. See RingGrids

Ring-based equi-longitude grids

SpeedyWeather.jl's spectral transform supports all ring-based equi-longitude grids. These grids have their grid points located on rings with constant latitude and on these rings the points are equi-spaced in longitude. There is technically no constrain on the spacing of the latitude rings, but the Legendre transform requires a quadrature to map those to spectral space and back. Common choices for latitudes are the Gaussian latitudes which use the Gaussian quadrature, or equi-angle latitudes (i.e. just regular latitudes but excluding the poles) that use the Clenshaw-Curtis quadrature. The longitudes have to be equi-spaced on every ring, which is necessary for the fast Fourier transform, as one would otherwise need to use a non-uniform Fourier transform. In SpeedyWeather.jl the first grid point on any ring can have a longitudinal offset though, for example by spacing 4 points around the globe at 45˚E, 135˚E, 225˚E, and 315˚E. In this case the offset is 45˚E as the first point is not at 0˚E.

Is the FullClenshawGrid a longitude-latitude grid?

Short answer: Yes. The FullClenshawGrid is a specific longitude-latitude grid with equi-angle spacing. The most common grids for geoscientific data use regular spacings for 0-360˚E in longitude and 90˚N-90˚S. The FullClenshawGrid does that too, but it does not have a point on the North or South pole, and the central latitude ring sits exactly on the Equator. We name it Clenshaw following the Clenshaw-Curtis quadrature that is used in the Legendre transfrom in the same way as Gaussian refers to the Gaussian quadrature.

Implemented grids

All grids in SpeedyWeather.jl are a subtype of AbstractGrid, i.e. <: AbstractGrid. We further distinguish between full, and reduced grids. Full grids have the same number of longitude points on every latitude ring (i.e. points converge towards the poles) and reduced grids reduce the number of points towards the poles to have them more evenly spread out across the globe. More evenly does not necessarily mean that a grid is equal-area, meaning that every grid cell covers exactly the same area (although the shape changes).

Currently the following full grids <: AbstractFullGrid are implemented

and additionally we have FullHEALPixGrid and FullOctaHEALPixGrid which are the full grid equivalents to the HEALPix grid and the OctaHEALPix grid discussed below. Full grid equivalent means that they have the same latitude rings, but no reduction in the number of points per ring towards the poles and no longitude offset. Other implemented reduced grids are

An overview of these grids is visualised here, and a more detailed description follows below.

Overview of implemented grids in SpeedyWeather.jl

Visualised are each grid's grid points (white dots) and grid faces (white lines). All grids shown have 16 latitude rings on one hemisphere, Equator included. The total number of grid points is denoted in the top left of every subplot. The sphere is shaded with grey, orange and turquoise regions to denote the hemispheres in a and b, the 8 octahedral faces c, d,f and the 12 dodecahedral faces (or base pixels) in e. Coastlines are added for orientation.

Grid resolution

All grids use the same resolution parameter nlat_half, i.e. the number of rings on one hemisphere, Equator included. The Gaussian grids (full and reduced) do not have a ring on the equator, so their total number of rings nlat is always even and twice nlat_half. Clenshaw-Curtis grids and the HEALPix grids have a ring on the equator such their total number of rings is always odd and one less than the Gaussian grids at the same nlat_half.

HEALPix grids do not use Nside as resolution parameter

The original formulation for HEALPix grids use $N_{side}$, the number of grid points along the edges of each basepixel (8 in the figure above), SpeedyWeather.jl uses nlat_half, the number of rings on one hemisphere, Equator included, for all grids. This is done for consistency across grids. We may use $N_{side}$ for the documentation or within functions though.

Related: Effective grid resolution and Available horizontal resolutions.

Matching spectral and grid resolution

A given spectral resolution can be matched to a variety of grid resolutions. A cubic grid, for example, combines a spectral truncation $T$ with a grid resolution $N$ (=nlat_half) such that $T + 1 = N$. Using T31 and an O32 is therefore often abbreviated as Tco31 meaning that the spherical harmonics are truncated at $l_{max}=31$ in combination with N=32, i.e. 64 latitude rings in total on an octahedral Gaussian grid. In SpeedyWeather.jl the choice of the order of truncation is controlled with the dealiasing parameter in the SpectralGrid construction.

Let J be the total number of rings. Then we have

and in general $\frac{m+1}{2}T \approx J$ for m-th order truncation. So the higher the truncation order the more grid points are used in combination with the same spectral resolution. A higher truncation order therefore makes all grid-point calculations more expensive, but can represent products of terms on the grid (which will have higher wavenumber components) to a higher accuracy as more grid points are available within a given wavelength. Using a sufficiently high truncation is therefore one way to avoid aliasing. A quick overview of how the grid resolution changes when dealiasing is passed onto SpectralGrid on the FullGaussianGrid

truncdealiasingFullGaussianGrid size
31164x32
31296x48
313128x64
42196x48
422128x64
423192x96
.........

You will obtain this information every time you create a SpectralGrid(;Grid,trunc,dealiasing).

Full Gaussian grid

(called FullGaussianGrid)

The full Gaussian grid is a grid that uses regularly spaced longitudes which points that do not reduce in number towards the poles. That means for every latitude $\theta$ the longitudes $\phi$ are

\[\phi_i = \frac{2\pi (i-1)}{N_\phi}\]

with $i = 1,...,N_\phi$ the in-ring index (1-based, counting from 0˚ eastward) and $N_\phi$ the number of longitudinal points on the grid. The first longitude is therefore 0˚, meaning that there is no longitudinal offset on this grid. There are always twice as many points in zonal direction as there are in meridional, i.e. $N_\phi = 2N_\theta$. The latitudes, however, are not regular, but chosen from the $j$-th zero crossing $z_j(l)$ of the $l$-th Legendre polynomial. For $\theta$ in latitudes

\[\sin(\theta_j) = z_j(l) \]

As it can be easy to mix up latitudes, colatitudes and as the Legendre polynomials are defined in $[0,1]$ an overview of the first Gaussian latitudes (approximated for $l>2$ for brevity)

$l$Zero crossings $z_j$Latitudes [˚N]
2$\pm \tfrac{1}{\sqrt{3}}$$\pm 35.3...$
4$\pm 0.34..., \pm 0.86...$$\pm 19.9..., \pm 59.44...$
6$\pm 0.24..., \pm 0.66..., \pm 0.93...$$\pm 13.8..., \pm 41.4..., \pm 68.8...$

Only even Legendre polynomials are used, such that there is always an even number of latitudes, with no latitude on the Equator. As you can already see from this short table, the Gaussian latitudes do not nest, i.e. different resolutions through different $l$ do not share latitudes. The latitudes are also only approximately evenly spaced. Due to the Gaussian latitudes, a spectral transform with a full Gaussian grid is exact as long as the truncation is at least quadratic, see Matching spectral and grid resolution. Exactness here means that only rounding errors occur in the transform, meaning that the transform error is very small compared to other errors in a simulation. This property arises from that property of the Gauss-Legendre quadrature, which is used in the Spherical Harmonic Transform with a full Gaussian grid.

On the full Gaussian grid there are in total $N_\phi N_\theta$ grid points, which are squeezed towards the poles, making the grid area smaller and smaller following a cosine. But no points are on the poles as $z=-1$ or $1$ is never a zero crossing of the Legendre polynomials.

Octahedral Gaussian grid

(called OctahedralGaussianGrid)

The octahedral Gaussian grid is a reduced grid, i.e. the number of longitudinal points reduces towards the poles. It still uses the Gaussian latitudes from the full Gaussian grid so the exactness property of the spherical harmonic transform also holds for this grid. However, the longitudes $\phi_i$ with $i = 1,...,16+4j$ on the $j$-th latitude ring (starting with 1 around the north pole), $j=1,...,\tfrac{N_\theta}{2}$, are now, on the northern hemisphere,

\[\phi_i = \frac{2\pi (i-1)}{16 + 4j}.\]

We start with 20 points, evenly spaced, starting at 0˚E, around the first latitude ring below the north pole. The next ring has 24 points, then 28, and so on till reaching the Equator (which is not a ring). For the southern hemisphere all points are mirrored around the Equator. For more details see Malardel, 2016[M16].

Note that starting with 20 grid points on the first ring is a choice that ECMWF made with their grid for accuracy reasons. An octahedral Gaussian grid can also be defined starting with fewer grid points on the first ring. However, in SpeedyWeather.jl we follow ECMWF's definition.

The grid cells of an octahedral Gaussian grid are not exactly equal area, but are usually within a factor of two. This largely solves the efficiency problem of having too many grid points near the poles for computational, memory and data storage reasons.

Full Clenshaw-Curtis grid

(called FullClenshawGrid)

The full Clenshaw-Curtis grid is a regular longitude-latitude grid, but a specific one: The colatitudes $\theta_j$, and the longitudes $\phi_i$ are

\[\theta_j = \frac{j}{N_\theta + 1}\pi, \quad \phi_i = \frac{2\pi (i-1)}{N_\phi}\]

with $i$ the in-ring zonal index $i = 1,...,N_\phi$ and $j = 1, ... ,N_\theta$ the ring index starting with 1 around the north pole. There is no grid point on the poles, but in contrast to the Gaussian grids there is a ring on the Equator. The longitudes are shared with the full Gaussian grid. Being a full grid, also the full Clenshaw-Curtis grid suffers from too many grid points around the poles, this is addressed with the octahedral Clenshaw-Curtis grid.

The full Clenshaw-Curtis grid gets its name from the Clenshaw-Curtis quadrature that is used in the Legendre transform (see Spherical Harmonic Transform). This quadrature relies on evenly spaced latitudes, which also means that this grid nests, see Hotta and Ujiie[HU18]. More importantly for our application, the Clenshaw-Curtis grids (including the octahedral described below) allow for an exact transform with cubic truncation (see Matching spectral and grid resolution). Recall that the Gaussian latitudes allow for an exact transform with quadratic truncation, so the Clenshaw-Curtis grids require more grid points for the same spectral resolution to be exact. But compared to other errors during a simulation this error may be masked anyway.

Octahedral Clenshaw-Curtis grid

(called OctahedralClenshawGrid)

In the same as we constructed the octahedral Gaussian grid from the full Gaussian grid, the octahedral Clenshaw-Curtis grid can be constructed from the full Clenshaw-Curtis grid. It therefore shares the latitudes with the full grid, but the longitudes with the octahedral Gaussian grid.

\[\theta_j = \frac{j}{N_\theta + 1}\pi, \quad \phi_i = \frac{2\pi (i-1)}{16 + 4j}.\]

Notation as before, but note that the definition for $\phi_i$ only holds for the northern hemisphere, Equator included. The southern hemisphere is mirrored. The octahedral Clenshaw-Curtis grid inherits the exactness properties from the full Clenshaw-Curtis grid, but as it is a reduced grid, it is more efficient in terms of computational aspects and memory than the full grid. Hotta and Ujiie[HU18] describe this grid in more detail.

HEALPix grid

(called HEALPixGrid)

Technically, HEALPix grids are a class of grids that tessalate the sphere into faces that are often called basepixels. For each member of this class there are $N_\varphi$ basepixels in zonal direction and $N_\theta$ basepixels in meridional direction. For $N_\varphi = 4$ and $N_\theta = 3$ we obtain the classical HEALPix grid with $N_\varphi N_\theta = 12$ basepixels shown above in Implemented grids. Each basepixel has a quadratic number of grid points in them. There's an equatorial zone where the number of zonal grid points is constant (always $2N$, so 32 at $N=16$) and there are polar caps above and below the equatorial zone with the border at $\cos(\theta) = 2/3$ ($\theta$ in colatitudes).

Following Górski, 2004[G04], the $z=cos(\theta)$ colatitude of the $j$-th ring in the north polar cap, $j=1,...,N_{side}$ with $2N_{side} = N$ is

\[z = 1 - \frac{j^2}{3N_{side}^2}\]

and on that ring, the longitude $\phi$ of the $i$-th point ($i$ is the in-ring-index) is at

\[\phi = \frac{\pi}{2j}(i-\tfrac{1}{2})\]

The in-ring index $i$ goes from $i=1,...,4$ for the first (i.e. northern-most) ring, $i=1,...,8$ for the second ring and $i = 1,...,4j$ for the $j$-th ring in the northern polar cap.

In the north equatorial belt $j=N_{side},...,2N_{side}$ this changes to

\[z = \frac{4}{3} - \frac{2j}{3N_{side}}\]

and the longitudes change to ($i$ is always $i = 1,...,4N_{side}$ in the equatorial belt meaning the number of longitude points is constant here)

\[\phi = \frac{\pi}{2N_{side}}(i - \frac{s}{2}), \quad s = (j - N_{side} + 1) \mod 2\]

The modulo function comes in as there is an alternating longitudinal offset from the prime meridian (see Implemented grids). For the southern hemisphere the grid point locations can be obtained by mirror symmetry.

Grid cell boundaries

The cell boundaries are obtained by setting $i = k + 1/2$ or $i = k + 1/2 + j$ (half indices) into the equations above, such that $z(\phi,k)$, a function for the cosine of colatitude $z$ of index $k$ and the longitude $\phi$ is obtained. These are then (northern polar cap)

\[z = 1 - \frac{k^2}{3N_{side}^2}\left(\frac{\pi}{2\phi_t}\right)^2, \quad z = 1 - \frac{k^2}{3N_{side}^2}\left(\frac{\pi}{2\phi_t - \pi}\right)^2\]

with $\phi_t = \phi \mod \tfrac{\pi}{2}$ and in the equatorial belt

\[z = \frac{2}{3}-\frac{4k}{3N_{side}} \pm \frac{8\phi}{3\pi}\]

OctaHEALPix grid

(called OctaHEALPixGrid)

While the classic HEALPix grid is based on a dodecahedron, other choices for $N_\varphi$ and $N_\theta$ in the class of HEALPix grids will change the number of faces there are in zonal/meridional direction. With $N_\varphi = 4$ and $N_\theta = 1$ we obtain a HEALPix grid that is based on an octahedron, which has the convenient property that there are twice as many longitude points around the equator than there are latitude rings between the poles. This is a desirable for truncation as this matches the distances too, $2\pi$ around the Equator versus $\pi$ between the poles. $N_\varphi = 6, N_\theta = 2$ or $N_\varphi = 8, N_\theta = 3$ are other possible choices for this, but also more complicated. See Górski, 2004[G04] for further examples and visualizations of these grids.

We call the $N_\varphi = 4, N_\theta = 1$ HEALPix grid the OctaHEALPix grid, which combines the equal-area property of the HEALPix grids with the octahedron that's also used in the OctahedralGaussianGrid or the OctahedralClenshawGrid. As $N_\theta = 1$ there is no equatorial belt which simplifies the grid. The latitude of the $j$-th isolatitude ring on the OctaHEALPixGrid is defined by

\[z = 1 - \frac{j^2}{N^2},\]

with $j=1,...,N$, and similarly for the southern hemisphere by symmetry. On this grid $N_{side} = N$ where $N$= nlat_half, the number of latitude rings on one hemisphere, Equator included, because each of the 4 basepixels spans from pole to pole and covers a quarter of the sphere. The longitudes with in-ring- index $i = 1,...,4j$ are

\[\phi = \frac{\pi}{2j}(i - \tfrac{1}{2})\]

and again, the southern hemisphere grid points are obtained by symmetry.

Grid cell boundaries

Similar to the grid cell boundaries for the HEALPix grid, the OctaHEALPix grid's boundaries are

\[z = 1 - \frac{k^2}{N^2}\left(\frac{\pi}{2\phi_t}\right)^2, \quad z = 1 - \frac{k^2}{N^2}\left(\frac{\pi}{2\phi_t - \pi}\right)^2\]

The $3N_{side}^2$ in the denominator of the HEALPix grid came simply $N^2$ for the OctaHEALPix grid and there's no separate equation for the equatorial belt (which doesn't exist in the OctaHEALPix grid).

References

+└ Vertical: 8-level SigmaCoordinates

The life of every SpeedyWeather.jl simulation starts with a SpectralGrid object which defines the resolution in spectral and in grid-point space. The generator SpectralGrid() can take as a keyword argument Grid which can be any of the grids described below. The resolution of the grid, however, is not directly chosen, but determined from the spectral resolution trunc and the dealiasing factor. More in SpectralGrid and Matching spectral and grid resolution.

RingGrids is a module too!

While RingGrids is the underlying module that SpeedyWeather.jl uses for data structs on the sphere, the module can also be used independently of SpeedyWeather, for example to interpolate between data on different grids. See RingGrids

Ring-based equi-longitude grids

SpeedyWeather.jl's spectral transform supports all ring-based equi-longitude grids. These grids have their grid points located on rings with constant latitude and on these rings the points are equi-spaced in longitude. There is technically no constrain on the spacing of the latitude rings, but the Legendre transform requires a quadrature to map those to spectral space and back. Common choices for latitudes are the Gaussian latitudes which use the Gaussian quadrature, or equi-angle latitudes (i.e. just regular latitudes but excluding the poles) that use the Clenshaw-Curtis quadrature. The longitudes have to be equi-spaced on every ring, which is necessary for the fast Fourier transform, as one would otherwise need to use a non-uniform Fourier transform. In SpeedyWeather.jl the first grid point on any ring can have a longitudinal offset though, for example by spacing 4 points around the globe at 45˚E, 135˚E, 225˚E, and 315˚E. In this case the offset is 45˚E as the first point is not at 0˚E.

Is the FullClenshawGrid a longitude-latitude grid?

Short answer: Yes. The FullClenshawGrid is a specific longitude-latitude grid with equi-angle spacing. The most common grids for geoscientific data use regular spacings for 0-360˚E in longitude and 90˚N-90˚S. The FullClenshawGrid does that too, but it does not have a point on the North or South pole, and the central latitude ring sits exactly on the Equator. We name it Clenshaw following the Clenshaw-Curtis quadrature that is used in the Legendre transfrom in the same way as Gaussian refers to the Gaussian quadrature.

Implemented grids

All grids in SpeedyWeather.jl are a subtype of AbstractGrid, i.e. <: AbstractGrid. We further distinguish between full, and reduced grids. Full grids have the same number of longitude points on every latitude ring (i.e. points converge towards the poles) and reduced grids reduce the number of points towards the poles to have them more evenly spread out across the globe. More evenly does not necessarily mean that a grid is equal-area, meaning that every grid cell covers exactly the same area (although the shape changes).

Currently the following full grids <: AbstractFullGrid are implemented

and additionally we have FullHEALPixGrid and FullOctaHEALPixGrid which are the full grid equivalents to the HEALPix grid and the OctaHEALPix grid discussed below. Full grid equivalent means that they have the same latitude rings, but no reduction in the number of points per ring towards the poles and no longitude offset. Other implemented reduced grids are

An overview of these grids is visualised here, and a more detailed description follows below.

Overview of implemented grids in SpeedyWeather.jl

Visualised are each grid's grid points (white dots) and grid faces (white lines). All grids shown have 16 latitude rings on one hemisphere, Equator included. The total number of grid points is denoted in the top left of every subplot. The sphere is shaded with grey, orange and turquoise regions to denote the hemispheres in a and b, the 8 octahedral faces c, d,f and the 12 dodecahedral faces (or base pixels) in e. Coastlines are added for orientation.

Grid resolution

All grids use the same resolution parameter nlat_half, i.e. the number of rings on one hemisphere, Equator included. The Gaussian grids (full and reduced) do not have a ring on the equator, so their total number of rings nlat is always even and twice nlat_half. Clenshaw-Curtis grids and the HEALPix grids have a ring on the equator such their total number of rings is always odd and one less than the Gaussian grids at the same nlat_half.

HEALPix grids do not use Nside as resolution parameter

The original formulation for HEALPix grids use $N_{side}$, the number of grid points along the edges of each basepixel (8 in the figure above), SpeedyWeather.jl uses nlat_half, the number of rings on one hemisphere, Equator included, for all grids. This is done for consistency across grids. We may use $N_{side}$ for the documentation or within functions though.

Related: Effective grid resolution and Available horizontal resolutions.

Matching spectral and grid resolution

A given spectral resolution can be matched to a variety of grid resolutions. A cubic grid, for example, combines a spectral truncation $T$ with a grid resolution $N$ (=nlat_half) such that $T + 1 = N$. Using T31 and an O32 is therefore often abbreviated as Tco31 meaning that the spherical harmonics are truncated at $l_{max}=31$ in combination with N=32, i.e. 64 latitude rings in total on an octahedral Gaussian grid. In SpeedyWeather.jl the choice of the order of truncation is controlled with the dealiasing parameter in the SpectralGrid construction.

Let J be the total number of rings. Then we have

and in general $\frac{m+1}{2}T \approx J$ for m-th order truncation. So the higher the truncation order the more grid points are used in combination with the same spectral resolution. A higher truncation order therefore makes all grid-point calculations more expensive, but can represent products of terms on the grid (which will have higher wavenumber components) to a higher accuracy as more grid points are available within a given wavelength. Using a sufficiently high truncation is therefore one way to avoid aliasing. A quick overview of how the grid resolution changes when dealiasing is passed onto SpectralGrid on the FullGaussianGrid

truncdealiasingFullGaussianGrid size
31164x32
31296x48
313128x64
42196x48
422128x64
423192x96
.........

You will obtain this information every time you create a SpectralGrid(;Grid,trunc,dealiasing).

Full Gaussian grid

(called FullGaussianGrid)

The full Gaussian grid is a grid that uses regularly spaced longitudes which points that do not reduce in number towards the poles. That means for every latitude $\theta$ the longitudes $\phi$ are

\[\phi_i = \frac{2\pi (i-1)}{N_\phi}\]

with $i = 1,...,N_\phi$ the in-ring index (1-based, counting from 0˚ eastward) and $N_\phi$ the number of longitudinal points on the grid. The first longitude is therefore 0˚, meaning that there is no longitudinal offset on this grid. There are always twice as many points in zonal direction as there are in meridional, i.e. $N_\phi = 2N_\theta$. The latitudes, however, are not regular, but chosen from the $j$-th zero crossing $z_j(l)$ of the $l$-th Legendre polynomial. For $\theta$ in latitudes

\[\sin(\theta_j) = z_j(l) \]

As it can be easy to mix up latitudes, colatitudes and as the Legendre polynomials are defined in $[0,1]$ an overview of the first Gaussian latitudes (approximated for $l>2$ for brevity)

$l$Zero crossings $z_j$Latitudes [˚N]
2$\pm \tfrac{1}{\sqrt{3}}$$\pm 35.3...$
4$\pm 0.34..., \pm 0.86...$$\pm 19.9..., \pm 59.44...$
6$\pm 0.24..., \pm 0.66..., \pm 0.93...$$\pm 13.8..., \pm 41.4..., \pm 68.8...$

Only even Legendre polynomials are used, such that there is always an even number of latitudes, with no latitude on the Equator. As you can already see from this short table, the Gaussian latitudes do not nest, i.e. different resolutions through different $l$ do not share latitudes. The latitudes are also only approximately evenly spaced. Due to the Gaussian latitudes, a spectral transform with a full Gaussian grid is exact as long as the truncation is at least quadratic, see Matching spectral and grid resolution. Exactness here means that only rounding errors occur in the transform, meaning that the transform error is very small compared to other errors in a simulation. This property arises from that property of the Gauss-Legendre quadrature, which is used in the Spherical Harmonic Transform with a full Gaussian grid.

On the full Gaussian grid there are in total $N_\phi N_\theta$ grid points, which are squeezed towards the poles, making the grid area smaller and smaller following a cosine. But no points are on the poles as $z=-1$ or $1$ is never a zero crossing of the Legendre polynomials.

Octahedral Gaussian grid

(called OctahedralGaussianGrid)

The octahedral Gaussian grid is a reduced grid, i.e. the number of longitudinal points reduces towards the poles. It still uses the Gaussian latitudes from the full Gaussian grid so the exactness property of the spherical harmonic transform also holds for this grid. However, the longitudes $\phi_i$ with $i = 1,...,16+4j$ on the $j$-th latitude ring (starting with 1 around the north pole), $j=1,...,\tfrac{N_\theta}{2}$, are now, on the northern hemisphere,

\[\phi_i = \frac{2\pi (i-1)}{16 + 4j}.\]

We start with 20 points, evenly spaced, starting at 0˚E, around the first latitude ring below the north pole. The next ring has 24 points, then 28, and so on till reaching the Equator (which is not a ring). For the southern hemisphere all points are mirrored around the Equator. For more details see Malardel, 2016[M16].

Note that starting with 20 grid points on the first ring is a choice that ECMWF made with their grid for accuracy reasons. An octahedral Gaussian grid can also be defined starting with fewer grid points on the first ring. However, in SpeedyWeather.jl we follow ECMWF's definition.

The grid cells of an octahedral Gaussian grid are not exactly equal area, but are usually within a factor of two. This largely solves the efficiency problem of having too many grid points near the poles for computational, memory and data storage reasons.

Full Clenshaw-Curtis grid

(called FullClenshawGrid)

The full Clenshaw-Curtis grid is a regular longitude-latitude grid, but a specific one: The colatitudes $\theta_j$, and the longitudes $\phi_i$ are

\[\theta_j = \frac{j}{N_\theta + 1}\pi, \quad \phi_i = \frac{2\pi (i-1)}{N_\phi}\]

with $i$ the in-ring zonal index $i = 1,...,N_\phi$ and $j = 1, ... ,N_\theta$ the ring index starting with 1 around the north pole. There is no grid point on the poles, but in contrast to the Gaussian grids there is a ring on the Equator. The longitudes are shared with the full Gaussian grid. Being a full grid, also the full Clenshaw-Curtis grid suffers from too many grid points around the poles, this is addressed with the octahedral Clenshaw-Curtis grid.

The full Clenshaw-Curtis grid gets its name from the Clenshaw-Curtis quadrature that is used in the Legendre transform (see Spherical Harmonic Transform). This quadrature relies on evenly spaced latitudes, which also means that this grid nests, see Hotta and Ujiie[HU18]. More importantly for our application, the Clenshaw-Curtis grids (including the octahedral described below) allow for an exact transform with cubic truncation (see Matching spectral and grid resolution). Recall that the Gaussian latitudes allow for an exact transform with quadratic truncation, so the Clenshaw-Curtis grids require more grid points for the same spectral resolution to be exact. But compared to other errors during a simulation this error may be masked anyway.

Octahedral Clenshaw-Curtis grid

(called OctahedralClenshawGrid)

In the same as we constructed the octahedral Gaussian grid from the full Gaussian grid, the octahedral Clenshaw-Curtis grid can be constructed from the full Clenshaw-Curtis grid. It therefore shares the latitudes with the full grid, but the longitudes with the octahedral Gaussian grid.

\[\theta_j = \frac{j}{N_\theta + 1}\pi, \quad \phi_i = \frac{2\pi (i-1)}{16 + 4j}.\]

Notation as before, but note that the definition for $\phi_i$ only holds for the northern hemisphere, Equator included. The southern hemisphere is mirrored. The octahedral Clenshaw-Curtis grid inherits the exactness properties from the full Clenshaw-Curtis grid, but as it is a reduced grid, it is more efficient in terms of computational aspects and memory than the full grid. Hotta and Ujiie[HU18] describe this grid in more detail.

HEALPix grid

(called HEALPixGrid)

Technically, HEALPix grids are a class of grids that tessalate the sphere into faces that are often called basepixels. For each member of this class there are $N_\varphi$ basepixels in zonal direction and $N_\theta$ basepixels in meridional direction. For $N_\varphi = 4$ and $N_\theta = 3$ we obtain the classical HEALPix grid with $N_\varphi N_\theta = 12$ basepixels shown above in Implemented grids. Each basepixel has a quadratic number of grid points in them. There's an equatorial zone where the number of zonal grid points is constant (always $2N$, so 32 at $N=16$) and there are polar caps above and below the equatorial zone with the border at $\cos(\theta) = 2/3$ ($\theta$ in colatitudes).

Following Górski, 2004[G04], the $z=cos(\theta)$ colatitude of the $j$-th ring in the north polar cap, $j=1,...,N_{side}$ with $2N_{side} = N$ is

\[z = 1 - \frac{j^2}{3N_{side}^2}\]

and on that ring, the longitude $\phi$ of the $i$-th point ($i$ is the in-ring-index) is at

\[\phi = \frac{\pi}{2j}(i-\tfrac{1}{2})\]

The in-ring index $i$ goes from $i=1,...,4$ for the first (i.e. northern-most) ring, $i=1,...,8$ for the second ring and $i = 1,...,4j$ for the $j$-th ring in the northern polar cap.

In the north equatorial belt $j=N_{side},...,2N_{side}$ this changes to

\[z = \frac{4}{3} - \frac{2j}{3N_{side}}\]

and the longitudes change to ($i$ is always $i = 1,...,4N_{side}$ in the equatorial belt meaning the number of longitude points is constant here)

\[\phi = \frac{\pi}{2N_{side}}(i - \frac{s}{2}), \quad s = (j - N_{side} + 1) \mod 2\]

The modulo function comes in as there is an alternating longitudinal offset from the prime meridian (see Implemented grids). For the southern hemisphere the grid point locations can be obtained by mirror symmetry.

Grid cell boundaries

The cell boundaries are obtained by setting $i = k + 1/2$ or $i = k + 1/2 + j$ (half indices) into the equations above, such that $z(\phi,k)$, a function for the cosine of colatitude $z$ of index $k$ and the longitude $\phi$ is obtained. These are then (northern polar cap)

\[z = 1 - \frac{k^2}{3N_{side}^2}\left(\frac{\pi}{2\phi_t}\right)^2, \quad z = 1 - \frac{k^2}{3N_{side}^2}\left(\frac{\pi}{2\phi_t - \pi}\right)^2\]

with $\phi_t = \phi \mod \tfrac{\pi}{2}$ and in the equatorial belt

\[z = \frac{2}{3}-\frac{4k}{3N_{side}} \pm \frac{8\phi}{3\pi}\]

OctaHEALPix grid

(called OctaHEALPixGrid)

While the classic HEALPix grid is based on a dodecahedron, other choices for $N_\varphi$ and $N_\theta$ in the class of HEALPix grids will change the number of faces there are in zonal/meridional direction. With $N_\varphi = 4$ and $N_\theta = 1$ we obtain a HEALPix grid that is based on an octahedron, which has the convenient property that there are twice as many longitude points around the equator than there are latitude rings between the poles. This is a desirable for truncation as this matches the distances too, $2\pi$ around the Equator versus $\pi$ between the poles. $N_\varphi = 6, N_\theta = 2$ or $N_\varphi = 8, N_\theta = 3$ are other possible choices for this, but also more complicated. See Górski, 2004[G04] for further examples and visualizations of these grids.

We call the $N_\varphi = 4, N_\theta = 1$ HEALPix grid the OctaHEALPix grid, which combines the equal-area property of the HEALPix grids with the octahedron that's also used in the OctahedralGaussianGrid or the OctahedralClenshawGrid. As $N_\theta = 1$ there is no equatorial belt which simplifies the grid. The latitude of the $j$-th isolatitude ring on the OctaHEALPixGrid is defined by

\[z = 1 - \frac{j^2}{N^2},\]

with $j=1,...,N$, and similarly for the southern hemisphere by symmetry. On this grid $N_{side} = N$ where $N$= nlat_half, the number of latitude rings on one hemisphere, Equator included, because each of the 4 basepixels spans from pole to pole and covers a quarter of the sphere. The longitudes with in-ring- index $i = 1,...,4j$ are

\[\phi = \frac{\pi}{2j}(i - \tfrac{1}{2})\]

and again, the southern hemisphere grid points are obtained by symmetry.

Grid cell boundaries

Similar to the grid cell boundaries for the HEALPix grid, the OctaHEALPix grid's boundaries are

\[z = 1 - \frac{k^2}{N^2}\left(\frac{\pi}{2\phi_t}\right)^2, \quad z = 1 - \frac{k^2}{N^2}\left(\frac{\pi}{2\phi_t - \pi}\right)^2\]

The $3N_{side}^2$ in the denominator of the HEALPix grid came simply $N^2$ for the OctaHEALPix grid and there's no separate equation for the equatorial belt (which doesn't exist in the OctaHEALPix grid).

References

diff --git a/previews/PR387/how_to_run_speedy/index.html b/previews/PR387/how_to_run_speedy/index.html index 7e91078e9..be178a9f5 100644 --- a/previews/PR387/how_to_run_speedy/index.html +++ b/previews/PR387/how_to_run_speedy/index.html @@ -118,4 +118,4 @@ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄ -90 ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ └──┘ └────────────────────────────────────────────────────────────┘-8.0f-5 - 0 ˚E 360

You would continue this simulation (the previous run! call already integrated 10 days!) for another 5 days and storing default NetCDF output.

+ 0 ˚E 360

You would continue this simulation (the previous run! call already integrated 10 days!) for another 5 days and storing default NetCDF output.

diff --git a/previews/PR387/index.html b/previews/PR387/index.html index b70c74a2e..00d06f3b7 100644 --- a/previews/PR387/index.html +++ b/previews/PR387/index.html @@ -1,2 +1,2 @@ -Home · SpeedyWeather.jl

SpeedyWeather.jl documentation

Welcome to the documentation for SpeedyWeather.jl a global atmospheric circulation model with simple parametrizations to represent physical processes such as clouds, precipitation and radiation. SpeedyWeather in general is more a library than just a model as it exposes most of its internal functions to the user such that simulations and analysis can be interactively combined. Its user interface is built in a very modular way such that new components can be easily defined and integrated into SpeedyWeather.

Overview

SpeedyWeather.jl is uses a spherical harmonic transform to simulate the general circulation of the atmosphere using a vorticity-divergence formulation, a semi-implicit time integration and simple parameterizations to represent various climate processes: Radiation, clouds, precipitation, surface fluxes, among others.

SpeedyWeather.jl defines

  • BarotropicModel for the 2D barotropic vorticity equation
  • ShallowWaterModel for the 2D shallow water equations
  • PrimitiveDryModel for the 3D primitive equations without humidity
  • PrimitiveWetModel for the 3D primitive equations with humidity

and solves these equations in spherical coordinates as described in this documentation.

Manual outline

See the following pages of the documentation for more details

and the submodules

and the original documentation by Molteni and Kucharski.

Developers

The development of SpeedyWeather.jl is lead by Milan Klöwer and current and past contributors include

Any contributions are always welcome!

Funding

MK received funding by the European Research Council under Horizon 2020 within the ITHACA project, grant agreement number 741112 from 2021-2022. Since 2023 this project is also funded by the National Science Foundation NSF.

+Home · SpeedyWeather.jl

SpeedyWeather.jl documentation

Welcome to the documentation for SpeedyWeather.jl a global atmospheric circulation model with simple parametrizations to represent physical processes such as clouds, precipitation and radiation. SpeedyWeather in general is more a library than just a model as it exposes most of its internal functions to the user such that simulations and analysis can be interactively combined. Its user interface is built in a very modular way such that new components can be easily defined and integrated into SpeedyWeather.

Overview

SpeedyWeather.jl is uses a spherical harmonic transform to simulate the general circulation of the atmosphere using a vorticity-divergence formulation, a semi-implicit time integration and simple parameterizations to represent various climate processes: Radiation, clouds, precipitation, surface fluxes, among others.

SpeedyWeather.jl defines

  • BarotropicModel for the 2D barotropic vorticity equation
  • ShallowWaterModel for the 2D shallow water equations
  • PrimitiveDryModel for the 3D primitive equations without humidity
  • PrimitiveWetModel for the 3D primitive equations with humidity

and solves these equations in spherical coordinates as described in this documentation.

Manual outline

See the following pages of the documentation for more details

and the submodules

and the original documentation by Molteni and Kucharski.

Developers

The development of SpeedyWeather.jl is lead by Milan Klöwer and current and past contributors include

Any contributions are always welcome!

Funding

MK received funding by the European Research Council under Horizon 2020 within the ITHACA project, grant agreement number 741112 from 2021-2022. Since 2023 this project is also funded by the National Science Foundation NSF.

diff --git a/previews/PR387/installation/index.html b/previews/PR387/installation/index.html index de0cf4093..88bce8de6 100644 --- a/previews/PR387/installation/index.html +++ b/previews/PR387/installation/index.html @@ -1,3 +1,3 @@ Installation · SpeedyWeather.jl

Installation

SpeedyWeather.jl is registered in the Julia Registry. In most cases just open the Julia REPL and type

julia> using Pkg
-julia> Pkg.add("SpeedyWeather")

or, equivalently, (] opens the package manager)

julia>] add SpeedyWeather

which will automatically install the latest release and all necessary dependencies. If you run into any troubles please raise an issue.

However, you may want to make use of the latest features, then install directly from the main branch with

julia> Pkg.add(url="https://github.com/SpeedyWeather/SpeedyWeather.jl",rev="main")

other branches than main can be similarly installed. You can also type, equivalently,

julia>] add https://github.com/SpeedyWeather/SpeedyWeather.jl#main

Compatibility with Julia versions

SpeedyWeather.jl usually lives on the latest minor release and/or its predecessor. At the moment (June 2023) this means

  • Julia v1.8
  • Julia v1.9

are supported, but we dropped the support of earlier versions.

+julia> Pkg.add("SpeedyWeather")

or, equivalently, (] opens the package manager)

julia>] add SpeedyWeather

which will automatically install the latest release and all necessary dependencies. If you run into any troubles please raise an issue.

However, you may want to make use of the latest features, then install directly from the main branch with

julia> Pkg.add(url="https://github.com/SpeedyWeather/SpeedyWeather.jl",rev="main")

other branches than main can be similarly installed. You can also type, equivalently,

julia>] add https://github.com/SpeedyWeather/SpeedyWeather.jl#main

Compatibility with Julia versions

SpeedyWeather.jl usually lives on the latest minor release and/or its predecessor. At the moment (June 2023) this means

are supported, but we dropped the support of earlier versions.

diff --git a/previews/PR387/lowertriangularmatrices/index.html b/previews/PR387/lowertriangularmatrices/index.html index 50afdea40..1fefa833e 100644 --- a/previews/PR387/lowertriangularmatrices/index.html +++ b/previews/PR387/lowertriangularmatrices/index.html @@ -55,6 +55,6 @@ 3×3 Matrix{Float32}: 0.332341 0.0 0.0 0.506243 0.766275 0.0 - 1.68542 1.59366 0.996616

Note, however, that the latter includes a conversion to Matrix, which is true for many operations, including inv or \. Hence when trying to do more sophisticated linear algebra with LowerTriangularMatrix we quickly leave lower triangular-land and go back to normal matrix-land.

Function and type index

SpeedyWeather.LowerTriangularMatrices.LowerTriangularMatrixType
L = LowerTriangularMatrix{T}(v::Vector{T},m::Int,n::Int)

A lower triangular matrix implementation that only stores the non-zero entries explicitly. L<:AbstractMatrix although in general we have L[i] != Matrix(L)[i], the former skips zero entries, tha latter includes them.

source
SpeedyWeather.LowerTriangularMatrices.LowerTriangularMatrixMethod
L = LowerTriangularMatrix(M)

Create a LowerTriangularMatrix L from Matrix M by copying over the non-zero elements in M.

source
Base.fill!Method
fill!(L::LowerTriangularMatrix,x)

Fills the elements of L with x. Faster than fill!(::AbstractArray,x) as only the non-zero elements in L are assigned with x.

source
SpeedyWeather.LowerTriangularMatrices.eachharmonicMethod
unit_range = eachharmonic(L::LowerTriangular)

creates unit_range::UnitRange to loop over all non-zeros in a LowerTriangularMatrix L. Like eachindex but skips the upper triangle with zeros in L.

source
SpeedyWeather.LowerTriangularMatrices.eachharmonicMethod
unit_range = eachharmonic(Ls::LowerTriangularMatrix...)

creates unit_range::UnitRange to loop over all non-zeros in the LowerTriangularMatrices provided as arguments. Checks bounds first. All LowerTriangularMatrix's need to be of the same size. Like eachindex but skips the upper triangle with zeros in L.

source
SpeedyWeather.LowerTriangularMatrices.ij2kMethod
k = ij2k(   i::Integer,     # row index of matrix
+ 1.68542   1.59366   0.996616

Note, however, that the latter includes a conversion to Matrix, which is true for many operations, including inv or \. Hence when trying to do more sophisticated linear algebra with LowerTriangularMatrix we quickly leave lower triangular-land and go back to normal matrix-land.

Function and type index

SpeedyWeather.LowerTriangularMatrices.LowerTriangularMatrixType
L = LowerTriangularMatrix{T}(v::Vector{T},m::Int,n::Int)

A lower triangular matrix implementation that only stores the non-zero entries explicitly. L<:AbstractMatrix although in general we have L[i] != Matrix(L)[i], the former skips zero entries, tha latter includes them.

source
Base.fill!Method
fill!(L::LowerTriangularMatrix,x)

Fills the elements of L with x. Faster than fill!(::AbstractArray,x) as only the non-zero elements in L are assigned with x.

source
SpeedyWeather.LowerTriangularMatrices.eachharmonicMethod
unit_range = eachharmonic(Ls::LowerTriangularMatrix...)

creates unit_range::UnitRange to loop over all non-zeros in the LowerTriangularMatrices provided as arguments. Checks bounds first. All LowerTriangularMatrix's need to be of the same size. Like eachindex but skips the upper triangle with zeros in L.

source
SpeedyWeather.LowerTriangularMatrices.ij2kMethod
k = ij2k(   i::Integer,     # row index of matrix
             j::Integer,     # column index of matrix
-            m::Integer)     # number of rows in matrix

Converts the index pair i,j of an mxn LowerTriangularMatrix L to a single index k that indexes the same element in the corresponding vector that stores only the lower triangle (the non-zero entries) of L.

source
+ m::Integer) # number of rows in matrix

Converts the index pair i,j of an mxn LowerTriangularMatrix L to a single index k that indexes the same element in the corresponding vector that stores only the lower triangle (the non-zero entries) of L.

source diff --git a/previews/PR387/output/index.html b/previews/PR387/output/index.html index 40897184d..42c216170 100644 --- a/previews/PR387/output/index.html +++ b/previews/PR387/output/index.html @@ -63,4 +63,4 @@ • output_dt_sec::Int64: actual output time step [sec] • output_vars::Vector{Symbol}: [OPTION] which variables to output, - u, v, vor, div, pres, temp, humid + u, v, vor, div, pres, temp, humid diff --git a/previews/PR387/parameterizations/index.html b/previews/PR387/parameterizations/index.html index 624bb1b7f..3420c81f6 100644 --- a/previews/PR387/parameterizations/index.html +++ b/previews/PR387/parameterizations/index.html @@ -1,2 +1,2 @@ -Parameterizations · SpeedyWeather.jl
+Parameterizations · SpeedyWeather.jl
diff --git a/previews/PR387/primitiveequation/index.html b/previews/PR387/primitiveequation/index.html index c22304d75..82f93f21e 100644 --- a/previews/PR387/primitiveequation/index.html +++ b/previews/PR387/primitiveequation/index.html @@ -70,4 +70,4 @@ \delta \mathcal{D} &= G_D - \xi \nabla^2(\mathbf{R}\delta T + \mathbf{U} \delta \ln p_s)\\ \delta T &= G_T + \xi \mathbf{L}\delta \mathcal{D} \\ \delta \ln p_s &= G_{\ln p_s} + \xi \mathbf{W}\delta \mathcal{D} -\end{aligned}\]

Solving for $\delta \mathcal{D}$ with the "combined" tendency

\[G = G_D - \xi \nabla^2(\mathbf{R}G_T + \mathbf{U}G_{\ln p_s})\]

via

\[\delta \mathcal{D} = G - \xi^2\nabla^2(\mathbf{RL + UW})\delta \mathcal{D}\]

($\mathbf{UW}$ is a matrix of size $N \times N$) yields

\[\delta D = \left( 1 + \xi^2\nabla^2(\mathbf{RL + UW}) \right)^{-1}G = \mathbf{S}^{-1}G\]

The other tendencies $\delta T$ and $\delta \ln p_s$ are then obtained through insertion above. We may call the operator to be inverted $\mathbf{S}$ which is of size $l_{max} \times N \times N$, hence for every degree $l$ of the spherical harmonics (which the Laplace operator depends on) a $N \times N$ matrix coupling the $N$ vertical levels. Furthermore, $S$ depends

so for any changes of these the matrix inversion of $\mathbf{S}$ has to be recomputed. Otherwise the algorithm for the semi-implicit scheme is as follows

0. Precompute the linear operators $\mathbf{R,U,L,W}$ and with them the matrix inversion $\mathbf{S}^{-1}$.

Then for every time step

  1. Compute the uncorrected tendencies evaluated at the current time step for the explicit terms and the previous time step for the implicit terms.
  2. Exception in SpeedyWeather.jl is the adiabatic conversion term, which is, using $\mathbf{L}$ moved afterwards from the current $i$ to the previous time step $i-1$.
  3. Compute the combined tendency $G$ from the uncorrected tendencies $G_\mathcal{D}$, $G_T$, $G_{\ln p_s}$.
  4. With the inverted operator get the corrected tendency for divergence, $\delta \mathcal{D} = \mathbf{S}^{-1}G$.
  5. Obtain the corrected tendencies for temperature $\delta T$ and surface pressure $\delta \ln p_s$ from $\delta \mathcal{D}$.
  6. Apply Horizontal diffusion (which is only mentioned here as it further updates the tendencies).
  7. Use $\delta \mathcal{D}$, $\delta T$ and $\delta \ln p_s$ in the Leapfrog time integration.

Horizontal diffusion

Horizontal diffusion in the primitive equations is applied to vorticity $\zeta$, divergence $\mathcal{D}$, temperature $T$ and humidity $q$. In short, all variables that are advected. For the dry equations, $q=0$ and no diffusion has to be applied.

The horizontal diffusion is applied implicitly in spectral space, as already described in Horizontal diffusion for the barotropic vorticity equation.

Algorithm

The following algorithm describes a time step of the PrimitiveWetModel, for the PrimitiveDryModel humidity can be set to zero and respective steps skipped.

0. Start with initial conditions of relative vorticity $\zeta_{lm}$, divergence $D_{lm}$, temperature $T_{lm}$, humidity $q_{lm}$ and the logarithm of surface pressure $(\ln p_s)_{lm}$ in spectral space. Variables $\zeta, D, T, q$ are defined on all vertical levels, the logarithm of surface pressure only at the surface. Transform this model state to grid-point space, obtaining velocities is done as in the shallow water model

Additionally we

Now loop over

  1. Compute all tendencies of $u, v, T, q$ due to physical parameterizations in grid-point space.
  2. Compute the gradient of the logarithm of surface pressure $\nabla (\ln p_s)_{lm}$ in spectral space and convert the two fields to grid-point space. Unscale the $\cos(\theta)$ on the fly.
  3. For every layer $k$ compute the pressure flux $\mathbf{u}_k \cdot \nabla \ln p_s$ in grid-point space.
  4. For every layer $k$ compute a linearized Virtual temperature in spectral space.
  5. For every layer $k$ compute a temperature anomaly (virtual and absolute) relative to a vertical reference profile $T_k$ in grid-point space.
  6. Compute the Geopotential $\Phi$ by integrating the virtual temperature vertically in spectral space from surface to top.
  7. Integrate $u,v,D$ vertically to obtain $\bar{u},\bar{v},\bar{D}$ in grid-point space and also $\bar{D}_{lm}$ in spectral space. Store on the fly also for every layer $k$ the partial integration from 1 to $k-1$ (top to layer above). These will be used in the adiabatic term of the Temperature equation.
  8. Compute the Surface pressure tendency with the vertical averages from the previous step. For the semi-implicit time stepping
  9. For every layer $k$ compute the Vertical velocity.
  10. For every layer $k$ add the linear contribution of the Pressure gradient $RT_k (\ln p_s)_{lm}$ to the geopotential $\Phi$ in spectral space.
  11. For every layer $k$ compute the Vertical advection for $u,v,T,q$ and add it to the respective tendency.
  12. For every layer $k$ compute the tendency of $u,v$ due to Vorticity advection and the Pressure gradient $RT_v \nabla \ln p_s$ and add to the respective existing tendency. Unscale $\cos(\theta)$, transform to spectral space, take curl and divergence to obtain tendencies for $\zeta_{lm},\mathcal{D}_{lm}$.
  13. For every layer $k$ compute the adiabatic term and the horizontal advection in the Temperature equation in grid-point space, add to existing tendency and transform to spectral.
  14. For every layer $k$ compute the horizontal advection of humidity $q$ in the Humidity equation in grid-point space, add to existing tendency and transform to spectral.
  15. For every layer $k$ compute the kinetic energy $\tfrac{1}{2}(u^2 + v^2)$, transform to spectral and add to the Geopotential. For the semi-implicit time stepping also add the linear pressure gradient calculated from the previous time step. Now apply the Laplace operator and subtract from the divergence tendency.
  16. Correct the tendencies following the semi-implicit time integration to prevent fast gravity waves from causing numerical instabilities.
  17. Compute the horizontal diffusion for the advected variables $\zeta,\mathcal{D},T,q$
  18. Compute a leapfrog time step as described in Time integration with a Robert-Asselin and Williams filter
  19. Transform the new spectral state of $\zeta_{lm}$, $\mathcal{D}_{lm}$, $T_{lm}$, $q_{lm}$ and $(\ln p_s)_{lm}$ to grid-point $u,v,\zeta,\mathcal{D},T,q,\ln p_s$ as described in 0.
  20. Possibly do some output
  21. Repeat from 1.

Scaled primitive equations

References

+\end{aligned}\]

Solving for $\delta \mathcal{D}$ with the "combined" tendency

\[G = G_D - \xi \nabla^2(\mathbf{R}G_T + \mathbf{U}G_{\ln p_s})\]

via

\[\delta \mathcal{D} = G - \xi^2\nabla^2(\mathbf{RL + UW})\delta \mathcal{D}\]

($\mathbf{UW}$ is a matrix of size $N \times N$) yields

\[\delta D = \left( 1 + \xi^2\nabla^2(\mathbf{RL + UW}) \right)^{-1}G = \mathbf{S}^{-1}G\]

The other tendencies $\delta T$ and $\delta \ln p_s$ are then obtained through insertion above. We may call the operator to be inverted $\mathbf{S}$ which is of size $l_{max} \times N \times N$, hence for every degree $l$ of the spherical harmonics (which the Laplace operator depends on) a $N \times N$ matrix coupling the $N$ vertical levels. Furthermore, $S$ depends

so for any changes of these the matrix inversion of $\mathbf{S}$ has to be recomputed. Otherwise the algorithm for the semi-implicit scheme is as follows

0. Precompute the linear operators $\mathbf{R,U,L,W}$ and with them the matrix inversion $\mathbf{S}^{-1}$.

Then for every time step

  1. Compute the uncorrected tendencies evaluated at the current time step for the explicit terms and the previous time step for the implicit terms.
  2. Exception in SpeedyWeather.jl is the adiabatic conversion term, which is, using $\mathbf{L}$ moved afterwards from the current $i$ to the previous time step $i-1$.
  3. Compute the combined tendency $G$ from the uncorrected tendencies $G_\mathcal{D}$, $G_T$, $G_{\ln p_s}$.
  4. With the inverted operator get the corrected tendency for divergence, $\delta \mathcal{D} = \mathbf{S}^{-1}G$.
  5. Obtain the corrected tendencies for temperature $\delta T$ and surface pressure $\delta \ln p_s$ from $\delta \mathcal{D}$.
  6. Apply Horizontal diffusion (which is only mentioned here as it further updates the tendencies).
  7. Use $\delta \mathcal{D}$, $\delta T$ and $\delta \ln p_s$ in the Leapfrog time integration.

Horizontal diffusion

Horizontal diffusion in the primitive equations is applied to vorticity $\zeta$, divergence $\mathcal{D}$, temperature $T$ and humidity $q$. In short, all variables that are advected. For the dry equations, $q=0$ and no diffusion has to be applied.

The horizontal diffusion is applied implicitly in spectral space, as already described in Horizontal diffusion for the barotropic vorticity equation.

Algorithm

The following algorithm describes a time step of the PrimitiveWetModel, for the PrimitiveDryModel humidity can be set to zero and respective steps skipped.

0. Start with initial conditions of relative vorticity $\zeta_{lm}$, divergence $D_{lm}$, temperature $T_{lm}$, humidity $q_{lm}$ and the logarithm of surface pressure $(\ln p_s)_{lm}$ in spectral space. Variables $\zeta, D, T, q$ are defined on all vertical levels, the logarithm of surface pressure only at the surface. Transform this model state to grid-point space, obtaining velocities is done as in the shallow water model

Additionally we

Now loop over

  1. Compute all tendencies of $u, v, T, q$ due to physical parameterizations in grid-point space.
  2. Compute the gradient of the logarithm of surface pressure $\nabla (\ln p_s)_{lm}$ in spectral space and convert the two fields to grid-point space. Unscale the $\cos(\theta)$ on the fly.
  3. For every layer $k$ compute the pressure flux $\mathbf{u}_k \cdot \nabla \ln p_s$ in grid-point space.
  4. For every layer $k$ compute a linearized Virtual temperature in spectral space.
  5. For every layer $k$ compute a temperature anomaly (virtual and absolute) relative to a vertical reference profile $T_k$ in grid-point space.
  6. Compute the Geopotential $\Phi$ by integrating the virtual temperature vertically in spectral space from surface to top.
  7. Integrate $u,v,D$ vertically to obtain $\bar{u},\bar{v},\bar{D}$ in grid-point space and also $\bar{D}_{lm}$ in spectral space. Store on the fly also for every layer $k$ the partial integration from 1 to $k-1$ (top to layer above). These will be used in the adiabatic term of the Temperature equation.
  8. Compute the Surface pressure tendency with the vertical averages from the previous step. For the semi-implicit time stepping
  9. For every layer $k$ compute the Vertical velocity.
  10. For every layer $k$ add the linear contribution of the Pressure gradient $RT_k (\ln p_s)_{lm}$ to the geopotential $\Phi$ in spectral space.
  11. For every layer $k$ compute the Vertical advection for $u,v,T,q$ and add it to the respective tendency.
  12. For every layer $k$ compute the tendency of $u,v$ due to Vorticity advection and the Pressure gradient $RT_v \nabla \ln p_s$ and add to the respective existing tendency. Unscale $\cos(\theta)$, transform to spectral space, take curl and divergence to obtain tendencies for $\zeta_{lm},\mathcal{D}_{lm}$.
  13. For every layer $k$ compute the adiabatic term and the horizontal advection in the Temperature equation in grid-point space, add to existing tendency and transform to spectral.
  14. For every layer $k$ compute the horizontal advection of humidity $q$ in the Humidity equation in grid-point space, add to existing tendency and transform to spectral.
  15. For every layer $k$ compute the kinetic energy $\tfrac{1}{2}(u^2 + v^2)$, transform to spectral and add to the Geopotential. For the semi-implicit time stepping also add the linear pressure gradient calculated from the previous time step. Now apply the Laplace operator and subtract from the divergence tendency.
  16. Correct the tendencies following the semi-implicit time integration to prevent fast gravity waves from causing numerical instabilities.
  17. Compute the horizontal diffusion for the advected variables $\zeta,\mathcal{D},T,q$
  18. Compute a leapfrog time step as described in Time integration with a Robert-Asselin and Williams filter
  19. Transform the new spectral state of $\zeta_{lm}$, $\mathcal{D}_{lm}$, $T_{lm}$, $q_{lm}$ and $(\ln p_s)_{lm}$ to grid-point $u,v,\zeta,\mathcal{D},T,q,\ln p_s$ as described in 0.
  20. Possibly do some output
  21. Repeat from 1.

Scaled primitive equations

References

diff --git a/previews/PR387/ringgrids/index.html b/previews/PR387/ringgrids/index.html index b3f263f40..0913596b4 100644 --- a/previews/PR387/ringgrids/index.html +++ b/previews/PR387/ringgrids/index.html @@ -1,93 +1,93 @@ Submodule: RingGrids · SpeedyWeather.jl

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.384495    0.0027913  -1.87479    -1.90022
-  0.640656    0.108701    1.56733     1.99488
-  1.44464    -0.222072   -0.0454803   1.10525
-  1.56495    -1.25712    -0.719962   -0.307085
-  0.551142   -1.03774     0.0674079  -0.350342
- -0.0315823   0.727755   -0.0536263   0.272658
-  2.24793     0.553278    1.09082     0.827939
- -0.122012    0.0827115  -0.6224     -0.191043
grid = FullGaussianGrid(map)
32-element, 4-ring FullGaussianGrid{Float32}:
- -0.38449454
-  0.64065605
-  1.4446386
-  1.5649458
-  0.5511419
- -0.031582333
-  2.247925
- -0.12201178
-  0.0027912955
-  0.10870084
+  0.0323783   1.24392    1.11506    0.219666
+ -1.48427    -1.58416    0.403698  -0.840577
+ -0.399655   -0.171206  -0.536831  -0.878711
+  1.44365     0.416995   1.34314   -0.140653
+ -0.722154    0.406199   0.138884  -0.905367
+ -2.56936     1.35457    0.698576   0.719075
+ -1.27974    -0.771802   1.4471     0.800861
+  0.125739    0.3444    -2.22072   -0.84624
grid = FullGaussianGrid(map)
32-element, 4-ring FullGaussianGrid{Float32}:
+  0.032378264
+ -1.4842738
+ -0.39965534
+  1.443653
+ -0.7221541
+ -2.5693562
+ -1.2797434
+  0.1257392
+  1.2439239
+ -1.5841565
   ⋮
- -0.62240005
- -1.9002167
-  1.9948833
-  1.1052519
- -0.30708522
- -0.35034212
-  0.27265763
-  0.82793915
- -0.19104256

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.38449454
-  0.64065605
-  1.4446386
-  1.5649458
-  0.5511419
- -0.031582333
-  2.247925
- -0.12201178
-  0.0027912955
-  0.10870084
+ -2.2207184
+  0.21966629
+ -0.84057707
+ -0.87871104
+ -0.14065346
+ -0.9053671
+  0.71907514
+  0.80086076
+ -0.84623975

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.032378264
+ -1.4842738
+ -0.39965534
+  1.443653
+ -0.7221541
+ -2.5693562
+ -1.2797434
+  0.1257392
+  1.2439239
+ -1.5841565
   ⋮
- -0.62240005
- -1.9002167
-  1.9948833
-  1.1052519
- -0.30708522
- -0.35034212
-  0.27265763
-  0.82793915
- -0.19104256

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
+ -2.2207184
+  0.21966629
+ -0.84057707
+ -0.87871104
+ -0.14065346
+ -0.9053671
+  0.71907514
+  0.80086076
+ -0.84623975

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.3745
- -0.1188
- -0.2306
-  1.326
- -1.133
- -0.3777
- -1.942
- -1.559
- -0.1375
- -1.305
+  0.3687
+ -0.896
+  0.608
+ -0.7554
+ -0.7876
+ -1.048
+ -0.6865
+ -1.214
+ -0.586
+ -0.822
   ⋮
-  0.0548
- -1.704
- -2.68
-  0.1628
- -0.2048
-  0.5054
-  0.4224
- -0.1719
-  1.914

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
+  1.637
+  0.8174
+  0.3098
+  0.515
+  1.309
+  0.719
+  1.849
+  0.3088
+  0.771

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)
 plot(grid)
                   48-ring OctahedralGaussianGrid{Float64}                
        ┌────────────────────────────────────────────────────────────┐  3  
-    90 ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ┌──┐
-       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
-       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
-       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
-       ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
-       ▄▄▄▄▄▄▄▄▄▄▄ ▄▄
-       ▄▄▄▄▄ ▄▄
-    ˚N ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
-       ▄▄▄▄ ▄▄
-       ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
-       ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
-       ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
-       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
-       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
-   -90 ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ └──┘
+    90 ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ┌──┐
+       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
+       ▄▄▄▄▄▄▄▄▄▄ ▄▄
+       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
+       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
+       ▄▄▄▄ ▄▄
+       ▄▄ ▄▄
+    ˚N ▄▄▄▄▄▄▄▄▄▄ ▄▄
+       ▄▄▄▄▄▄▄▄▄▄ ▄▄
+       ▄▄▄▄▄▄▄▄▄▄ ▄▄
+       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
+       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
+       ▄▄▄▄ ▄▄
+       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
+   -90 ▄▄▄▄▄▄▄▄▄▄▄▄▄ └──┘
        └────────────────────────────────────────────────────────────┘ -3  
         0                           ˚E                           360      

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}:
@@ -111,161 +111,161 @@
 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}:
-  0.8020713
- -1.583676
- -1.2173315
- -0.95042676
-  0.2930733
-  0.047305822
-  2.0571434
-  0.63778573
-  1.1166124
- -0.10542589
+  1.4255751
+ -1.309481
+  0.6231467
+ -1.7938612
+  0.07164579
+  0.24647328
+ -0.90753895
+  0.8543748
+ -0.6990326
+  0.58978236
   ⋮
-  0.60044277
-  1.1339539
-  0.42115515
- -0.15895349
-  0.4908934
- -1.2637861
-  1.771968
- -1.7271228
-  0.5047978
interpolate(FullGaussianGrid,grid)
128-element, 8-ring FullGaussianGrid{Float64}:
-  0.8020712733268738
- -1.492089867591858
- -1.083879142999649
- -0.017801761627197266
-  0.04730582237243652
-  1.7023040056228638
-  0.8771990835666656
-  0.20008373260498047
-  1.0139169692993164
-  0.5312594771385193
+ -1.6980417
+  1.5007036
+ -1.8604438
+  0.28537956
+  0.5705362
+ -1.3501593
+ -0.06627656
+  0.81239814
+ -0.4265069
interpolate(FullGaussianGrid,grid)
128-element, 8-ring FullGaussianGrid{Float64}:
+  1.4255751371383667
+ -0.8263241052627563
+ -0.5853572487831116
+ -0.39473098516464233
+  0.2464732825756073
+ -0.46706050634384155
+  0.0776711106300354
+  0.267578661441803
+ -1.0115002393722534
+ -0.16445070505142212
   ⋮
- -0.5321667790412903
- -0.4901690185070038
-  0.7338205575942993
-  0.7775545418262482
- -0.013926327228545088
-  0.4908933937549591
- -0.504847526550293
-  0.0224226713180542
- -0.053182244300842285

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.6226552626091908
- -0.7296891483012045
- -0.8340302019262598
- -0.6601903855184942
- -0.28746673128129263
-  0.24856653307894822
-  0.10925492836286746
-  1.248517583170355
-  0.8327275485095867
-  0.6737580824845959
+  0.35125666856765747
+ -0.6507344245910645
+ -0.8983553647994995
+ -0.1798701286315918
+ -0.25107622146606823
+  0.570536196231842
+ -1.0291886031627655
+  0.37306078523397446
+ -0.11678066849708557

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.9876192403267864
+ -0.562728559682099
+  0.0035992605104878256
+ -0.38024061305323653
+ -0.7792992702468243
+  0.08648053012714597
+  0.18558029501273607
+ -0.4685635962081578
+  0.19959058342871142
+  0.07075907288502434
   ⋮
-  0.48107861396160895
-  0.10711932044967123
- -0.08227290759797487
-  0.2860883304912544
- -0.7085396591239892
-  0.4691718584940371
- -0.03257096317353109
- -0.7165727807431477
-  0.5254834121427282

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.5338499f0

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.5338499
- 0.41140273
- 0.24750414

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)
+ -0.14636683744693166
+ -0.8029747093368653
+  0.20242928660925114
+  0.36406836797782877
+ -0.7246647087052893
+ -0.36020319438703347
+  0.22974327150775223
+  0.24767977060844396
+ -0.4368863285367758

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.15453684f0

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.15453684
+ -0.23148234
+ -0.19469064

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}}(SpeedyWeather.RingGrids.GridGeometry{HEALPixGrid{Float64}}(4, 7, 48, [90.0, 66.44353569089876, 41.8103148957786, 19.471220634490685, 0.0, -19.47122063449071, -41.81031489577862, -66.44353569089876, -90.00000000000001], [45.0, 135.0, 225.0, 315.0, 22.5, 67.5, 112.5, 157.5, 202.5, 247.49999999999997  …  112.5, 157.5, 202.5, 247.49999999999997, 292.5, 337.5, 45.0, 135.0, 225.0, 315.0], UnitRange{Int64}[1:4, 5:12, 13:20, 21:28, 29:36, 37:44, 45:48], [4, 8, 8, 8, 8, 8, 4], [45.0, 22.5, 0.0, 22.5, 0.0, 22.5, 45.0]), SpeedyWeather.RingGrids.AnvilLocator{Float64}(264, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0  …  7, 7, 7, 7, 7, 7, 7, 7, 7, 7], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0  …  46, 46, 47, 47, 47, 47, 47, 48, 48, 48], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0  …  47, 47, 48, 48, 48, 48, 48, 45, 45, 45], [4, 4, 4, 1, 1, 1, 1, 1, 1, 2  …  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], [1, 1, 1, 2, 2, 2, 2, 2, 2, 3  …  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], [0.6367678868600251, 0.6367678868600251, 0.6367678868600251, 0.6367678868600251, 0.6367678868600251, 0.6367678868600251, 0.6367678868600251, 0.6367678868600251, 0.6367678868600251, 0.6367678868600251  …  0.3632321131399741, 0.3632321131399741, 0.3632321131399741, 0.3632321131399741, 0.3632321131399741, 0.3632321131399741, 0.3632321131399741, 0.3632321131399741, 0.3632321131399741, 0.3632321131399741], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0  …  0.8333333333333333, 0.9999999999999998, 0.16666666666666652, 0.33333333333333304, 0.5, 0.6666666666666665, 0.8333333333333326, 0.0, 0.16666666666666652, 0.3333333333333326], [0.5, 0.6666666666666667, 0.8333333333333333, 0.0, 0.16666666666666657, 0.33333333333333315, 0.5, 0.6666666666666666, 0.8333333333333331, 0.0  …  0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]))

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.633179929288644
- 0.6405666295469195
- 0.6479533298051949
- 0.6553400300634704
- 0.6502523602266344
- 0.6451646903897981
- 0.640077020552962
- 0.6349893507161259
- 0.6299016808792897
- 0.6248140110424536
+ 0.6654969470097967
+ 0.6931246696185578
+ 0.7207523922273189
+ 0.7483801148360801
+ 0.7509249476597815
+ 0.7534697804834828
+ 0.7560146133071842
+ 0.7585594461308856
+ 0.7611042789545869
+ 0.7636491117782882
  ⋮
- 0.7317103838740944
- 0.7621720800203607
- 0.792633776166627
- 0.8230954723128935
- 0.8535571684591597
- 0.884018864605426
- 0.9144805607516925
- 0.9073782261241907
- 0.9002758914966889

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);
+ 0.7116468020222301
+ 0.6820951340028104
+ 0.6525434659833907
+ 0.6229917979639711
+ 0.5934401299445514
+ 0.5638884619251319
+ 0.534336793905712
+ 0.5073764005689011
+ 0.4804160072320902

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.6333
- 0.6406
- 0.648
- 0.6553
- 0.6504
- 0.645
- 0.64
- 0.635
- 0.63
- 0.625
+ 0.6655
+ 0.6934
+ 0.7207
+ 0.7485
+ 0.751
+ 0.7534
+ 0.756
+ 0.759
+ 0.761
+ 0.7637
  ⋮
- 0.732
- 0.762
- 0.7925
- 0.823
- 0.8535
- 0.884
- 0.9146
- 0.907
- 0.9004

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)
+ 0.7114
+ 0.682
+ 0.6523
+ 0.623
+ 0.5933
+ 0.564
+ 0.534
+ 0.5073
+ 0.4805

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.8755
-  0.8257
-  0.7754
-  0.7256
-  0.6753
-  0.5312
-  0.556
-  0.581
-  0.606
-  0.689
+  0.09125
+  0.1506
+  0.21
+  0.2693
+  0.3286
+  0.538
+  0.8696
+  1.201
+  1.532
+  1.582
   ⋮
- -0.468
- -0.3633
- -0.0679
-  0.2275
-  0.523
-  0.6514
-  0.551
-  0.4504
-  0.35

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
+  1.0205
+  1.049
+  0.8564
+  0.665
+  0.4731
+  0.1084
+  0.05106
+ -0.006535
+ -0.06415

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}(SpeedyWeather.RingGrids.GridGeometry{HEALPixGrid}(24, 47, 1728, [90.0, 86.10076357950555, 82.19700324028634, 78.28414760510762, 74.35752898700072, 70.41233167174659, 66.44353569089876, 62.445854167002665, 58.41366190347208, 54.34091230386124  …  -54.340912303861266, -58.41366190347208, -62.445854167002665, -66.44353569089876, -70.41233167174661, -74.35752898700072, -78.28414760510763, -82.19700324028634, -86.10076357950557, -90.00000000000001], [45.0, 135.0, 225.0, 315.0, 22.5, 67.5, 112.5, 157.5, 202.5, 247.49999999999997  …  112.5, 157.5, 202.5, 247.49999999999997, 292.5, 337.5, 45.0, 135.0, 225.0, 315.0], UnitRange{Int64}[1:4, 5:12, 13:24, 25:40, 41:60, 61:84, 85:112, 113:144, 145:180, 181:220  …  1509:1548, 1549:1584, 1585:1616, 1617:1644, 1645:1668, 1669:1688, 1689:1704, 1705:1716, 1717:1724, 1725:1728], [4, 8, 12, 16, 20, 24, 28, 32, 36, 40  …  40, 36, 32, 28, 24, 20, 16, 12, 8, 4], [45.0, 22.5, 14.999999999999998, 11.25, 9.0, 7.499999999999999, 6.428571428571429, 5.625, 5.0, 4.5  …  4.5, 5.0, 5.625, 6.428571428571429, 7.499999999999999, 9.0, 11.25, 14.999999999999998, 22.5, 45.0]), SpeedyWeather.RingGrids.AnvilLocator{Float32}(10, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], Float32[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], Float32[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], Float32[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]))

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.18708100338198244
- 0.31573544670577497
- 0.32389114440649
- 0.4460803598687273
- 0.6025352472673648
- 0.28408305945307755
- 0.5284327626692358
- 0.17517369661811344
- 0.4742338388562279
- 0.5756077276735999

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.187080998624228
- 0.3157354453811603
- 0.3238911365719386
- 0.4460803737642498
- 0.6025352459964833
- 0.28408306030897285
- 0.5284327686553335
- 0.1751737040281226
- 0.4742338422694344
- 0.5756077268826814

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
+ 0.588230785316095
+ 0.8642179847856983
+ 0.7622365191567455
+ 0.36272690191511137
+ 0.6500977513224726
+ 0.5387626538104123
+ 0.5349327817054701
+ 0.2688604447917838
+ 0.31697114719702313
+ 0.5178650673443685

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.5882307855597485
+ 0.8642179844229808
+ 0.7622365265119007
+ 0.36272689778751194
+ 0.6500977516887622
+ 0.5387626559331357
+ 0.5349327893548189
+ 0.26886044495477024
+ 0.31697114944356103
+ 0.5178650678077409

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
@@ -278,26 +278,26 @@
           |<  Δ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
+^ 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
+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,
+                    ) 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,
+        )

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!(
+        )

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   >|
 
@@ -309,38 +309,38 @@
     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(
+

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
+

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!(
+

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(
+

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(
+

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
+

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

source diff --git a/previews/PR387/run_0001/progress.txt b/previews/PR387/run_0001/progress.txt index 4033d6e6d..00dee32a3 100644 --- a/previews/PR387/run_0001/progress.txt +++ b/previews/PR387/run_0001/progress.txt @@ -1,4 +1,4 @@ -Starting SpeedyWeather.jl run 0001 on Mon, 16 Oct 2023 21:35:44 +Starting SpeedyWeather.jl run 0001 on Mon, 16 Oct 2023 21:37:14 Integrating: SpectralGrid: ├ Spectral: T63 LowerTriangularMatrix{Complex{Float32}}, radius = 6.371e6 m @@ -10,24 +10,24 @@ Time: 6.0 days at Δt = 900s All data will be stored in /home/runner/work/SpeedyWeather.jl/SpeedyWeather.jl/docs/build/run_0001 5%, ETA: 0:00:01, Inf millenia/day - 10%, ETA: 0:00:01, 1393.65 years/day - 15%, ETA: 0:00:01, 1050.00 years/day - 20%, ETA: 0:00:01, 1391.86 years/day - 25%, ETA: 0:00:01, 1165.27 years/day - 30%, ETA: 0:00:01, 1041.83 years/day - 35%, ETA: 0:00:01, 972.78 years/day - 40%, ETA: 0:00:01, 1108.29 years/day - 45%, ETA: 0:00:01, 1036.10 years/day - 50%, ETA: 0:00:01, 988.14 years/day - 55%, ETA: 0:00:01, 1087.98 years/day - 60%, ETA: 0:00:01, 1036.56 years/day - 65%, ETA: 0:00:01, 948.25 years/day - 70%, ETA: 0:00:00, 1021.97 years/day - 75%, ETA: 0:00:00, 991.05 years/day - 80%, ETA: 0:00:00, 963.03 years/day - 85%, ETA: 0:00:00, 1023.87 years/day - 90%, ETA: 0:00:00, 999.39 years/day - 95%, ETA: 0:00:00, 976.18 years/day -100%, ETA: 0:00:00, 955.43 years/day + 10%, ETA: 0:00:01, 1376.56 years/day + 15%, ETA: 0:00:01, 1041.39 years/day + 20%, ETA: 0:00:01, 1380.45 years/day + 25%, ETA: 0:00:01, 1143.69 years/day + 30%, ETA: 0:00:01, 1036.52 years/day + 35%, ETA: 0:00:01, 1211.28 years/day + 40%, ETA: 0:00:01, 1088.68 years/day + 45%, ETA: 0:00:01, 1026.59 years/day + 50%, ETA: 0:00:01, 982.43 years/day + 55%, ETA: 0:00:01, 1081.70 years/day + 60%, ETA: 0:00:01, 1026.92 years/day + 65%, ETA: 0:00:01, 990.54 years/day + 70%, ETA: 0:00:00, 1067.55 years/day + 75%, ETA: 0:00:00, 988.26 years/day + 80%, ETA: 0:00:00, 962.52 years/day + 85%, ETA: 0:00:00, 1023.34 years/day + 90%, ETA: 0:00:00, 995.12 years/day + 95%, ETA: 0:00:00, 973.60 years/day +100%, ETA: 0:00:00, 1023.53 years/day -Integration done in 1 second, 480 milliseconds. +Integration done in 1 second, 380 milliseconds. diff --git a/previews/PR387/run_0002/progress.txt b/previews/PR387/run_0002/progress.txt index ae404c3ef..132014fdd 100644 --- a/previews/PR387/run_0002/progress.txt +++ b/previews/PR387/run_0002/progress.txt @@ -1,4 +1,4 @@ -Starting SpeedyWeather.jl run 0002 on Mon, 16 Oct 2023 21:36:12 +Starting SpeedyWeather.jl run 0002 on Mon, 16 Oct 2023 21:37:42 Integrating: SpectralGrid: ├ Spectral: T63 LowerTriangularMatrix{Complex{Float32}}, radius = 6.371e6 m @@ -9,25 +9,25 @@ Time: 12.0 days at Δt = 900s All data will be stored in /home/runner/work/SpeedyWeather.jl/SpeedyWeather.jl/docs/build/run_0002 - 5%, ETA: 0:00:03, 1376.54 years/day - 10%, ETA: 0:00:02, 1390.33 years/day - 15%, ETA: 0:00:02, 1042.56 years/day - 20%, ETA: 0:00:02, 1103.77 years/day - 25%, ETA: 0:00:02, 1152.08 years/day - 30%, ETA: 0:00:02, 1179.79 years/day - 35%, ETA: 0:00:02, 1071.73 years/day - 40%, ETA: 0:00:02, 1106.27 years/day - 45%, ETA: 0:00:01, 1130.90 years/day - 50%, ETA: 0:00:01, 1060.82 years/day - 55%, ETA: 0:00:01, 1085.28 years/day - 60%, ETA: 0:00:01, 1102.59 years/day - 65%, ETA: 0:00:01, 1055.20 years/day - 70%, ETA: 0:00:01, 1073.31 years/day - 75%, ETA: 0:00:01, 1036.75 years/day - 80%, ETA: 0:00:01, 1051.61 years/day - 85%, ETA: 0:00:00, 1067.23 years/day - 90%, ETA: 0:00:00, 1035.68 years/day - 95%, ETA: 0:00:00, 1031.02 years/day -100%, ETA: 0:00:00, 1043.94 years/day + 5%, ETA: 0:00:03, 1386.89 years/day + 10%, ETA: 0:00:02, 1404.86 years/day + 15%, ETA: 0:00:02, 1398.98 years/day + 20%, ETA: 0:00:02, 1119.48 years/day + 25%, ETA: 0:00:02, 1158.51 years/day + 30%, ETA: 0:00:02, 1193.55 years/day + 35%, ETA: 0:00:02, 1083.90 years/day + 40%, ETA: 0:00:02, 1114.29 years/day + 45%, ETA: 0:00:01, 1137.58 years/day + 50%, ETA: 0:00:01, 1071.27 years/day + 55%, ETA: 0:00:01, 1091.28 years/day + 60%, ETA: 0:00:01, 1110.71 years/day + 65%, ETA: 0:00:01, 1060.63 years/day + 70%, ETA: 0:00:01, 1078.29 years/day + 75%, ETA: 0:00:01, 1095.53 years/day + 80%, ETA: 0:00:01, 1057.65 years/day + 85%, ETA: 0:00:00, 1073.52 years/day + 90%, ETA: 0:00:00, 1086.73 years/day + 95%, ETA: 0:00:00, 1036.39 years/day +100%, ETA: 0:00:00, 1049.65 years/day -Integration done in 2 seconds, 720 milliseconds. +Integration done in 2 seconds, 700 milliseconds. diff --git a/previews/PR387/run_0003/progress.txt b/previews/PR387/run_0003/progress.txt index 6b0d712de..319dc2129 100644 --- a/previews/PR387/run_0003/progress.txt +++ b/previews/PR387/run_0003/progress.txt @@ -1,4 +1,4 @@ -Starting SpeedyWeather.jl run 0003 on Mon, 16 Oct 2023 21:36:25 +Starting SpeedyWeather.jl run 0003 on Mon, 16 Oct 2023 21:37:55 Integrating: SpectralGrid: ├ Spectral: T63 LowerTriangularMatrix{Complex{Float32}}, radius = 6.371e6 m diff --git a/previews/PR387/run_0004/progress.txt b/previews/PR387/run_0004/progress.txt index 654950256..fbea7e60f 100644 --- a/previews/PR387/run_0004/progress.txt +++ b/previews/PR387/run_0004/progress.txt @@ -1,4 +1,4 @@ -Starting SpeedyWeather.jl run 0004 on Mon, 16 Oct 2023 21:36:32 +Starting SpeedyWeather.jl run 0004 on Mon, 16 Oct 2023 21:38:02 Integrating: SpectralGrid: ├ Spectral: T127 LowerTriangularMatrix{Complex{Float32}}, radius = 6.371e6 m diff --git a/previews/PR387/run_0005/progress.txt b/previews/PR387/run_0005/progress.txt index 1cb112932..68fbca374 100644 --- a/previews/PR387/run_0005/progress.txt +++ b/previews/PR387/run_0005/progress.txt @@ -1,4 +1,4 @@ -Starting SpeedyWeather.jl run 0005 on Mon, 16 Oct 2023 21:36:45 +Starting SpeedyWeather.jl run 0005 on Mon, 16 Oct 2023 21:38:15 Integrating: SpectralGrid: ├ Spectral: T31 LowerTriangularMatrix{Complex{Float32}}, radius = 6.371e6 m @@ -9,17 +9,17 @@ Time: 9.0 days at Δt = 1800s All data will be stored in /home/runner/work/SpeedyWeather.jl/SpeedyWeather.jl/docs/build/run_0005 - 5%, ETA: 0:02:26, Inf millenia/day - 10%, ETA: 0:01:10, Inf millenia/day - 15%, ETA: 0:00:46, Inf millenia/day - 20%, ETA: 0:00:33, Inf millenia/day - 25%, ETA: 0:00:26, Inf millenia/day - 30%, ETA: 0:00:22, Inf millenia/day - 35%, ETA: 0:00:18, Inf millenia/day - 40%, ETA: 0:00:15, Inf millenia/day + 5%, ETA: 0:02:16, Inf millenia/day + 10%, ETA: 0:01:05, Inf millenia/day + 15%, ETA: 0:00:43, Inf millenia/day + 20%, ETA: 0:00:31, Inf millenia/day + 25%, ETA: 0:00:24, Inf millenia/day + 30%, ETA: 0:00:21, Inf millenia/day + 35%, ETA: 0:00:17, Inf millenia/day + 40%, ETA: 0:00:14, Inf millenia/day 45%, ETA: 0:00:12, Inf millenia/day 50%, ETA: 0:00:10, Inf millenia/day - 55%, ETA: 0:00:09, Inf millenia/day + 55%, ETA: 0:00:08, Inf millenia/day 60%, ETA: 0:00:07, Inf millenia/day 65%, ETA: 0:00:06, Inf millenia/day 70%, ETA: 0:00:05, Inf millenia/day diff --git a/previews/PR387/run_test/progress.txt b/previews/PR387/run_test/progress.txt index b7eceba69..8d355b357 100644 --- a/previews/PR387/run_test/progress.txt +++ b/previews/PR387/run_test/progress.txt @@ -1,4 +1,4 @@ -Starting SpeedyWeather.jl run test on Mon, 16 Oct 2023 21:35:23 +Starting SpeedyWeather.jl run test on Mon, 16 Oct 2023 21:36:53 Integrating: SpectralGrid: ├ Spectral: T63 LowerTriangularMatrix{Complex{Float32}}, radius = 6.371e6 m @@ -9,25 +9,25 @@ Time: 5.0 days at Δt = 450s All data will be stored in /home/runner/work/SpeedyWeather.jl/SpeedyWeather.jl/docs/build/run_test - 5%, ETA: 0:00:03, 572.55 years/day - 10%, ETA: 0:00:03, 577.05 years/day - 15%, ETA: 0:00:02, 434.65 years/day - 20%, ETA: 0:00:02, 464.88 years/day - 25%, ETA: 0:00:02, 413.22 years/day - 30%, ETA: 0:00:02, 432.26 years/day - 35%, ETA: 0:00:02, 449.39 years/day - 40%, ETA: 0:00:02, 419.08 years/day - 45%, ETA: 0:00:02, 417.92 years/day - 50%, ETA: 0:00:01, 399.71 years/day - 55%, ETA: 0:00:01, 411.46 years/day - 60%, ETA: 0:00:01, 421.63 years/day - 65%, ETA: 0:00:01, 406.48 years/day - 70%, ETA: 0:00:01, 415.33 years/day - 75%, ETA: 0:00:01, 422.63 years/day - 80%, ETA: 0:00:01, 410.73 years/day - 85%, ETA: 0:00:00, 417.26 years/day - 90%, ETA: 0:00:00, 407.22 years/day - 95%, ETA: 0:00:00, 413.03 years/day -100%, ETA: 0:00:00, 418.25 years/day + 5%, ETA: 0:00:03, 569.11 years/day + 10%, ETA: 0:00:03, 570.14 years/day + 15%, ETA: 0:00:02, 428.50 years/day + 20%, ETA: 0:00:02, 460.18 years/day + 25%, ETA: 0:00:02, 476.82 years/day + 30%, ETA: 0:00:02, 428.93 years/day + 35%, ETA: 0:00:02, 424.24 years/day + 40%, ETA: 0:00:02, 439.00 years/day + 45%, ETA: 0:00:02, 414.16 years/day + 50%, ETA: 0:00:01, 425.75 years/day + 55%, ETA: 0:00:01, 436.25 years/day + 60%, ETA: 0:00:01, 418.20 years/day + 65%, ETA: 0:00:01, 427.01 years/day + 70%, ETA: 0:00:01, 435.17 years/day + 75%, ETA: 0:00:01, 421.17 years/day + 80%, ETA: 0:00:01, 428.20 years/day + 85%, ETA: 0:00:00, 434.98 years/day + 90%, ETA: 0:00:00, 422.67 years/day + 95%, ETA: 0:00:00, 428.96 years/day +100%, ETA: 0:00:00, 433.68 years/day -Integration done in 2 seconds, 820 milliseconds. +Integration done in 2 seconds, 720 milliseconds. diff --git a/previews/PR387/search/index.html b/previews/PR387/search/index.html index abd29e4ab..d4eab187c 100644 --- a/previews/PR387/search/index.html +++ b/previews/PR387/search/index.html @@ -1,2 +1,2 @@ -Search · SpeedyWeather.jl

Loading search...

    +Search · SpeedyWeather.jl

    Loading search...

      diff --git a/previews/PR387/search_index.js b/previews/PR387/search_index.js index c43fa150a..0a29a5bae 100644 --- a/previews/PR387/search_index.js +++ b/previews/PR387/search_index.js @@ -1,3 +1,3 @@ var documenterSearchIndex = {"docs": -[{"location":"parameterizations/#parameterizations","page":"Parameterizations","title":"Parameterizations","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"This page describes the mathematical formulation of the parameterizations used in SpeedyWeather.jl to represent physical processes in the atmosphere. Every section is followed by a brief description of implementation details.","category":"page"},{"location":"parameterizations/#Convection","page":"Parameterizations","title":"Convection","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"more to come ...","category":"page"},{"location":"parameterizations/#Large-scale-condensation","page":"Parameterizations","title":"Large-scale condensation","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"more to come ...","category":"page"},{"location":"parameterizations/#Clouds","page":"Parameterizations","title":"Clouds","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"more to come ...","category":"page"},{"location":"parameterizations/#Short-wave-radiation","page":"Parameterizations","title":"Short-wave radiation","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"more to come ...","category":"page"},{"location":"parameterizations/#Long-wave-radiation","page":"Parameterizations","title":"Long-wave radiation","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"more to come ...","category":"page"},{"location":"parameterizations/#Surface-fluxes-of-momentum-and-energy","page":"Parameterizations","title":"Surface fluxes of momentum and energy","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"more to come ...","category":"page"},{"location":"parameterizations/#Vertical-diffusion","page":"Parameterizations","title":"Vertical diffusion","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"more to come ...","category":"page"},{"location":"barotropic/#Barotropic-vorticity-model","page":"Barotropic model","title":"Barotropic vorticity model","text":"","category":"section"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"The barotropic vorticity model describes the evolution of a 2D non-divergent flow with velocity components mathbfu = (uv) through self-advection, forces and dissipation. Due to the non-divergent nature of the flow, it can be described by (the vertical component) of the relative vorticity zeta = nabla times mathbfu.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"The dynamical core presented here to solve the barotropic vorticity equations largely follows the idealized models with spectral dynamics developed at the Geophysical Fluid Dynamics Laboratory[1]: A barotropic vorticity model[2].","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"Many concepts of the Shallow water model and the Primitive equation model are similar, such that for example horizontal diffusion and the Time integration are only explained here.","category":"page"},{"location":"barotropic/#Barotropic-vorticity-equation","page":"Barotropic model","title":"Barotropic vorticity equation","text":"","category":"section"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"The barotropic vorticity equation is the prognostic equation that describes the time evolution of relative vorticity zeta with advection, Coriolis force, forcing and diffusion in a single global layer on the sphere.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"fracpartial zetapartial t + nabla cdot (mathbfu(zeta + f)) =\nF_zeta + nabla times mathbfF_mathbfu + (-1)^n+1nunabla^2nzeta","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"We denote timet, velocity vector mathbfu = (u v), Coriolis parameter f, and hyperdiffusion (-1)^n+1 nu nabla^2n zeta (n is the hyperdiffusion order, see Horizontal diffusion). We also include possible forcing terms F_zeta mathbfF_mathbfu = (F_uF_v) which act on the vorticity and/or on the zonal velocity u and the meridional velocity v and hence the curl nabla times mathbfF_mathbfu is a tendency for relative vorticity zeta. See Extending SpeedyWeather how to define these.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"Starting with some relative vorticity zeta, the Laplacian is inverted to obtain the stream function Psi","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"Psi = nabla^-2zeta","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"The zonal velocity u and meridional velocity v are then the (negative) meridional gradient and zonal gradient of Psi","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"beginaligned\nu = -frac1R fracpartial Psipartial theta \nv = frac1Rcos(theta) fracpartial Psipartial phi \nendaligned","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"which is described in Derivatives in spherical coordinates. Using u and v we can then advect the absolute vorticity zeta + f. In order to avoid to calculate both the curl and the divergence of a flux we rewrite the barotropic vorticity equation as","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"fracpartial zetapartial t = F_zeta +\nnabla times (mathbfF + mathbfu_perp(zeta + f)) + (-1)^n+1nunabla^2nzeta","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"with mathbfu_perp = (v-u) the rotated velocity vector, because -nablacdotmathbfu = nabla times mathbfu_perp. This is the form that is solved in the BarotropicModel, as outlined in the following section.","category":"page"},{"location":"barotropic/#Algorithm","page":"Barotropic model","title":"Algorithm","text":"","category":"section"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"We briefly outline the algorithm that SpeedyWeather.jl uses in order to integrate the barotropic vorticity equation. As an initial step","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"0. Start with initial conditions of zeta_lm in spectral space and transform this model state to grid-point space:","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"Invert the Laplacian of vorticity zeta_lm to obtain the stream function Psi_lm in spectral space\nobtain zonal velocity (cos(theta)u)_lm through a Meridional derivative\nobtain meridional velocity (cos(theta)v)_lm through a Zonal derivative\nTransform zonal and meridional velocity (cos(theta)u)_lm, (cos(theta)v)_lm to grid-point space\nUnscale the cos(theta) factor to obtain uv\nTransform zeta_lm to zeta in grid-point space","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"Now loop over","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"Compute the forcing (or drag) terms F_zeta mathbfF_mathbfu\nMultiply uv with zeta+f in grid-point space\nAdd A = F_u + v(zeta + f) and B = F_v - u(zeta + f)\nTransform these vector components to spectral space A_lm, B_lm\nCompute the curl of (AB)_lm in spectral space, add to F_zeta to accumulate the tendency of zeta_lm\nCompute the horizontal diffusion based on that tendency\nCompute a leapfrog time step as described in Time integration with a Robert-Asselin and Williams filter\nTransform the new spectral state of zeta_lm to grid-point uvzeta as described in 0.\nPossibly do some output\nRepeat from 1.","category":"page"},{"location":"barotropic/#diffusion","page":"Barotropic model","title":"Horizontal diffusion","text":"","category":"section"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"In SpeedyWeather.jl we use hyperdiffusion through an n-th power Laplacian (-1)^n+1nabla^2n (hyper when n1) which can be implemented as a multiplication of the spectral coefficients Psi_lm with (-l(l+1))^nR^-2n (see spectral Laplacian) It is therefore computationally not more expensive to apply hyperdiffusion over diffusion as the (-l(l+1))^nR^-2n can be precomputed. Note the sign change (-1)^n+1 here is such that the dissipative nature of the diffusion operator is retained for n odd and even.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"In SpeedyWeather.jl the diffusion is applied implicitly. For that, consider a leapfrog scheme with time step Delta t of variable zeta to obtain from time steps i-1 and i, the next time step i+1","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"zeta_i+1 = zeta_i-1 + 2Delta t dzeta","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"with dzeta being some tendency evaluated from zeta_i. Now we want to add a diffusion term (-1)^n+1nu nabla^2nzeta with coefficient nu, which however, is implicitly calculated from zeta_i+1, then","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"zeta_i+1 = zeta_i-1 + 2Delta t (dzeta + (-1)^n+1 nunabla^2nzeta_i+1)","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"As the application of (-1)^n+1nunabla^2n is, for every spectral mode, equivalent to a multiplication of a constant, we can rewrite this to","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"zeta_i+1 = fraczeta_i-1 + 2Delta t dzeta1 - 2Delta (-1)^n+1nunabla^2n","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"and expand the numerator to","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"zeta_i+1 = zeta_i-1 + 2Delta t fracdzeta + (-1)^n+1 nunabla^2nzeta_i-11 - 2Delta t (-1)^n+1nu nabla^2n","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"Hence the diffusion can be applied implicitly by updating the tendency dzeta as","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"dzeta to fracdzeta + (-1)^n+1nunabla^2nzeta_i-11 - 2Delta t nu nabla^2n","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"which only depends on zeta_i-1. Now let D_textexplicit = (-1)^n+1nunabla^2n be the explicit part and D_textimplicit = 1 - (-1)^n+1 2Delta t nunabla^2n the implicit part. Both parts can be precomputed and are D_textimplicit = 1 - 2Delta t nunabla^2n the implicit part. Both parts can be precomputed and are only an element-wise multiplication in spectral space. For every spectral harmonic lm we do","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"dzeta to D_textimplicit^-1(dzeta + D_textexplicitzeta_i-1)","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"Hence 2 multiplications and 1 subtraction with precomputed constants. However, we will normalize the (hyper-)Laplacians as described in the following. This also will take care of the alternating sign such that the diffusion operation is dissipative regardless the power n.","category":"page"},{"location":"barotropic/#Normalization-of-diffusion","page":"Barotropic model","title":"Normalization of diffusion","text":"","category":"section"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"In physics, the Laplace operator nabla^2 is often used to represent diffusion due to viscosity in a fluid or diffusion that needs to be added to retain numerical stability. In both cases, the coefficient is nu of units textm^2texts^-1 and the full operator reads as nu nabla^2 with units (textm^2texts^-1)(textm^-2) = texts^-1. This motivates us to normalize the Laplace operator by a constant of units textm^-2 and the coefficient by its inverse such that it becomes a damping timescale of unit texts^-1. Given the application in spectral space we decide to normalize by the largest eigenvalue -l_textmax(l_textmax+1) such that all entries in the discrete spectral Laplace operator are in 01. This also has the effect that the alternating sign drops out, such that higher wavenumbers are always dampened and not amplified. The normalized coefficient nu^* = l_textmax(l_textmax+1)nu (always positive) is therefore reinterpreted as the (inverse) time scale at which the highest wavenumber is dampened to zero due to diffusion. Together we have ","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"D^textexplicit_lm = -nu^* fracl(l+1)l_textmax(l_textmax+1)","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"and the hyper-Laplacian of power n follows as","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"D^textexplicitn_lm = -nu^* left(fracl(l+1)l_textmax(l_textmax+1)right)^n","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"and the implicit part is accordingly D^textimplicitn_lm = 1 - 2Delta t D^textexplicitn_lm. Note that the diffusion time scale nu^* is then also scaled by the radius, see next section.","category":"page"},{"location":"barotropic/#scaling","page":"Barotropic model","title":"Radius scaling","text":"","category":"section"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"Similar to a non-dimensionalization of the equations, SpeedyWeather.jl scales the barotropic vorticity equation with R^2 to obtain normalized gradient operators as follows. A scaling for vorticity zeta and stream function Psi is used that is","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"tildezeta = zeta R tildePsi = Psi R^-1","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"This is also convenient as vorticity is often 10^-5text s^-1 in the atmosphere, but the stream function more like 10^5text m^2text s^-1 and so this scaling brings both closer to 1 with a typical radius of the Earth of 6371km. The inversion of the Laplacians in order to obtain Psi from zeta therefore becomes","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"tildezeta = tildenabla^2 tildePsi","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"where the dimensionless gradients simply omit the scaling with 1R, tildenabla = Rnabla. The Barotropic vorticity equation scaled with R^2 is","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"partial_tildettildezeta + tildenabla cdot (mathbfu(tildezeta + tildef)) =\nnabla times tildemathbfF + (-1)^n+1tildenutildenabla^2ntildezeta","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"with","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"tildet = tR^-1, the scaled time t\nmathbfu = (uv), the velocity vector (no scaling applied)\ntildef = fR, the scaled Coriolis parameter f\ntildemathbfF = RmathbfF, the scaled forcing vector mathbfF\ntildenu = nu^* R, the scaled diffusion coefficient nu^*, which itself is normalized to a damping time scale, see Normalization of diffusion.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"So scaling with the radius squared means we can use dimensionless operators, however, this comes at the cost of needing to deal with both a time step in seconds as well as a scaled time step in seconds per meter, which can be confusing. Furthermore, some constants like Coriolis or the diffusion coefficient need to be scaled too during initialization, which may be confusing too because values are not what users expect them to be. SpeedyWeather.jl follows the logic that the scaling to the prognostic variables is only applied just before the time integration and variables are unscaled for output and after the time integration finished. That way, the scaling is hidden as much as possible from the user. In hopefully many other cases it is clearly denoted that a variable or constant is scaled.","category":"page"},{"location":"barotropic/#leapfrog","page":"Barotropic model","title":"Time integration","text":"","category":"section"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"SpeedyWeather.jl is based on the Leapfrog time integration, which, for relative vorticity zeta, is in its simplest form","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"fraczeta_i+1 - zeta_i-12Delta t = RHS(zeta_i)","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"meaning we step from the previous time step i-1, leapfrogging over the current time stepi to the next time step i+1 by evaluating the tendencies on the right-hand side RHS at the current time step i. The time stepping is done in spectral space. Once the right-hand side RHS is evaluated, leapfrogging is a linear operation, meaning that its simply applied to every spectral coefficient zeta_lm as one would evaluate it on every grid point in grid-point models.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"For the Leapfrog time integration two time steps of the prognostic variables have to be stored, i-1 and i. Time step i is used to evaluate the tendencies which are then added to i-1 in a step that also swaps the indices for the next time step i to i-1 and i+1 to i, so that no additional memory than two time steps have to be stored at the same time.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"The Leapfrog time integration has to be initialized with an Euler forward step in order to have a second time step i+1 available when starting from i to actually leapfrog over. SpeedyWeather.jl therefore does two initial time steps that are different from the leapfrog time steps that follow and that have been described above.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"an Euler forward step with Delta t2, then\none leapfrog time step with Delta t, then\nleapfrog with 2 Delta t till the end","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"This is particularly done in a way that after 2. we have t=0 at i-1 and t=Delta t at i available so that 3. can start the leapfrogging without any offset from the intuitive spacing 0Delta t 2Delta t 3Delta t. The following schematic can be useful","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":" time at step i-1 time at step i time step at i+1\nInitial conditions t = 0 \n1: Euler (T) quad t = 0 t=Delta t2 \n2: Leapfrog with Delta t t = 0 (T) quad t = Delta t2 t = Delta t\n3 to n: Leapfrog with 2Delta t t-Delta t (T) qquad quad quad t t+Delta t","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"The time step that is used to evaluate the tendencies is denoted with (T). It is always the time step furthest in time that is available.","category":"page"},{"location":"barotropic/#Robert-Asselin-and-Williams-filter","page":"Barotropic model","title":"Robert-Asselin and Williams filter","text":"","category":"section"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"The standard leapfrog time integration is often combined with a Robert-Asselin filter[Robert66][Asselin72] to dampen a computational mode. The idea is to start with a standard leapfrog step to obtain the next time step i+1 but then to correct the current time step i by applying a filter which dampens the computational mode. The filter looks like a discrete Laplacian in time with a (1 -2 1) stencil, and so, maybe unsurprisingly, is efficient to filter out a \"grid-scale oscillation\" in time, aka the computational mode. Let v be the unfiltered variable and u be the filtered variable, F the right-hand side tendency, then the standard leapfrog step is","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"v_i+1 = u_i-1 + 2Delta tF(v_i)","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"Meaning we start with a filtered variable u at the previous time step i-1, evaluate the tendency F(v_i) based on the current time step i to obtain an unfiltered next time step v_i+1. We then filter the current time step i (which will become i-1 on the next iteration)","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"u_i = v_i + fracnu2(v_i+1 - 2v_i + u_i-1)","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"by adding a discrete Laplacian with coefficient tfracnu2 to it, evaluated from the available filtered and unfiltered time steps centred around i: v_i-1 is not available anymore because it was overwritten by the filtering at the previous iteration, u_i u_i+1 are not filtered yet when applying the Laplacian. The filter parameter nu is typically chosen between 0.01-0.2, with stronger filtering for higher values.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"Williams[Williams2009] then proposed an additional filter step to regain accuracy that is otherwise lost with a strong Robert-Asselin filter[Amezcua2011][Williams2011]. Now let w be unfiltered, v be once filtered, and u twice filtered, then","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"beginaligned\nw_i+1 = u_i-1 + 2Delta tF(v_i) \nu_i = v_i + fracnualpha2(w_i+1 - 2v_i + u_i-1) \nv_i+1 = w_i+1 - fracnu(1-alpha)2(w_i+1 - 2v_i + u_i-1)\nendaligned","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"with the Williams filter parameter alpha in 051. For alpha=1 we're back with the Robert-Asselin filter (the first two lines).","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"The Laplacian in the parentheses is often called a displacement, meaning that the filtered value is displaced (or corrected) in the direction of the two surrounding time steps. The Williams filter now also applies the same displacement, but in the opposite direction to the next time step i+1 as a correction step (line 3 above) for a once-filtered value v_i+1 which will then be twice-filtered by the Robert-Asselin filter on the next iteration. For more details see the referenced publications.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"The initial Euler step (see Time integration, Table) is not filtered. Both the the Robert-Asselin and Williams filter are then switched on for all following leapfrog time steps.","category":"page"},{"location":"barotropic/#References","page":"Barotropic model","title":"References","text":"","category":"section"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"[1]: Geophysical Fluid Dynamics Laboratory, Idealized models with spectral dynamics","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"[2]: Geophysical Fluid Dynamics Laboratory, The barotropic vorticity equation.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"[Robert66]: Robert, André. “The Integration of a Low Order Spectral Form of the Primitive Meteorological Equations.” Journal of the Meteorological Society of Japan 44 (1966): 237-245.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"[Asselin72]: ASSELIN, R., 1972: Frequency Filter for Time Integrations. Mon. Wea. Rev., 100, 487–490, doi:10.1175/1520-0493(1972)100<0487:FFFTI>2.3.CO;2","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"[Williams2009]: Williams, P. D., 2009: A Proposed Modification to the Robert–Asselin Time Filter. Mon. Wea. Rev., 137, 2538–2546, 10.1175/2009MWR2724.1.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"[Amezcua2011]: Amezcua, J., E. Kalnay, and P. D. Williams, 2011: The Effects of the RAW Filter on the Climatology and Forecast Skill of the SPEEDY Model. Mon. Wea. Rev., 139, 608–619, doi:10.1175/2010MWR3530.1. ","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"[Williams2011]: Williams, P. D., 2011: The RAW Filter: An Improvement to the Robert–Asselin Filter in Semi-Implicit Integrations. Mon. Wea. Rev., 139, 1996–2007, doi:10.1175/2010MWR3601.1. ","category":"page"},{"location":"installation/#Installation","page":"Installation","title":"Installation","text":"","category":"section"},{"location":"installation/","page":"Installation","title":"Installation","text":"SpeedyWeather.jl is registered in the Julia Registry. In most cases just open the Julia REPL and type","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"julia> using Pkg\njulia> Pkg.add(\"SpeedyWeather\")","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"or, equivalently, (] opens the package manager)","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"julia>] add SpeedyWeather","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"which will automatically install the latest release and all necessary dependencies. If you run into any troubles please raise an issue.","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"However, you may want to make use of the latest features, then install directly from the main branch with","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"julia> Pkg.add(url=\"https://github.com/SpeedyWeather/SpeedyWeather.jl\",rev=\"main\")","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"other branches than main can be similarly installed. You can also type, equivalently,","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"julia>] add https://github.com/SpeedyWeather/SpeedyWeather.jl#main","category":"page"},{"location":"installation/#Compatibility-with-Julia-versions","page":"Installation","title":"Compatibility with Julia versions","text":"","category":"section"},{"location":"installation/","page":"Installation","title":"Installation","text":"SpeedyWeather.jl usually lives on the latest minor release and/or its predecessor. At the moment (June 2023) this means ","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"Julia v1.8\nJulia v1.9","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"are supported, but we dropped the support of earlier versions.","category":"page"},{"location":"output/#NetCDF-output","page":"NetCDF output","title":"NetCDF output","text":"","category":"section"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"SpeedyWeather.jl uses NetCDF to output the data of a simulation. The following describes the details of this and how to change the way in which the NetCDF output is written. There are many options to this available.","category":"page"},{"location":"output/#Accessing-the-NetCDF-output-writer","page":"NetCDF output","title":"Accessing the NetCDF output writer","text":"","category":"section"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"The output writer is a component of every Model, i.e. BarotropicModel, ShallowWaterModel, PrimitiveDryModel and PrimitiveWetModel, hence a non-default output writer can be passed on as a keyword argument to the model constructor","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"julia> using SpeedyWeather\njulia> spectral_grid = SpectralGrid()\njulia> my_output_writer = OutputWriter(spectral_grid, PrimitiveDry)\njulia> model = PrimitiveDryModel(;spectral_grid, output=my_output_writer)","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"So after we have defined the grid through the SpectralGrid object we can use and change the implemented OutputWriter by passing on the following arguments","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"julia> my_output_writer = OutputWriter(spectral_grid, PrimitiveDry, kwargs...)","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"the spectral_grid has to be the first argument then the model type (Barotropic, ShallowWater, PrimitiveDry, PrimitiveWet) which helps the output writer to make default choices on which variables to output. However, we can also pass on further keyword arguments. So let's start with an example.","category":"page"},{"location":"output/#Example-1:-NetCDF-output-every-hour","page":"NetCDF output","title":"Example 1: NetCDF output every hour","text":"","category":"section"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"If we want to increase the frequency of the output we can choose output_dt (default =6 in hours) like so","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"julia> my_output_writer = OutputWriter(spectral_grid, PrimitiveDry, output_dt=1)\njulia> model = PrimitiveDryModel(;spectral_grid, output=my_output_writer)","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"which will now output every hour. It is important to pass on the new output writer my_output_writer to the model constructor, otherwise it will not be part of your model and the default is used instead. Note that output_dt has to be understood as the minimum frequency or maximum output time step. Example, we run the model at a resolution of T85 and the time step is going to be 670s","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"julia> spectral_grid = SpectralGrid(trunc=85)\njulia> time_stepper = Leapfrog(spectral_grid)\nLeapfrog{Float32}:\n...\n Δt_sec::Int64 = 670\n...","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"This means that after 32 time steps 5h 57min and 20s will have passed where output will happen as the next time step would be >6h. The time axis of the NetCDF output will look like","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"julia> using NCDatasets\njulia> ds = NCDataset(\"run_0001/output.nc\");\njulia> ds[\"time\"][:]\n5-element Vector{Dates.DateTime}:\n 2000-01-01T00:00:00\n 2000-01-01T05:57:20\n 2000-01-01T11:54:40\n 2000-01-01T17:52:00\n 2000-01-01T23:49:20\n\njulia> diff(ds[\"time\"][:])\n4-element Vector{Dates.Millisecond}:\n 21440000 milliseconds\n 21440000 milliseconds\n 21440000 milliseconds\n 21440000 milliseconds","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"This is so that we don't interpolate in time during output to hit exactly every 6 hours, but at the same time have a constant spacing in time between output time steps.","category":"page"},{"location":"output/#Example-2:-Output-onto-a-higher/lower-resolution-grid","page":"NetCDF output","title":"Example 2: Output onto a higher/lower resolution grid","text":"","category":"section"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"Say we want to run the model at a given horizontal resolution but want to output on another resolution, the OutputWriter takes as argument output_Grid<:AbstractFullGrid and nlat_half::Int. So for example output_Grid=FullClenshawGrid and nlat_half=48 will always interpolate onto a regular 192x95 longitude-latitude grid of 1.875˚ resolution, regardless the grid and resolution used for the model integration.","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"julia> my_output_writer = OutputWriter(spectral_grid, PrimitiveDry, output_Grid=FullClenshawGrid, nlat_half=48)","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"Note that by default the output is on the corresponding full of the grid used in the dynamical core so that interpolation only happens at most in the zonal direction as they share the location of the latitude rings. You can check this by","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"julia> RingGrids.full_grid(OctahedralGaussianGrid)\nFullGaussianGrid","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"So the corresponding full grid of an OctahedralGaussianGrid is the FullGaussiangrid and the same resolution nlat_half is chosen by default in the output writer (which you can change though as shown above). Overview of the corresponding full grids","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"Grid Corresponding full grid\nFullGaussianGrid FullGaussianGrid\nFullClenshawGrid FullClenshawGrid\nOctahadralGaussianGrid FullGaussianGrid\nOctahedralClensawhGrid FullClenshawGrid\nHEALPixGrid FullHEALPixGrid\nOctaHEALPixGrid FullOctaHEALPixGrid","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"The grids FullHEALPixGrid, FullOctaHEALPixGrid share the same latitude rings as their reduced grids, but have always as many longitude points as they are at most around the equator. These grids are not tested in the dynamical core (but you may use them experimentally) and mostly designed for output purposes.","category":"page"},{"location":"output/#Example-3:-Changing-the-output-path-or-identification","page":"NetCDF output","title":"Example 3: Changing the output path or identification","text":"","category":"section"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"That's easy by passing on path=\"/my/favourite/path/\" and the folder run_* with * the identification of the run (that's the id keyword, which can be manually set but is also automatically determined as a number counting up depending on which folders already exist) will be created within.","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"julia> path = pwd()\n\"/Users/milan\"\njulia> my_output_writer = OutputWriter(spectral_grid, PrimitiveDry, path=path)","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"This folder must already exist. If you want to give your run a name/identification you can pass on id","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"julia> my_output_writer = OutputWriter(spectral_grid,PrimitiveDry,id=\"diffusion_test\");","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"which will be used instead of a 4 digit number like 0001, 0002 which is automatically determined if id is not provided. You will see the id of the run in the progress bar","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"Weather is speedy: run diffusion_test 100%|███████████████████████| Time: 0:00:12 (19.20 years/day)","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"and the run folder, here run_diffusion_test, is also named accordingly","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"shell> ls\n...\nrun_diffusion_test\n...","category":"page"},{"location":"output/#Further-options","page":"NetCDF output","title":"Further options","text":"","category":"section"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"Further options are described in the OutputWriter docstring, (also accessible via julia>?OutputWriter for example). Note that some fields are actual options, but others are derived from the options you provided or are arrays/objects the output writer needs, but shouldn't be passed on by the user. The actual options are declared as [OPTION] in the following","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"help?> OutputWriter\nsearch: OutputWriter\n\n NetCDF output writer. Contains all output options and auxiliary fields for\n output interpolation. To be initialised with\n OutputWriter(::SpectralGrid,::Type{<:ModelSetup},kwargs...) to pass on the\n resolution information and the model type which chooses which variables to\n output. Options include\n\n • spectral_grid::SpectralGrid\n\n • output::Bool\n\n • path::String: [OPTION] path to output folder, run_???? will be\n created within\n\n • id::String: [OPTION] run identification number/string\n\n • run_path::String\n\n • filename::String: [OPTION] name of the output netcdf file\n\n • write_restart::Bool: [OPTION] also write restart file if\n output==true?\n\n • pkg_version::VersionNumber\n\n • startdate::Dates.DateTime\n\n • output_dt::Float64: [OPTION] output frequency, time step [hrs]\n\n • output_dt_sec::Int64: actual output time step [sec]\n\n • output_vars::Vector{Symbol}: [OPTION] which variables to output,\n u, v, vor, div, pres, temp, humid","category":"page"},{"location":"functions/#Function-and-type-index","page":"Function and type index","title":"Function and type index","text":"","category":"section"},{"location":"functions/","page":"Function and type index","title":"Function and type index","text":"Modules = [SpeedyWeather]","category":"page"},{"location":"functions/#SpeedyWeather.AbstractDevice","page":"Function and type index","title":"SpeedyWeather.AbstractDevice","text":"abstract type AbstractDevice\n\nSupertype of all devices SpeedyWeather.jl can ran on\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.BarotropicModel","page":"Function and type index","title":"SpeedyWeather.BarotropicModel","text":"The BarotropicModel struct holds all other structs that contain precalculated constants, whether scalars or arrays that do not change throughout model integration.\n\nspectral_grid::SpectralGrid: dictates resolution for many other components\nplanet::SpeedyWeather.AbstractPlanet: contains physical and orbital characteristics\natmosphere::SpeedyWeather.AbstractAtmosphere\nforcing::SpeedyWeather.AbstractForcing{NF} where NF<:AbstractFloat\ndrag::SpeedyWeather.AbstractDrag{NF} where NF<:AbstractFloat\ninitial_conditions::SpeedyWeather.InitialConditions\ntime_stepping::SpeedyWeather.TimeStepper{NF} where NF<:AbstractFloat\nspectral_transform::SpectralTransform\nhorizontal_diffusion::SpeedyWeather.HorizontalDiffusion{NF} where NF<:AbstractFloat\nimplicit::SpeedyWeather.AbstractImplicit{NF} where NF<:AbstractFloat\ngeometry::Geometry\nconstants::DynamicsConstants\ndevice_setup::SpeedyWeather.DeviceSetup\noutput::SpeedyWeather.AbstractOutputWriter\nfeedback::SpeedyWeather.AbstractFeedback\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.CPUDevice","page":"Function and type index","title":"SpeedyWeather.CPUDevice","text":"CPUDevice <: AbstractDevice\n\nIndicates that SpeedyWeather.jl runs on a single CPU \n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Clock","page":"Function and type index","title":"SpeedyWeather.Clock","text":"Clock struct keeps track of the model time, how many days to integrate for and how many time steps this takes\n\ntime::Dates.DateTime: current model time\nn_days::Float64: number of days to integrate for, set in run!(::Simulation)\nn_timesteps::Int64: number of time steps to integrate for, set in initialize!(::Clock,::TimeStepper)\n\n.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Clock-Tuple{SpeedyWeather.TimeStepper}","page":"Function and type index","title":"SpeedyWeather.Clock","text":"Clock(\n time_stepping::SpeedyWeather.TimeStepper;\n kwargs...\n) -> SpeedyWeather.Clock\n\n\nCreate and initialize a clock from time_stepping\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.ColumnVariables","page":"Function and type index","title":"SpeedyWeather.ColumnVariables","text":"Mutable struct that contains all prognostic (copies thereof) and diagnostic variables in a single column needed to evaluate the physical parametrizations. For now the struct is mutable as we will reuse the struct to iterate over horizontal grid points. Every column vector has nlev entries, from [1] at the top to [end] at the lowermost model level at the planetary boundary layer.\n\nnlev::Int64\nnband::Int64\nn_stratosphere_levels::Int64\njring::Int64\nlond::AbstractFloat\nlatd::AbstractFloat\nland_fraction::AbstractFloat\nu::Vector{NF} where NF<:AbstractFloat\nv::Vector{NF} where NF<:AbstractFloat\ntemp::Vector{NF} where NF<:AbstractFloat\nhumid::Vector{NF} where NF<:AbstractFloat\nln_pres::Vector{NF} where NF<:AbstractFloat\npres::Vector{NF} where NF<:AbstractFloat\nu_tend::Vector{NF} where NF<:AbstractFloat\nv_tend::Vector{NF} where NF<:AbstractFloat\ntemp_tend::Vector{NF} where NF<:AbstractFloat\nhumid_tend::Vector{NF} where NF<:AbstractFloat\ngeopot::Vector{NF} where NF<:AbstractFloat\nflux_u_upward::Vector{NF} where NF<:AbstractFloat\nflux_u_downward::Vector{NF} where NF<:AbstractFloat\nflux_v_upward::Vector{NF} where NF<:AbstractFloat\nflux_v_downward::Vector{NF} where NF<:AbstractFloat\nflux_temp_upward::Vector{NF} where NF<:AbstractFloat\nflux_temp_downward::Vector{NF} where NF<:AbstractFloat\nflux_humid_upward::Vector{NF} where NF<:AbstractFloat\nflux_humid_downward::Vector{NF} where NF<:AbstractFloat\nsat_humid::Vector{NF} where NF<:AbstractFloat\nsat_vap_pres::Vector{NF} where NF<:AbstractFloat\ndry_static_energy::Vector{NF} where NF<:AbstractFloat\nmoist_static_energy::Vector{NF} where NF<:AbstractFloat\nhumid_half::Vector{NF} where NF<:AbstractFloat\nsat_humid_half::Vector{NF} where NF<:AbstractFloat\nsat_moist_static_energy::Vector{NF} where NF<:AbstractFloat\ndry_static_energy_half::Vector{NF} where NF<:AbstractFloat\nsat_moist_static_energy_half::Vector{NF} where NF<:AbstractFloat\nconditional_instability::Bool\nactivate_convection::Bool\ncloud_top::Int64\nexcess_humidity::AbstractFloat\ncloud_base_mass_flux::AbstractFloat\nprecip_convection::AbstractFloat\nnet_flux_humid::Vector{NF} where NF<:AbstractFloat\nnet_flux_dry_static_energy::Vector{NF} where NF<:AbstractFloat\nentrainment_profile::Vector{NF} where NF<:AbstractFloat\nprecip_large_scale::AbstractFloat\nwvi::Matrix{NF} where NF<:AbstractFloat\ntau2::Matrix{NF} where NF<:AbstractFloat\ndfabs::Vector{NF} where NF<:AbstractFloat\nfsfcd::AbstractFloat\nst4a::Matrix{NF} where NF<:AbstractFloat\nflux::Vector{NF} where NF<:AbstractFloat\nfsfcu::AbstractFloat\nts::AbstractFloat\nfsfc::AbstractFloat\nftop::AbstractFloat\nstratc::Vector{NF} where NF<:AbstractFloat\ntyear::AbstractFloat\ncsol::AbstractFloat\ntopsr::AbstractFloat\nfsol::AbstractFloat\nozupp::AbstractFloat\nozone::AbstractFloat\nzenit::AbstractFloat\nstratz::AbstractFloat\nalbsfc::AbstractFloat\nssrd::AbstractFloat\nssr::AbstractFloat\ntsr::AbstractFloat\ntend_t_rsw::Vector{NF} where NF<:AbstractFloat\nnorm_pres::AbstractFloat\nicltop::Int64\ncloudc::AbstractFloat\nclstr::AbstractFloat\nqcloud::AbstractFloat\nfmask::AbstractFloat\nrel_hum::Vector{NF} where NF<:AbstractFloat\ngrad_dry_static_energy::AbstractFloat\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.DeviceSetup","page":"Function and type index","title":"SpeedyWeather.DeviceSetup","text":"DeviceSetup{S<:AbstractDevice}\n\nHolds information about the device the model is running on and workgroup size. \n\ndevice::AbstractDevice: Device the model is running on \ndevice_KA::KernelAbstractions.Device: Device for use with KernelAbstractions\nn: workgroup size \n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.DiagnosticVariables","page":"Function and type index","title":"SpeedyWeather.DiagnosticVariables","text":"DiagnosticVariables{Grid<:AbstractGrid,NF<:AbstractFloat}\n\nStruct holding the diagnostic variables.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.DynamicsConstants","page":"Function and type index","title":"SpeedyWeather.DynamicsConstants","text":"Struct holding constants needed at runtime for the dynamical core in number format NF.\n\nradius::AbstractFloat: Radius of Planet [m]\nrotation::AbstractFloat: Angular frequency of Planet's rotation [s^-1]\ngravity::AbstractFloat: Gravitational acceleration [m/s^2]\nlayer_thickness::AbstractFloat: shallow water layer thickness [m]\nR_dry::AbstractFloat: specific gas constant for dry air [J/kg/K]\nR_vapour::AbstractFloat: specific gas constant for water vapour [J/kg/K]\nμ_virt_temp::AbstractFloat: used in Tv = T(1+μq) for virt temp Tv(T,q) calculation\ncₚ::AbstractFloat: specific heat at constant pressure [J/K/kg]\nκ::AbstractFloat: = R_dry/cₚ, gas const for air over heat capacity\nwater_density::AbstractFloat: water density [kg/m³]\nf_coriolis::Vector{NF} where NF<:AbstractFloat: coriolis frequency [s^-1], scaled by radius as is vorticity = 2Ωsin(lat)radius\nσ_lnp_A::Vector{NF} where NF<:AbstractFloat: σ-related factor A needed for adiabatic conversion term\nσ_lnp_B::Vector{NF} where NF<:AbstractFloat: σ-related factor B needed for adiabatic conversion term\nΔp_geopot_half::Vector{NF} where NF<:AbstractFloat: = R*(ln(pk+1) - ln(pk+1/2)), for half level geopotential\nΔp_geopot_full::Vector{NF} where NF<:AbstractFloat: = R*(ln(pk+1/2) - ln(pk)), for full level geopotential\ntemp_ref_profile::Vector{NF} where NF<:AbstractFloat: reference temperature profile\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.DynamicsConstants-Tuple{SpectralGrid, SpeedyWeather.AbstractPlanet, SpeedyWeather.AbstractAtmosphere, Geometry}","page":"Function and type index","title":"SpeedyWeather.DynamicsConstants","text":"DynamicsConstants(\n spectral_grid::SpectralGrid,\n planet::SpeedyWeather.AbstractPlanet,\n atmosphere::SpeedyWeather.AbstractAtmosphere,\n geometry::Geometry\n) -> Any\n\n\nGenerator function for a DynamicsConstants struct.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.DynamicsVariables","page":"Function and type index","title":"SpeedyWeather.DynamicsVariables","text":"DynamicsVariables{Grid<:AbstractGrid,NF<:AbstractFloat}\n\nStruct holding intermediate quantities for the dynamics of a given layer.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Earth","page":"Function and type index","title":"SpeedyWeather.Earth","text":"Create a struct Earth<:AbstractPlanet, with the following physical/orbital characteristics. Note that radius is not part of it as this should be chosen in SpectralGrid. Keyword arguments are\n\nrotation::Float64: angular frequency of Earth's rotation [rad/s]\ngravity::Float64: gravitational acceleration [m/s^2]\ndaily_cycle::Bool: switch on/off daily cycle\nlength_of_day::Float64: [hrs] in a day\nseasonal_cycle::Bool: switch on/off seasonal cycle\nlength_of_year::Float64: [days] in a year\nequinox::Dates.DateTime: time of spring equinox (year irrelevant)\naxial_tilt::Float64: angle [˚] rotation axis tilt wrt to orbit\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.EarthAtmosphere","page":"Function and type index","title":"SpeedyWeather.EarthAtmosphere","text":"Create a struct EarthAtmosphere<:AbstractPlanet, with the following physical/chemical characteristics. Note that radius is not part of it as this should be chosen in SpectralGrid. Keyword arguments are\n\nmol_mass_dry_air::Float64: molar mass of dry air [g/mol]\nmol_mass_vapour::Float64: molar mass of water vapour [g/mol]\ncₚ::Float64: specific heat at constant pressure [J/K/kg]\nR_gas::Float64: universal gas constant [J/K/mol]\nR_dry::Float64: specific gas constant for dry air [J/kg/K]\nR_vapour::Float64: specific gas constant for water vapour [J/kg/K]\nwater_density::Float64: water density [kg/m³]\nlatent_heat_condensation::Float64: latent heat of condensation [J/g] for consistency with specific humidity [g/Kg], also called alhc\nlatent_heat_sublimation::Float64: latent heat of sublimation [J/g], also called alhs\nstefan_boltzmann::Float64: stefan-Boltzmann constant [W/m²/K⁴]\nlapse_rate::Float64: moist adiabatic temperature lapse rate -dTdz [K/km]\ntemp_ref::Float64: absolute temperature at surface z=0 [K]\ntemp_top::Float64: absolute temperature in stratosphere [K]\nΔT_stratosphere::Float64: for stratospheric lapse rate [K] after Jablonowski\nσ_tropopause::Float64: start of the stratosphere in sigma coordinates\nσ_boundary_layer::Float64: top of the planetary boundary layer in sigma coordinates\nscale_height::Float64: scale height for pressure [km]\npres_ref::Float64: surface pressure [hPa]\nscale_height_humid::Float64: scale height for specific humidity [km]\nrelhumid_ref::Float64: relative humidity of near-surface air [1]\nwater_pres_ref::Float64: saturation water vapour pressure [Pa]\nlayer_thickness::Float64: layer thickness for the shallow water model [km]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.EarthOrography","page":"Function and type index","title":"SpeedyWeather.EarthOrography","text":"Earth's orography read from file, with smoothing.\n\npath::String: path to the folder containing the orography file, pkg path default\nfile::String: filename of orography\nfile_Grid::Type{<:SpeedyWeather.RingGrids.AbstractGrid}: Grid the orography file comes on\nscale::Float64: scale orography by a factor\nsmoothing::Bool: smooth the orography field?\nsmoothing_power::Float64: power of Laplacian for smoothing\nsmoothing_strength::Float64: highest degree l is multiplied by\nsmoothing_truncation::Int64: resolution of orography in spectral trunc\norography::SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat: height [m] on grid-point space.\ngeopot_surf::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: surface geopotential, height*gravity [m²/s²]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.EarthOrography-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.EarthOrography","text":"EarthOrography(\n spectral_grid::SpectralGrid;\n kwargs...\n) -> Any\n\n\nGenerator function pulling the resolution information from spectral_grid.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.Feedback","page":"Function and type index","title":"SpeedyWeather.Feedback","text":"Feedback() -> Feedback\nFeedback(verbose::Bool) -> Feedback\nFeedback(verbose::Bool, debug::Bool) -> Feedback\n\n\nGenerator function for a Feedback struct.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Feedback-2","page":"Function and type index","title":"SpeedyWeather.Feedback","text":"Feedback struct that contains options and object for command-line feedback like the progress meter.\n\nverbose::Bool: print feedback to REPL?\ndebug::Bool: check for NaRs in the prognostic variables\noutput::Bool: write a progress.txt file? State synced with OutputWriter.output\nid::Union{Int64, String}: identification of run, taken from ::OutputWriter\nrun_path::String: path to run folder, taken from ::OutputWriter\nprogress_meter::ProgressMeter.Progress: struct containing everything progress related\nprogress_txt::Union{Nothing, IOStream}: txt is a Nothing in case of no output\nnars_detected::Bool: did Infs/NaNs occur in the simulation?\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.GPUDevice","page":"Function and type index","title":"SpeedyWeather.GPUDevice","text":"GPUDevice <: AbstractDevice\n\nIndicates that SpeedyWeather.jl runs on a single GPU\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.GenLogisticCoefs","page":"Function and type index","title":"SpeedyWeather.GenLogisticCoefs","text":"Coefficients of the generalised logistic function to describe the vertical coordinate. Default coefficients A,K,C,Q,B,M,ν are fitted to the old L31 configuration at ECMWF.\n\nFollowing the notation of https://en.wikipedia.org/wiki/Generalisedlogisticfunction (Dec 15 2021).\n\nChange default parameters for more/fewer levels in the stratosphere vs troposphere vs boundary layer.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Geometry","page":"Function and type index","title":"SpeedyWeather.Geometry","text":"Construct Geometry struct containing parameters and arrays describing an iso-latitude grid <:AbstractGrid and the vertical levels. Pass on SpectralGrid to calculate the following fields\n\nspectral_grid::SpectralGrid: SpectralGrid that defines spectral and grid resolution\nGrid::Type{<:SpeedyWeather.RingGrids.AbstractGrid}: grid of the dynamical core\nnlat_half::Int64: resolution parameter nlat_half of Grid, # of latitudes on one hemisphere (incl Equator)\nnlon_max::Int64: maximum number of longitudes (at/around Equator)\nnlon::Int64: =nlon_max, same (used for compatibility), TODO: still needed?\nnlat::Int64: number of latitude rings\nnlev::Int64: number of vertical levels\nnpoints::Int64: total number of grid points\nradius::AbstractFloat: Planet's radius [m]\ncolat::Vector{Float64}: array of colatitudes in radians (0...π)\nlatd::Vector{Float64}: array of latitudes in degrees (90˚...-90˚)\nlond::Vector{Float64}: array of longitudes in degrees (0...360˚), empty for non-full grids\nlonds::Vector{NF} where NF<:AbstractFloat: longitude (-180˚...180˚) for each grid point in ring order\nlatds::Vector{NF} where NF<:AbstractFloat: latitude (-90˚...˚90) for each grid point in ring order\nsinlat::Vector{NF} where NF<:AbstractFloat: sin of latitudes\ncoslat::Vector{NF} where NF<:AbstractFloat: cos of latitudes\ncoslat⁻¹::Vector{NF} where NF<:AbstractFloat: = 1/cos(lat)\ncoslat²::Vector{NF} where NF<:AbstractFloat: = cos²(lat)\ncoslat⁻²::Vector{NF} where NF<:AbstractFloat: # = 1/cos²(lat)\nσ_levels_half::Vector{NF} where NF<:AbstractFloat: σ at half levels, σ_k+1/2\nσ_levels_full::Vector{NF} where NF<:AbstractFloat: σ at full levels, σₖ\nσ_levels_thick::Vector{NF} where NF<:AbstractFloat: σ level thicknesses, σₖ₊₁ - σₖ\nln_σ_levels_full::Vector{NF} where NF<:AbstractFloat: log of σ at full levels, include surface (σ=1) as last element\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Geometry-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.Geometry","text":"Geometry(spectral_grid::SpectralGrid) -> Any\n\n\nGenerator function for Geometry struct based on spectral_grid.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.GridVariables","page":"Function and type index","title":"SpeedyWeather.GridVariables","text":"GridVariables{NF<:AbstractFloat}\n\nStruct holding the prognostic spectral variables of a given layer in grid point space.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.HeldSuarez","page":"Function and type index","title":"SpeedyWeather.HeldSuarez","text":"Struct that defines the temperature relaxation from Held and Suarez, 1996 BAMS\n\nnlat::Int64: number of latitude rings\nnlev::Int64: number of vertical levels\nσb::Float64: sigma coordinate below which faster surface relaxation is applied\nrelax_time_slow::Float64: time scale [hrs] for slow global relaxation\nrelax_time_fast::Float64: time scale [hrs] for faster tropical surface relaxation\nTmin::Float64: minimum equilibrium temperature [K]\nTmax::Float64: maximum equilibrium temperature [K]\nΔTy::Float64: meridional temperature gradient [K]\nΔθz::Float64: vertical temperature gradient [K]\nκ::Base.RefValue{NF} where NF<:AbstractFloat\np₀::Base.RefValue{NF} where NF<:AbstractFloat\ntemp_relax_freq::Matrix{NF} where NF<:AbstractFloat\ntemp_equil_a::Vector{NF} where NF<:AbstractFloat\ntemp_equil_b::Vector{NF} where NF<:AbstractFloat\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.HeldSuarez-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.HeldSuarez","text":"HeldSuarez(SG::SpectralGrid; kwargs...) -> Any\n\n\ncreate a HeldSuarez temperature relaxation with arrays allocated given spectral_grid\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.HyperDiffusion","page":"Function and type index","title":"SpeedyWeather.HyperDiffusion","text":"Struct for horizontal hyper diffusion of vor, div, temp; implicitly in spectral space with a power of the Laplacian (default=4) and the strength controlled by time_scale. Options exist to scale the diffusion by resolution, and adaptive depending on the current vorticity maximum to increase diffusion in active layers. Furthermore the power can be decreased above the tapering_σ to power_stratosphere (default 2). For Barotropic, ShallowWater, the default non-adaptive constant-time scale hyper diffusion is used. Options are\n\ntrunc::Int64: spectral resolution\nnlev::Int64: number of vertical levels\npower::Float64: power of Laplacian\ntime_scale::Float64: diffusion time scales [hrs]\nresolution_scaling::Float64: stronger diffusion with resolution? 0: constant with trunc, 1: (inverse) linear with trunc, etc\npower_stratosphere::Float64: different power for tropopause/stratosphere\ntapering_σ::Float64: linearly scale towards power_stratosphere above this σ\nadaptive::Bool: adaptive = higher diffusion for layers with higher vorticity levels.\nvor_max::Float64: above this (absolute) vorticity level [1/s], diffusion is increased\nadaptive_strength::Float64: increase strength above vor_max by this factor times max(abs(vor))/vor_max\n∇²ⁿ_2D::Vector\n∇²ⁿ_2D_implicit::Vector\n∇²ⁿ::Array{Vector{NF}, 1} where NF\n∇²ⁿ_implicit::Array{Vector{NF}, 1} where NF\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.HyperDiffusion-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.HyperDiffusion","text":"HyperDiffusion(\n spectral_grid::SpectralGrid;\n kwargs...\n) -> Any\n\n\nGenerator function based on the resolutin in spectral_grid. Passes on keyword arguments.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.ImplicitPrimitiveEq","page":"Function and type index","title":"SpeedyWeather.ImplicitPrimitiveEq","text":"Struct that holds various precomputed arrays for the semi-implicit correction to prevent gravity waves from amplifying in the primitive equation model.\n\ntrunc::Int64: spectral resolution\nnlev::Int64: number of vertical levels\nα::Float64: time-step coefficient: 0=explicit, 0.5=centred implicit, 1=backward implicit\ntemp_profile::Vector{NF} where NF<:AbstractFloat: vertical temperature profile, obtained from diagn\nξ::Base.RefValue{NF} where NF<:AbstractFloat: time step 2α*Δt packed in RefValue for mutability\nR::Matrix{NF} where NF<:AbstractFloat: divergence: operator for the geopotential calculation\nU::Vector{NF} where NF<:AbstractFloat: divergence: the -RdTₖ∇² term excl the eigenvalues from ∇² for divergence\nL::Matrix{NF} where NF<:AbstractFloat: temperature: operator for the TₖD + κTₖDlnps/Dt term\nW::Vector{NF} where NF<:AbstractFloat: pressure: vertical averaging of the -D̄ term in the log surface pres equation\nL0::Vector{NF} where NF<:AbstractFloat: components to construct L, 1/ 2Δσ\nL1::Matrix{NF} where NF<:AbstractFloat: vert advection term in the temperature equation (below+above)\nL2::Vector{NF} where NF<:AbstractFloat: factor in front of the divsumabove term\nL3::Matrix{NF} where NF<:AbstractFloat: sumabove operator itself\nL4::Vector{NF} where NF<:AbstractFloat: factor in front of div term in Dlnps/Dt\nS::Matrix{NF} where NF<:AbstractFloat: for every l the matrix to be inverted\nS⁻¹::Array{NF, 3} where NF<:AbstractFloat: combined inverted operator: S = 1 - ξ²(RL + UW)\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ImplicitPrimitiveEq-Tuple{SpectralGrid, Vararg{Any}}","page":"Function and type index","title":"SpeedyWeather.ImplicitPrimitiveEq","text":"ImplicitPrimitiveEq(\n spectral_grid::SpectralGrid,\n kwargs...\n) -> Any\n\n\nGenerator using the resolution from SpectralGrid.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.ImplicitShallowWater","page":"Function and type index","title":"SpeedyWeather.ImplicitShallowWater","text":"Struct that holds various precomputed arrays for the semi-implicit correction to prevent gravity waves from amplifying in the shallow water model.\n\ntrunc::Int64\nα::Float64: coefficient for semi-implicit computations to filter gravity waves\nH::Base.RefValue{NF} where NF<:AbstractFloat\nξH::Base.RefValue{NF} where NF<:AbstractFloat\ng∇²::Vector{NF} where NF<:AbstractFloat\nξg∇²::Vector{NF} where NF<:AbstractFloat\nS⁻¹::Vector{NF} where NF<:AbstractFloat\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ImplicitShallowWater-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.ImplicitShallowWater","text":"ImplicitShallowWater(\n spectral_grid::SpectralGrid;\n kwargs...\n) -> Any\n\n\nGenerator using the resolution from spectral_grid.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.JablonowskiRelaxation","page":"Function and type index","title":"SpeedyWeather.JablonowskiRelaxation","text":"HeldSuarez-like temperature relaxation, but towards the Jablonowski temperature profile with increasing temperatures in the stratosphere.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.JablonowskiRelaxation-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.JablonowskiRelaxation","text":"JablonowskiRelaxation(SG::SpectralGrid; kwargs...) -> Any\n\n\ncreate a JablonowskiRelaxation temperature relaxation with arrays allocated given spectral_grid\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.JetStreamForcing","page":"Function and type index","title":"SpeedyWeather.JetStreamForcing","text":"Forcing term for the Barotropic or ShallowWaterModel with an idealised jet stream similar to the initial conditions from Galewsky, 2004, but mirrored for both hemispheres.\n\nnlat::Int64: Number of latitude rings\nlatitude::Float64: jet latitude [˚N]\nwidth::Float64: jet width [˚], default ≈ 19.29˚\nspeed::Float64: jet speed scale [m/s]\ntime_scale::Float64: time scale [days]\namplitude::Vector: precomputed amplitude vector [m/s²]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Keepbits","page":"Function and type index","title":"SpeedyWeather.Keepbits","text":"Number of mantissa bits to keep for each prognostic variable when compressed for netCDF and .jld2 data output.\n\nu::Int64\nv::Int64\nvor::Int64\ndiv::Int64\ntemp::Int64\npres::Int64\nhumid::Int64\nprecip_cond::Int64\nprecip_conv::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.LandSeaMask","page":"Function and type index","title":"SpeedyWeather.LandSeaMask","text":"Land-sea mask, fractional, read from file.\n\npath::String: path to the folder containing the land-sea mask file, pkg path default\nfile::String: filename of land sea mask\nfile_Grid::Type{<:SpeedyWeather.RingGrids.AbstractGrid}: Grid the land-sea mask file comes on\nland_sea_mask::SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat: Land-sea mask [1] on grid-point space. Land=1, sea=0, land-area fraction in between.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.LandSeaMask-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.LandSeaMask","text":"LandSeaMask(spectral_grid::SpectralGrid; kwargs...) -> Any\n\n\nGenerator function pulling the resolution information from spectral_grid.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.Leapfrog","page":"Function and type index","title":"SpeedyWeather.Leapfrog","text":"Leapfrog time stepping defined by the following fields\n\ntrunc::Int64: spectral resolution (max degree of spherical harmonics)\nΔt_at_T31::Float64: time step in minutes for T31, scale linearly to trunc\nradius::Any: radius of sphere [m], used for scaling\nrobert_filter::Any: Robert (1966) time filter coefficeint to suppress comput. mode\nwilliams_filter::Any: Williams time filter (Amezcua 2011) coefficient for 3rd order acc\nΔt_sec::Int64: time step Δt [s] at specified resolution\nΔt::Any: time step Δt [s/m] at specified resolution, scaled by 1/radius\nΔt_hrs::Float64: convert time step Δt from minutes to hours\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Leapfrog-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.Leapfrog","text":"Leapfrog(spectral_grid::SpectralGrid; kwargs...) -> Any\n\n\nGenerator function for a Leapfrog struct using spectral_grid for the resolution information.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.LinearDrag","page":"Function and type index","title":"SpeedyWeather.LinearDrag","text":"Linear boundary layer drag Following Held and Suarez, 1996 BAMS\n\nσb::Float64\ntime_scale::Float64\nnlev::Int64\ndrag_coefs::Vector{NF} where NF<:AbstractFloat\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.LinearDrag-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.LinearDrag","text":"LinearDrag(SG::SpectralGrid; kwargs...) -> Any\n\n\nGenerator function using nlev from SG::SpectralGrid\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.MagnusCoefs","page":"Function and type index","title":"SpeedyWeather.MagnusCoefs","text":"Parameters for computing saturation vapour pressure using the August-Roche-Magnus formula,\n\neᵢ(T) = e₀ * exp(Cᵢ * (T - T₀) / (T - Tᵢ)),\n\nwhere T is in Kelvin and i = 1,2 for saturation with respect to water and ice, respectively.\n\ne₀::AbstractFloat: Saturation vapour pressure at 0°C [Pa]\nT₀::AbstractFloat: 0°C in Kelvin\nT₁::AbstractFloat\nT₂::AbstractFloat\nC₁::AbstractFloat\nC₂::AbstractFloat\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.NoBoundaryLayerDrag","page":"Function and type index","title":"SpeedyWeather.NoBoundaryLayerDrag","text":"Concrete type that disables the boundary layer drag scheme.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.NoOrography","page":"Function and type index","title":"SpeedyWeather.NoOrography","text":"Orography with zero height in orography and zero surface geopotential geopot_surf.\n\norography::SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat: height [m] on grid-point space.\ngeopot_surf::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: surface geopotential, height*gravity [m²/s²]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.NoOrography-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.NoOrography","text":"NoOrography(spectral_grid::SpectralGrid) -> NoOrography\n\n\nGenerator function pulling the resolution information from spectral_grid.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.OutputWriter","page":"Function and type index","title":"SpeedyWeather.OutputWriter","text":"NetCDF output writer. Contains all output options and auxiliary fields for output interpolation. To be initialised with OutputWriter(::SpectralGrid,::Type{<:ModelSetup},kwargs...) to pass on the resolution information and the model type which chooses which variables to output. Options include\n\nspectral_grid::SpectralGrid\noutput::Bool\npath::String: [OPTION] path to output folder, run_???? will be created within\nid::String: [OPTION] run identification number/string\nrun_path::String\nfilename::String: [OPTION] name of the output netcdf file\nwrite_restart::Bool: [OPTION] also write restart file if output==true?\npkg_version::VersionNumber\nstartdate::Dates.DateTime\noutput_dt::Float64: [OPTION] output frequency, time step [hrs]\noutput_dt_sec::Int64: actual output time step [sec]\noutput_vars::Vector{Symbol}: [OPTION] which variables to output, u, v, vor, div, pres, temp, humid\nmissing_value::Union{Float32, Float64}: [OPTION] missing value to be used in netcdf output\ncompression_level::Int64: [OPTION] lossless compression level; 1=low but fast, 9=high but slow\nshuffle::Bool: [OPTION] shuffle/bittranspose filter for compression\nkeepbits::SpeedyWeather.Keepbits: [OPTION] mantissa bits to keep for every variable\noutput_every_n_steps::Int64\ntimestep_counter::Int64\noutput_counter::Int64\nnetcdf_file::Union{Nothing, NCDatasets.NCDataset}\ninput_Grid::Type{<:SpeedyWeather.RingGrids.AbstractGrid}\nas_matrix::Bool: [OPTION] sort grid points into a matrix (interpolation-free), for OctahedralClenshawGrid, OctaHEALPixGrid only\nquadrant_rotation::NTuple{4, Int64}\nmatrix_quadrant::NTuple{4, Tuple{Int64, Int64}}\noutput_Grid::Type{<:SpeedyWeather.RingGrids.AbstractFullGrid}: [OPTION] the grid used for output, full grids only\nnlat_half::Int64: [OPTION] the resolution of the output grid, default: same nlat_half as in the dynamical core\nnlon::Int64\nnlat::Int64\nnpoints::Int64\nnlev::Int64\ninterpolator::SpeedyWeather.RingGrids.AbstractInterpolator\nu::Matrix{NF} where NF<:Union{Float32, Float64}\nv::Matrix{NF} where NF<:Union{Float32, Float64}\nvor::Matrix{NF} where NF<:Union{Float32, Float64}\ndiv::Matrix{NF} where NF<:Union{Float32, Float64}\ntemp::Matrix{NF} where NF<:Union{Float32, Float64}\npres::Matrix{NF} where NF<:Union{Float32, Float64}\nhumid::Matrix{NF} where NF<:Union{Float32, Float64}\nprecip_cond::Matrix{NF} where NF<:Union{Float32, Float64}\nprecip_conv::Matrix{NF} where NF<:Union{Float32, Float64}\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.PrimitiveDryModel","page":"Function and type index","title":"SpeedyWeather.PrimitiveDryModel","text":"The PrimitiveDryModel struct holds all other structs that contain precalculated constants, whether scalars or arrays that do not change throughout model integration.\n\nspectral_grid::SpectralGrid: dictates resolution for many other components\nplanet::SpeedyWeather.AbstractPlanet: contains physical and orbital characteristics\natmosphere::SpeedyWeather.AbstractAtmosphere\ninitial_conditions::SpeedyWeather.InitialConditions\norography::SpeedyWeather.AbstractOrography{NF} where NF<:AbstractFloat\nland_sea_mask::SpeedyWeather.AbstractLandSeaMask{NF} where NF<:AbstractFloat\nphysics::Bool\nboundary_layer_drag::SpeedyWeather.BoundaryLayerDrag{NF} where NF<:AbstractFloat\ntemperature_relaxation::SpeedyWeather.TemperatureRelaxation{NF} where NF<:AbstractFloat\nstatic_energy_diffusion::SpeedyWeather.VerticalDiffusion{NF} where NF<:AbstractFloat\nvertical_advection::SpeedyWeather.VerticalAdvection{NF} where NF<:AbstractFloat\ntime_stepping::SpeedyWeather.TimeStepper{NF} where NF<:AbstractFloat\nspectral_transform::SpectralTransform\nhorizontal_diffusion::SpeedyWeather.HorizontalDiffusion{NF} where NF<:AbstractFloat\nimplicit::SpeedyWeather.AbstractImplicit{NF} where NF<:AbstractFloat\ngeometry::Geometry\nconstants::DynamicsConstants\ndevice_setup::SpeedyWeather.DeviceSetup\noutput::SpeedyWeather.AbstractOutputWriter\nfeedback::SpeedyWeather.AbstractFeedback\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.PrimitiveWetModel","page":"Function and type index","title":"SpeedyWeather.PrimitiveWetModel","text":"The PrimitiveDryModel struct holds all other structs that contain precalculated constants, whether scalars or arrays that do not change throughout model integration.\n\nspectral_grid::SpectralGrid: dictates resolution for many other components\nplanet::SpeedyWeather.AbstractPlanet: contains physical and orbital characteristics\natmosphere::SpeedyWeather.AbstractAtmosphere\ninitial_conditions::SpeedyWeather.InitialConditions\norography::SpeedyWeather.AbstractOrography{NF} where NF<:AbstractFloat\nland_sea_mask::SpeedyWeather.AbstractLandSeaMask{NF} where NF<:AbstractFloat\nphysics::Bool\nthermodynamics::SpeedyWeather.Thermodynamics{NF} where NF<:AbstractFloat\nboundary_layer_drag::SpeedyWeather.BoundaryLayerDrag{NF} where NF<:AbstractFloat\ntemperature_relaxation::SpeedyWeather.TemperatureRelaxation{NF} where NF<:AbstractFloat\nstatic_energy_diffusion::SpeedyWeather.VerticalDiffusion{NF} where NF<:AbstractFloat\nlarge_scale_condensation::SpeedyWeather.AbstractCondensation{NF} where NF<:AbstractFloat\nvertical_advection::SpeedyWeather.VerticalAdvection{NF} where NF<:AbstractFloat\ntime_stepping::SpeedyWeather.TimeStepper{NF} where NF<:AbstractFloat\nspectral_transform::SpectralTransform\nhorizontal_diffusion::SpeedyWeather.HorizontalDiffusion{NF} where NF<:AbstractFloat\nimplicit::SpeedyWeather.AbstractImplicit{NF} where NF<:AbstractFloat\ngeometry::Geometry\nconstants::DynamicsConstants\ndevice_setup::SpeedyWeather.DeviceSetup\noutput::SpeedyWeather.AbstractOutputWriter\nfeedback::SpeedyWeather.AbstractFeedback\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.PrognosticLayerTimesteps","page":"Function and type index","title":"SpeedyWeather.PrognosticLayerTimesteps","text":"Collect the n time steps of PrognosticVariablesLayer of an n-step time integration (leapfrog=2) into a single struct.\n\ntimesteps::Array{SpeedyWeather.PrognosticVariablesLayer{NF}, 1} where NF<:AbstractFloat\n\n.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.PrognosticSurfaceTimesteps","page":"Function and type index","title":"SpeedyWeather.PrognosticSurfaceTimesteps","text":"Collect the n time steps of PrognosticVariablesSurface of an n-step time integration (leapfrog=2) into a single struct.\n\ntimesteps::Array{SpeedyWeather.PrognosticVariablesSurface{NF}, 1} where NF<:AbstractFloat\n\n.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.PrognosticVariablesLayer","page":"Function and type index","title":"SpeedyWeather.PrognosticVariablesLayer","text":"A layer of the prognostic variables in spectral space.\n\ntrunc::Int64: Spectral resolution as max degree of spherical harmonics\nvor::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: Vorticity of horizontal wind field [1/s]\ndiv::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: Divergence of horizontal wind field [1/s]\ntemp::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: Absolute temperature [K]\nhumid::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: Specific humidity [kg/kg]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.PrognosticVariablesSurface","page":"Function and type index","title":"SpeedyWeather.PrognosticVariablesSurface","text":"The spectral and gridded prognostic variables at the surface.\n\ntrunc::Int64: Spectral resolution as max degree of spherical harmonics\npres::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: log of surface pressure [log(Pa)] for PrimitiveEquation, interface displacement [m] for ShallowWaterModel\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.RandomWaves","page":"Function and type index","title":"SpeedyWeather.RandomWaves","text":"Parameters for random initial conditions for the interface displacement η in the shallow water equations.\n\nA::Float64\nlmin::Int64\nlmax::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ShallowWaterModel","page":"Function and type index","title":"SpeedyWeather.ShallowWaterModel","text":"The ShallowWaterModel struct holds all other structs that contain precalculated constants, whether scalars or arrays that do not change throughout model integration.\n\nspectral_grid::SpectralGrid: dictates resolution for many other components\nplanet::SpeedyWeather.AbstractPlanet: contains physical and orbital characteristics\natmosphere::SpeedyWeather.AbstractAtmosphere\nforcing::SpeedyWeather.AbstractForcing{NF} where NF<:AbstractFloat\ndrag::SpeedyWeather.AbstractDrag{NF} where NF<:AbstractFloat\ninitial_conditions::SpeedyWeather.InitialConditions\norography::SpeedyWeather.AbstractOrography{NF} where NF<:AbstractFloat\ntime_stepping::SpeedyWeather.TimeStepper{NF} where NF<:AbstractFloat\nspectral_transform::SpectralTransform\nhorizontal_diffusion::SpeedyWeather.HorizontalDiffusion{NF} where NF<:AbstractFloat\nimplicit::SpeedyWeather.AbstractImplicit{NF} where NF<:AbstractFloat\ngeometry::Geometry\nconstants::DynamicsConstants\ndevice_setup::SpeedyWeather.DeviceSetup\noutput::SpeedyWeather.AbstractOutputWriter\nfeedback::SpeedyWeather.AbstractFeedback\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Simulation","page":"Function and type index","title":"SpeedyWeather.Simulation","text":"Simulation is a container struct to be used with run!(::Simulation). It contains\n\nprognostic_variables::PrognosticVariables: define the current state of the model\ndiagnostic_variables::DiagnosticVariables: contain the tendencies and auxiliary arrays to compute them\nmodel::SpeedyWeather.ModelSetup: all parameters, constant at runtime\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SpectralGrid","page":"Function and type index","title":"SpeedyWeather.SpectralGrid","text":"Defines the horizontal spectral resolution and corresponding grid and the vertical coordinate for SpeedyWeather.jl. Options are\n\nNF::Type{<:AbstractFloat}: number format used throughout the model\ntrunc::Int64: horizontal resolution as the maximum degree of spherical harmonics\nGrid::Type{<:SpeedyWeather.RingGrids.AbstractGrid}: horizontal grid used for calculations in grid-point space\ndealiasing::Float64: how to match spectral with grid resolution: dealiasing factor, 1=linear, 2=quadratic, 3=cubic grid\nradius::Float64: radius of the sphere [m]\nnlat_half::Int64: number of latitude rings on one hemisphere (Equator incl)\nnpoints::Int64: total number of grid points in the horizontal\nnlev::Int64: number of vertical levels\nvertical_coordinates::SpeedyWeather.VerticalCoordinates: coordinates used to discretize the vertical\n\nnlat_half and npoints should not be chosen but are derived from trunc, Grid and dealiasing.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SpeedyCondensation","page":"Function and type index","title":"SpeedyWeather.SpeedyCondensation","text":"Large scale condensation as in Fortran SPEEDY with default values from therein.\n\nnlev::Int64: number of vertical levels\nthreshold_boundary_layer::Float64: Relative humidity threshold for boundary layer\nthreshold_range::Float64: Vertical range of relative humidity threshold\nthreshold_max::Float64: Maximum relative humidity threshold [1]\ntime_scale::Float64: Relaxation time for humidity [hrs]\nn_stratosphere_levels::Base.RefValue{Int64}\nhumid_tend_max::Vector{NF} where NF<:AbstractFloat\nrelative_threshold::Vector{NF} where NF<:AbstractFloat\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SpeedyTransforms.SpectralTransform-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.SpeedyTransforms.SpectralTransform","text":"SpectralTransform(\n spectral_grid::SpectralGrid;\n recompute_legendre,\n one_more_degree,\n kwargs...\n) -> SpectralTransform\n\n\nGenerator function for a SpectralTransform struct pulling in parameters from a SpectralGrid struct.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.StartFromFile","page":"Function and type index","title":"SpeedyWeather.StartFromFile","text":"Restart from a previous SpeedyWeather.jl simulation via the restart file restart.jld2 Applies interpolation in the horizontal but not in the vertical. restart.jld2 is identified by\n\npath::String: path for restart file\nid::Union{Int64, String}: run_id of restart file in run_????/restart.jld2\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.StartWithRandomVorticity","page":"Function and type index","title":"SpeedyWeather.StartWithRandomVorticity","text":"Start with random vorticity as initial conditions\n\npower::Float64: Power of the spectral distribution k^power\namplitude::Float64: (approximate) amplitude in [1/s], used as standard deviation of spherical harmonic coefficients\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.StaticEnergyDiffusion","page":"Function and type index","title":"SpeedyWeather.StaticEnergyDiffusion","text":"Diffusion of dry static energy: A relaxation towards a reference gradient of static energy wrt to geopotential, see Fortran SPEEDY documentation.\n\ntime_scale::Float64: time scale [hrs] for strength\nstatic_energy_lapse_rate::Float64: [1] ∂SE/∂Φ, vertical gradient of static energy SE with geopotential Φ\nFstar::Base.RefValue{NF} where NF<:AbstractFloat\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Tendencies","page":"Function and type index","title":"SpeedyWeather.Tendencies","text":"Tendencies{Grid<:AbstractGrid,NF<:AbstractFloat}\n\nStruct holding the tendencies of the prognostic spectral variables for a given layer.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ZonalJet","page":"Function and type index","title":"SpeedyWeather.ZonalJet","text":"Create a struct that contains all parameters for the Galewsky et al, 2004 zonal jet intitial conditions for the shallow water model. Default values as in Galewsky.\n\nlatitude::Float64: jet latitude [˚N]\nwidth::Float64: jet width [˚], default ≈ 19.29˚\numax::Float64: jet maximum velocity [m/s]\nperturb_lat::Float64: perturbation latitude [˚N], position in jet by default\nperturb_lon::Float64: perturbation longitude [˚E]\nperturb_xwidth::Float64: perturbation zonal extent [˚], default ≈ 19.1˚\nperturb_ywidth::Float64: perturbation meridinoal extent [˚], default ≈ 3.8˚\nperturb_height::Float64: perturbation amplitude [m]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ZonalRidge","page":"Function and type index","title":"SpeedyWeather.ZonalRidge","text":"Zonal ridge orography after Jablonowski and Williamson, 2006.\n\nη₀::Float64: conversion from σ to Jablonowski's ηᵥ-coordinates\nu₀::Float64: max amplitude of zonal wind [m/s] that scales orography height\norography::SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat: height [m] on grid-point space.\ngeopot_surf::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: surface geopotential, height*gravity [m²/s²]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ZonalRidge-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.ZonalRidge","text":"ZonalRidge(spectral_grid::SpectralGrid; kwargs...) -> Any\n\n\nGenerator function pulling the resolution information from spectral_grid.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.ZonalWind","page":"Function and type index","title":"SpeedyWeather.ZonalWind","text":"Create a struct that contains all parameters for the Jablonowski and Williamson, 2006 intitial conditions for the primitive equation model. Default values as in Jablonowski.\n\nη₀::Float64: conversion from σ to Jablonowski's ηᵥ-coordinates\nu₀::Float64: max amplitude of zonal wind [m/s]\nperturb_lat::Float64: perturbation centred at [˚N]\nperturb_lon::Float64: perturbation centred at [˚E]\nperturb_uₚ::Float64: perturbation strength [m/s]\nperturb_radius::Float64: radius of Gaussian perturbation in units of Earth's radius [1]\nΔT::Float64: temperature difference used for stratospheric lapse rate [K], Jablonowski uses ΔT = 4.8e5 [K]\nTmin::Float64: minimum temperature [K] of profile\npressure_on_orography::Bool: initialize pressure given the atmosphere.lapse_rate on orography?\n\n\n\n\n\n","category":"type"},{"location":"functions/#Base.copy!-Tuple{PrognosticVariables, PrognosticVariables}","page":"Function and type index","title":"Base.copy!","text":"copy!(progn_new::PrognosticVariables, progn_old::PrognosticVariables)\n\nCopies entries of progn_old into progn_new. Only copies those variables that are present in the model of both progn_new and progn_old.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.Device-Tuple{}","page":"Function and type index","title":"SpeedyWeather.Device","text":"Device()\n\nReturn default used device for internal purposes, either CPUDevice or GPUDevice if a GPU is available.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.DeviceArray-Tuple{SpeedyWeather.GPUDevice, Any}","page":"Function and type index","title":"SpeedyWeather.DeviceArray","text":"DeviceArray(device::AbstractDevice, x)\n\nAdapts x to a CuArray when device<:GPUDevice is used, otherwise a regular Array. Uses adapt, thus also can return SubArrays etc.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.DeviceArrayNotAdapt-Tuple{SpeedyWeather.GPUDevice, Any}","page":"Function and type index","title":"SpeedyWeather.DeviceArrayNotAdapt","text":"DeviceArrayNotAdapt(device::AbstractDevice, x)\n\nReturns a CuArray when device<:GPUDevice is used, otherwise a regular Array. Doesn't uses adapt, therefore always returns CuArray/Array\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.Device_KernelAbstractions-Tuple{SpeedyWeather.CPUDevice}","page":"Function and type index","title":"SpeedyWeather.Device_KernelAbstractions","text":"Device_KernelAbstractions(::AbstractDevice)\n\nReturn used device for use with KernelAbstractions\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.Device_KernelAbstractions-Tuple{}","page":"Function and type index","title":"SpeedyWeather.Device_KernelAbstractions","text":"Device_KernelAbstractions()\n\nReturn default used device for KernelAbstractions, either CPU or CUDADevice if a GPU is available\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SpeedyTransforms.gridded!-Tuple{DiagnosticVariables, PrognosticVariables, Int64, SpeedyWeather.ModelSetup}","page":"Function and type index","title":"SpeedyWeather.SpeedyTransforms.gridded!","text":"gridded!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Int64,\n model::SpeedyWeather.ModelSetup\n)\n\n\nPropagate the spectral state of progn to diagn using time step/leapfrog index lf. Function barrier that calls gridded! for the respective model.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SpeedyTransforms.gridded!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, SpeedyWeather.PrognosticLayerTimesteps, Int64, Barotropic}","page":"Function and type index","title":"SpeedyWeather.SpeedyTransforms.gridded!","text":"gridded!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n progn::SpeedyWeather.PrognosticLayerTimesteps,\n lf::Int64,\n model::Barotropic\n)\n\n\nPropagate the spectral state of the prognostic variables progn to the diagnostic variables in diagn for the barotropic vorticity model. Updates grid vorticity, spectral stream function and spectral and grid velocities u,v.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SpeedyTransforms.gridded!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, SpeedyWeather.PrognosticLayerTimesteps, Int64, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.SpeedyTransforms.gridded!","text":"gridded!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n progn::SpeedyWeather.PrognosticLayerTimesteps,\n lf::Int64,\n model::PrimitiveEquation\n)\n\n\nPropagate the spectral state of the prognostic variables progn to the diagnostic variables in diagn for primitive equation models. Updates grid vorticity, grid divergence, grid temperature, pressure (pres_grid) and the velocities u,v.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SpeedyTransforms.gridded!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, SpeedyWeather.PrognosticLayerTimesteps, Int64, ShallowWater}","page":"Function and type index","title":"SpeedyWeather.SpeedyTransforms.gridded!","text":"gridded!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n progn::SpeedyWeather.PrognosticLayerTimesteps,\n lf::Int64,\n model::ShallowWater\n)\n\n\nPropagate the spectral state of the prognostic variables progn to the diagnostic variables in diagn for the shallow water model. Updates grid vorticity, grid divergence, grid interface displacement (pres_grid) and the velocities u,v.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.bernoulli_potential!-Union{Tuple{NF}, Tuple{SpeedyWeather.DiagnosticVariablesLayer{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, SpectralTransform}} where NF","page":"Function and type index","title":"SpeedyWeather.bernoulli_potential!","text":"bernoulli_potential!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF},\n S::SpectralTransform\n) -> LowerTriangularMatrix{Complex{NF}} where NF\n\n\nComputes the Laplace operator ∇² of the Bernoulli potential B in spectral space.\n\ncomputes the kinetic energy KE = ½(u²+v²) on the grid\ntransforms KE to spectral space\nadds geopotential for the Bernoulli potential in spectral space\ntakes the Laplace operator.\n\nThis version is used for both ShallowWater and PrimitiveEquation, only the geopotential calculation in geopotential! differs.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.boundary_layer_drag!-Tuple{ColumnVariables, LinearDrag}","page":"Function and type index","title":"SpeedyWeather.boundary_layer_drag!","text":"boundary_layer_drag!(\n column::ColumnVariables,\n scheme::LinearDrag\n)\n\n\nCompute tendency for boundary layer drag of a column and add to its tendencies fields\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.boundary_layer_drag!-Tuple{ColumnVariables, SpeedyWeather.NoBoundaryLayerDrag, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.boundary_layer_drag!","text":"NoBoundaryLayer scheme just passes.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.clip_negatives!-Union{Tuple{AbstractArray{T}}, Tuple{T}} where T","page":"Function and type index","title":"SpeedyWeather.clip_negatives!","text":"clip_negatives!(A::AbstractArray)\n\nSet all negative entries a in A to zero.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.coriolis-Tuple{Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid","page":"Function and type index","title":"SpeedyWeather.coriolis","text":"coriolis(\n grid::SpeedyWeather.RingGrids.AbstractGrid;\n rotation\n) -> Any\n\n\nReturn the Coriolis parameter f on a grid like grid on a planet of ratation [1/s]. Default rotation of Earth.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.coriolis-Union{Tuple{Grid}, Tuple{Type{Grid}, Integer}} where Grid<:SpeedyWeather.RingGrids.AbstractGrid","page":"Function and type index","title":"SpeedyWeather.coriolis","text":"coriolis(\n grid::SpeedyWeather.RingGrids.AbstractGrid;\n rotation\n) -> Any\n\n\nReturn the Coriolis parameter f on the grid Grid of resolution nlat_half on a planet of ratation [1/s]. Default rotation of Earth.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.create_output_folder-Tuple{String, Union{Int64, String}}","page":"Function and type index","title":"SpeedyWeather.create_output_folder","text":"create_output_folder(\n path::String,\n id::Union{Int64, String}\n) -> String\n\n\nCreates a new folder run_* with the identification id. Also returns the full path run_path of that folder.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.default_sigma_coordinates-Tuple{Integer}","page":"Function and type index","title":"SpeedyWeather.default_sigma_coordinates","text":"default_sigma_coordinates(nlev::Integer) -> Any\n\n\nVertical sigma coordinates defined by their nlev+1 half levels σ_levels_half. Sigma coordinates are fraction of surface pressure (p/p0) and are sorted from top (stratosphere) to bottom (surface). The first half level is at 0 the last at 1. Evaluate a generalised logistic function with coefficients in P for the distribution of values in between. Default coefficients follow the L31 configuration historically used at ECMWF.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.drag!-Union{Tuple{NF}, Tuple{SpeedyWeather.DiagnosticVariablesLayer, QuadraticDrag{NF}, DynamicsConstants}} where NF","page":"Function and type index","title":"SpeedyWeather.drag!","text":"drag!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n drag::QuadraticDrag{NF},\n C::DynamicsConstants\n)\n\n\nQuadratic drag for the momentum equations.\n\nF = -c_D/H*|(u,v)|*(u,v)\n\nwith c_D the non-dimensional drag coefficient as defined in drag::QuadraticDrag. Layer thickness H is taken from the Atmosphere via DynamicsConstants, as defined for the ShallowWaterModel.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.dry_static_energy!-Tuple{ColumnVariables, DynamicsConstants}","page":"Function and type index","title":"SpeedyWeather.dry_static_energy!","text":"dry_static_energy!(\n column::ColumnVariables,\n constants::DynamicsConstants\n)\n\n\nCompute the dry static energy SE = cₚT + Φ (latent heat times temperature plus geopotential) for the column.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.dynamics_tendencies!","page":"Function and type index","title":"SpeedyWeather.dynamics_tendencies!","text":"dynamics_tendencies!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n model::PrimitiveEquation\n) -> Any\ndynamics_tendencies!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n model::PrimitiveEquation,\n lf::Int64\n) -> Any\n\n\nCalculate all tendencies for the PrimitiveEquation model (wet or dry).\n\n\n\n\n\n","category":"function"},{"location":"functions/#SpeedyWeather.dynamics_tendencies!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, Dates.DateTime, Barotropic}","page":"Function and type index","title":"SpeedyWeather.dynamics_tendencies!","text":"dynamics_tendencies!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n time::Dates.DateTime,\n model::Barotropic\n)\n\n\nCalculate all tendencies for the BarotropicModel.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.dynamics_tendencies!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, SpeedyWeather.SurfaceVariables, LowerTriangularMatrix, Dates.DateTime, ShallowWater}","page":"Function and type index","title":"SpeedyWeather.dynamics_tendencies!","text":"dynamics_tendencies!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n surface::SpeedyWeather.SurfaceVariables,\n pres::LowerTriangularMatrix,\n time::Dates.DateTime,\n model::ShallowWater\n)\n\n\nCalculate all tendencies for the ShallowWaterModel.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.first_timesteps!-Tuple{PrognosticVariables, DiagnosticVariables, SpeedyWeather.ModelSetup, SpeedyWeather.AbstractOutputWriter}","page":"Function and type index","title":"SpeedyWeather.first_timesteps!","text":"first_timesteps!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::SpeedyWeather.ModelSetup,\n output::SpeedyWeather.AbstractOutputWriter\n) -> typeof(time)\n\n\nPerforms the first two initial time steps (Euler forward, unfiltered leapfrog) to populate the prognostic variables with two time steps (t=0,Δt) that can then be used in the normal leap frogging.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.flipsign!-Tuple{AbstractArray}","page":"Function and type index","title":"SpeedyWeather.flipsign!","text":"flipgsign!(A::AbstractArray)\n\nLike -A but in-place.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.flux_divergence!-Union{Tuple{NF}, Tuple{LowerTriangularMatrix{Complex{NF}}, SpeedyWeather.RingGrids.AbstractGrid{NF}, SpeedyWeather.DiagnosticVariablesLayer{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Geometry{NF}, SpectralTransform{NF}}} where NF","page":"Function and type index","title":"SpeedyWeather.flux_divergence!","text":"flux_divergence!(\n A_tend::LowerTriangularMatrix{Complex{NF}},\n A_grid::SpeedyWeather.RingGrids.AbstractGrid{NF},\n diagn::SpeedyWeather.DiagnosticVariablesLayer{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF},\n G::Geometry{NF},\n S::SpectralTransform{NF};\n add,\n flipsign\n)\n\n\nComputes ∇⋅((u,v)*A) with the option to add/overwrite A_tend and to flip_sign of the flux divergence by doing so.\n\nA_tend = ∇⋅((u,v)*A) for add=false, flip_sign=false\nA_tend = -∇⋅((u,v)*A) for add=false, flip_sign=true\nA_tend += ∇⋅((u,v)*A) for add=true, flip_sign=false\nA_tend -= ∇⋅((u,v)*A) for add=true, flip_sign=true\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.fluxes_to_tendencies!-Tuple{ColumnVariables, Geometry, DynamicsConstants}","page":"Function and type index","title":"SpeedyWeather.fluxes_to_tendencies!","text":"fluxes_to_tendencies!(\n column::ColumnVariables,\n geometry::Geometry,\n constants::DynamicsConstants\n)\n\n\nConvert the fluxes on half levels to tendencies on full levels.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.forcing!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, JetStreamForcing}","page":"Function and type index","title":"SpeedyWeather.forcing!","text":"forcing!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n forcing::JetStreamForcing\n)\n\n\nSet for every latitude ring the tendency to the precomputed forcing in the momentum equations following the JetStreamForcing. The forcing is precomputed in initialize!(::JetStreamForcing,::ModelSetup).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.generalised_logistic-Tuple{Any, SpeedyWeather.GenLogisticCoefs}","page":"Function and type index","title":"SpeedyWeather.generalised_logistic","text":"Generalised logistic function based on the coefficients in coefs.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.geopotential!-Tuple{DiagnosticVariables, SpeedyWeather.AbstractOrography, DynamicsConstants}","page":"Function and type index","title":"SpeedyWeather.geopotential!","text":"geopotential!(\n diagn::DiagnosticVariables,\n O::SpeedyWeather.AbstractOrography,\n C::DynamicsConstants\n)\n\n\nCompute spectral geopotential geopot from spectral temperature temp and spectral surface geopotential geopot_surf (orography*gravity).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.geopotential!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, LowerTriangularMatrix, DynamicsConstants}","page":"Function and type index","title":"SpeedyWeather.geopotential!","text":"geopotential!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n pres::LowerTriangularMatrix,\n C::DynamicsConstants\n) -> Any\n\n\ncalculates the geopotential in the ShallowWaterModel as g*η, i.e. gravity times the interface displacement (field pres)\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.geopotential!-Tuple{Vector, DynamicsConstants}","page":"Function and type index","title":"SpeedyWeather.geopotential!","text":"geopotential!(temp::Vector, C::DynamicsConstants) -> Vector\n\n\nCalculate the geopotential based on temp in a single column. This exclues the surface geopotential that would need to be added to the returned vector. Function not used in the dynamical core but for post-processing and analysis.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.get_column!-Tuple{ColumnVariables, DiagnosticVariables, Int64, Geometry, LandSeaMask}","page":"Function and type index","title":"SpeedyWeather.get_column!","text":"Recalculate ring index if not provided.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.get_column!-Tuple{ColumnVariables, DiagnosticVariables, Integer, Integer, Geometry, SpeedyWeather.AbstractLandSeaMask}","page":"Function and type index","title":"SpeedyWeather.get_column!","text":"get_column!(\n C::ColumnVariables,\n D::DiagnosticVariables,\n ij::Integer,\n jring::Integer,\n G::Geometry,\n L::SpeedyWeather.AbstractLandSeaMask\n)\n\n\nUpdate C::ColumnVariables by copying the prognostic variables from D::DiagnosticVariables at gridpoint index ij. Provide G::Geometry for coordinate information.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.get_full_output_file_path-Tuple{OutputWriter}","page":"Function and type index","title":"SpeedyWeather.get_full_output_file_path","text":"get_full_output_file_path(output::OutputWriter) -> String\n\n\nReturns the full path of the output file after it was created.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.get_run_id-Tuple{String, String}","page":"Function and type index","title":"SpeedyWeather.get_run_id","text":"get_run_id(path::String, id::String) -> String\n\n\nChecks existing run_???? folders in path to determine a 4-digit id number by counting up. E.g. if folder run_0001 exists it will return the string \"0002\". Does not create a folder for the returned run id.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.get_thermodynamics!-Tuple{ColumnVariables, PrimitiveDry}","page":"Function and type index","title":"SpeedyWeather.get_thermodynamics!","text":"get_thermodynamics!(\n column::ColumnVariables,\n model::PrimitiveDry\n)\n\n\nCalculate the dry static energy for the primitive dry model.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.get_thermodynamics!-Tuple{ColumnVariables, PrimitiveWet}","page":"Function and type index","title":"SpeedyWeather.get_thermodynamics!","text":"get_thermodynamics!(\n column::ColumnVariables,\n model::PrimitiveWet\n)\n\n\nCalculate thermodynamic quantities like saturation vapour pressure, saturation specific humidity, dry static energy, moist static energy and saturation moist static energy from the prognostic column variables.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.get_var-Tuple{PrognosticVariables, Symbol}","page":"Function and type index","title":"SpeedyWeather.get_var","text":"get_var(progn::PrognosticVariables, var_name::Symbol; lf::Integer=1)\n\nReturns the prognostic variable var_name at leapfrog index lf as a Vector{LowerTriangularMatrices}.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.has-Tuple{Type{<:SpeedyWeather.ModelSetup}, Symbol}","page":"Function and type index","title":"SpeedyWeather.has","text":"has(\n M::Type{<:SpeedyWeather.ModelSetup},\n var_name::Symbol\n) -> Bool\n\n\nReturns true if the model M has a prognostic variable var_name, false otherwise. The default fallback is that all variables are included. \n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.horizontal_diffusion!","page":"Function and type index","title":"SpeedyWeather.horizontal_diffusion!","text":"horizontal_diffusion!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n progn::SpeedyWeather.PrognosticLayerTimesteps,\n model::Barotropic\n)\nhorizontal_diffusion!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n progn::SpeedyWeather.PrognosticLayerTimesteps,\n model::Barotropic,\n lf::Int64\n)\n\n\nApply horizontal diffusion to vorticity in the Barotropic models.\n\n\n\n\n\n","category":"function"},{"location":"functions/#SpeedyWeather.horizontal_diffusion!-2","page":"Function and type index","title":"SpeedyWeather.horizontal_diffusion!","text":"horizontal_diffusion!(\n progn::SpeedyWeather.PrognosticLayerTimesteps,\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n model::ShallowWater\n)\nhorizontal_diffusion!(\n progn::SpeedyWeather.PrognosticLayerTimesteps,\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n model::ShallowWater,\n lf::Int64\n)\n\n\nApply horizontal diffusion to vorticity and diffusion in the ShallowWater models.\n\n\n\n\n\n","category":"function"},{"location":"functions/#SpeedyWeather.horizontal_diffusion!-3","page":"Function and type index","title":"SpeedyWeather.horizontal_diffusion!","text":"horizontal_diffusion!(\n progn::SpeedyWeather.PrognosticLayerTimesteps,\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n model::PrimitiveEquation\n) -> Union{Nothing, Bool}\nhorizontal_diffusion!(\n progn::SpeedyWeather.PrognosticLayerTimesteps,\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n model::PrimitiveEquation,\n lf::Int64\n) -> Union{Nothing, Bool}\n\n\nApply horizontal diffusion applied to vorticity, diffusion and temperature in the PrimitiveEquation models. Uses the constant diffusion for temperature but possibly adaptive diffusion for vorticity and divergence.\n\n\n\n\n\n","category":"function"},{"location":"functions/#SpeedyWeather.horizontal_diffusion!-Union{Tuple{NF}, Tuple{LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, AbstractVector{NF}, AbstractVector{NF}}} where NF<:AbstractFloat","page":"Function and type index","title":"SpeedyWeather.horizontal_diffusion!","text":"horizontal_diffusion!(\n tendency::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n A::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n ∇²ⁿ_expl::AbstractArray{NF<:AbstractFloat, 1},\n ∇²ⁿ_impl::AbstractArray{NF<:AbstractFloat, 1}\n)\n\n\nApply horizontal diffusion to a 2D field A in spectral space by updating its tendency tendency with an implicitly calculated diffusion term. The implicit diffusion of the next time step is split into an explicit part ∇²ⁿ_expl and an implicit part ∇²ⁿ_impl, such that both can be calculated in a single forward step by using A as well as its tendency tendency.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.implicit_correction!-Tuple{DiagnosticVariables, ImplicitPrimitiveEq, PrognosticVariables}","page":"Function and type index","title":"SpeedyWeather.implicit_correction!","text":"implicit_correction!(\n diagn::DiagnosticVariables,\n implicit::ImplicitPrimitiveEq,\n progn::PrognosticVariables\n) -> Any\n\n\nApply the implicit corrections to dampen gravity waves in the primitive equation models.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.implicit_correction!-Union{Tuple{NF}, Tuple{SpeedyWeather.DiagnosticVariablesLayer{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, SpeedyWeather.PrognosticLayerTimesteps{NF}, SpeedyWeather.SurfaceVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, SpeedyWeather.PrognosticSurfaceTimesteps{NF}, ImplicitShallowWater}} where NF","page":"Function and type index","title":"SpeedyWeather.implicit_correction!","text":"implicit_correction!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF},\n progn::SpeedyWeather.PrognosticLayerTimesteps{NF},\n diagn_surface::SpeedyWeather.SurfaceVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF},\n progn_surface::SpeedyWeather.PrognosticSurfaceTimesteps{NF},\n implicit::ImplicitShallowWater\n)\n\n\nApply correction to the tendencies in diagn to prevent the gravity waves from amplifying. The correction is implicitly evaluated using the parameter implicit.α to switch between forward, centered implicit or backward evaluation of the gravity wave terms.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n scheme::HyperDiffusion,\n k::Int64,\n G::Geometry,\n L::SpeedyWeather.TimeStepper\n)\ninitialize!(\n scheme::HyperDiffusion,\n k::Int64,\n G::Geometry,\n L::SpeedyWeather.TimeStepper,\n vor_max::Real\n)\n\n\nPrecomputes the hyper diffusion terms in scheme for layer k based on the model time step in L, the vertical level sigma level in G, and the current (absolute) vorticity maximum level vor_max\n\n\n\n\n\n","category":"function"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{Barotropic}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(model::Barotropic) -> SpeedyWeather.Simulation\n\n\nCalls all initialize! functions for components of model, except for model.output and model.feedback which are always called at in time_stepping!.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{EarthOrography, SpeedyWeather.AbstractPlanet, SpectralTransform, Geometry}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n orog::EarthOrography,\n P::SpeedyWeather.AbstractPlanet,\n S::SpectralTransform,\n G::Geometry\n) -> LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat\n\n\nInitialize the arrays orography,geopot_surf in orog by reading the orography field from file.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{Feedback, SpeedyWeather.Clock, SpeedyWeather.ModelSetup}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n feedback::Feedback,\n clock::SpeedyWeather.Clock,\n model::SpeedyWeather.ModelSetup\n) -> Union{Nothing, IOStream}\n\n\nInitializes the a Feedback struct.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{HeldSuarez, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(scheme::HeldSuarez, model::PrimitiveEquation)\n\n\ninitialize the HeldSuarez temperature relaxation by precomputing terms for the equilibrium temperature Teq.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{HyperDiffusion, SpeedyWeather.DiagnosticVariablesLayer, Geometry, SpeedyWeather.TimeStepper}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n scheme::HyperDiffusion,\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n G::Geometry,\n L::SpeedyWeather.TimeStepper\n)\n\n\nPre-function to other initialize!(::HyperDiffusion) initialisors that calculates the (absolute) vorticity maximum for the layer of diagn.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{HyperDiffusion, SpeedyWeather.ModelSetup}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n scheme::HyperDiffusion,\n model::SpeedyWeather.ModelSetup\n)\n\n\nPrecomputes the hyper diffusion terms in scheme based on the model time step, and possibly with a changing strength/power in the vertical.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{HyperDiffusion, SpeedyWeather.TimeStepper}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n scheme::HyperDiffusion,\n L::SpeedyWeather.TimeStepper\n)\n\n\nPrecomputes the 2D hyper diffusion terms in scheme based on the model time step.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{ImplicitPrimitiveEq, Real, DiagnosticVariables, Geometry, DynamicsConstants}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n implicit::ImplicitPrimitiveEq,\n dt::Real,\n diagn::DiagnosticVariables,\n geometry::Geometry,\n constants::DynamicsConstants\n)\n\n\nInitialize the implicit terms for the PrimitiveEquation models.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{ImplicitShallowWater, Real, DynamicsConstants}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n implicit::ImplicitShallowWater,\n dt::Real,\n constants::DynamicsConstants\n)\n\n\nUpdate the implicit terms in implicit for the shallow water model as they depend on the time step dt.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{JablonowskiRelaxation, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n scheme::JablonowskiRelaxation,\n model::PrimitiveEquation\n)\n\n\ninitialize the JablonowskiRelaxation temperature relaxation by precomputing terms for the equilibrium temperature Teq and the frequency (strength of relaxation).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{LinearDrag, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(scheme::LinearDrag, model::PrimitiveEquation)\n\n\nPrecomputes the drag coefficients for this BoundaryLayerDrag scheme.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{NoTemperatureRelaxation, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n scheme::NoTemperatureRelaxation,\n model::PrimitiveEquation\n)\n\n\njust passes, does not need any initialization.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{PrimitiveDry}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(model::PrimitiveDry) -> SpeedyWeather.Simulation\n\n\nCalls all initialize! functions for components of model, except for model.output and model.feedback which are always called at in time_stepping! and model.implicit which is done in first_timesteps!.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{PrimitiveWet}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(model::PrimitiveWet) -> SpeedyWeather.Simulation\n\n\nCalls all initialize! functions for components of model, except for model.output and model.feedback which are always called at in time_stepping! and model.implicit which is done in first_timesteps!.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{PrognosticVariables, StartFromFile, SpeedyWeather.ModelSetup}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn_new::PrognosticVariables,\n initial_conditions::StartFromFile,\n model::SpeedyWeather.ModelSetup\n) -> PrognosticVariables\n\n\nRestart from a previous SpeedyWeather.jl simulation via the restart file restart.jld2 Applies interpolation in the horizontal but not in the vertical.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{PrognosticVariables, ZonalJet, ShallowWater}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn::PrognosticVariables,\n initial_conditions::ZonalJet,\n model::ShallowWater\n) -> LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat\n\n\nInitial conditions from Galewsky, 2004, Tellus\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{ShallowWater}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(model::ShallowWater) -> SpeedyWeather.Simulation\n\n\nCalls all initialize! functions for components of model, except for model.output and model.feedback which are always called at in time_stepping! and model.implicit which is done in first_timesteps!.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{SpeedyCondensation, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n scheme::SpeedyCondensation,\n model::PrimitiveEquation\n)\n\n\nInitialize the SpeedyCondensation scheme.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{SpeedyWeather.Clock, SpeedyWeather.TimeStepper}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n clock::SpeedyWeather.Clock,\n time_stepping::SpeedyWeather.TimeStepper\n) -> SpeedyWeather.Clock\n\n\nInitialize the clock with the time step Δt in the time_stepping.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{SpeedyWeather.NoBoundaryLayerDrag, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"NoBoundaryLayer scheme does not need any initialisation.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{ZonalRidge, SpeedyWeather.AbstractPlanet, SpectralTransform, Geometry}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n orog::ZonalRidge,\n P::SpeedyWeather.AbstractPlanet,\n S::SpectralTransform,\n G::Geometry\n) -> LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat\n\n\nInitialize the arrays orography,geopot_surf in orog following Jablonowski and Williamson, 2006.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Union{Tuple{Model}, Tuple{output_NF}, Tuple{OutputWriter{output_NF, Model}, SpeedyWeather.AbstractFeedback, SpeedyWeather.TimeStepper, SpeedyWeather.Clock, DiagnosticVariables, Model}} where {output_NF, Model}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n output::OutputWriter{output_NF, Model},\n feedback::SpeedyWeather.AbstractFeedback,\n time_stepping::SpeedyWeather.TimeStepper,\n clock::SpeedyWeather.Clock,\n diagn::DiagnosticVariables,\n model\n)\n\n\nCreates a netcdf file on disk and the corresponding netcdf_file object preallocated with output variables and dimensions. write_output! then writes consecuitive time steps into this file.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Union{Tuple{NF}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, SpeedyWeather.RandomWaves, ShallowWater}} where NF","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn::PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF},\n initial_conditions::SpeedyWeather.RandomWaves,\n model::ShallowWater\n)\n\n\nRandom initial conditions for the interface displacement η in the shallow water equations. The flow (u,v) is zero initially. This kicks off gravity waves that will interact with orography.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Union{Tuple{NF}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, StartWithRandomVorticity, SpeedyWeather.ModelSetup}} where NF","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn::PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF},\n initial_conditions::StartWithRandomVorticity,\n model::SpeedyWeather.ModelSetup\n)\n\n\nStart with random vorticity as initial conditions\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Union{Tuple{NF}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, ZonalWind, PrimitiveEquation}} where NF","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn::PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF},\n initial_conditions::ZonalWind,\n model::PrimitiveEquation\n)\n\n\nInitial conditions from Jablonowski and Williamson, 2006, QJR Meteorol. Soc\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Union{Tuple{NF}, Tuple{SpeedyWeather.StaticEnergyDiffusion{NF}, PrimitiveEquation}} where NF","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n scheme::SpeedyWeather.StaticEnergyDiffusion{NF},\n model::PrimitiveEquation\n) -> Any\n\n\nInitialize dry static energy diffusion.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize_geopotential-Tuple{Vector, Vector, Real}","page":"Function and type index","title":"SpeedyWeather.initialize_geopotential","text":"initialize_geopotential(\n σ_levels_full::Vector,\n σ_levels_half::Vector,\n R_dry::Real\n) -> Tuple{Vector{Float64}, Vector{Float64}}\n\n\nPrecomputes constants for the vertical integration of the geopotential, defined as\n\nΦ_{k+1/2} = Φ_{k+1} + R*T_{k+1}*(ln(p_{k+1}) - ln(p_{k+1/2})) (half levels) Φ_k = Φ_{k+1/2} + R*T_k*(ln(p_{k+1/2}) - ln(p_k)) (full levels)\n\nSame formula but k → k-1/2.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.isdecreasing-Tuple{AbstractVector}","page":"Function and type index","title":"SpeedyWeather.isdecreasing","text":"isdecreasing(x::AbstractVector) -> Bool\n\n\nCheck whether elements of a vector v are strictly decreasing.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.isincreasing-Tuple{AbstractVector}","page":"Function and type index","title":"SpeedyWeather.isincreasing","text":"isincreasing(x::AbstractVector) -> Bool\n\n\nCheck whether elements of a vector v are strictly increasing.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.large_scale_condensation!-Tuple{ColumnVariables, PrimitiveDry}","page":"Function and type index","title":"SpeedyWeather.large_scale_condensation!","text":"large_scale_condensation!(\n column::ColumnVariables,\n model::PrimitiveDry\n)\n\n\nNo condensation in a PrimitiveDry model.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.large_scale_condensation!-Tuple{ColumnVariables, PrimitiveWet}","page":"Function and type index","title":"SpeedyWeather.large_scale_condensation!","text":"Function barrier only.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.large_scale_condensation!-Union{Tuple{NF}, Tuple{ColumnVariables{NF}, SpeedyCondensation, Geometry, DynamicsConstants, SpeedyWeather.AbstractAtmosphere, SpeedyWeather.TimeStepper}} where NF","page":"Function and type index","title":"SpeedyWeather.large_scale_condensation!","text":"large_scale_condensation!(\n column::ColumnVariables{NF},\n scheme::SpeedyCondensation,\n geometry::Geometry,\n constants::DynamicsConstants,\n atmosphere::SpeedyWeather.AbstractAtmosphere,\n time_stepping::SpeedyWeather.TimeStepper\n)\n\n\nLarge-scale condensation for a column by relaxation back to a reference relative humidity if larger than that. Calculates the tendencies for specific humidity and temperature and integrates the large-scale precipitation vertically for output.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.launch_kernel!-Tuple{SpeedyWeather.DeviceSetup, Any, Any, Vararg{Any}}","page":"Function and type index","title":"SpeedyWeather.launch_kernel!","text":"launch_kernel!(device_setup::DeviceSetup, kernel!, ndrange, kernel_args...)\n\nLaunches the kernel! on the device_setup with ndrange computations over the kernel and arguments kernel_args\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.leapfrog!-Union{Tuple{NF}, Tuple{LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, Real, Int64, Leapfrog{NF}}} where NF<:AbstractFloat","page":"Function and type index","title":"SpeedyWeather.leapfrog!","text":"leapfrog!(\n A_old::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n A_new::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n tendency::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n dt::Real,\n lf::Int64,\n L::Leapfrog{NF<:AbstractFloat}\n)\n\n\nPerforms one leapfrog time step with (lf=2) or without (lf=1) Robert+Williams filter (see Williams (2009), Montly Weather Review, Eq. 7-9).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.linear_pressure_gradient!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, SpeedyWeather.PrognosticSurfaceTimesteps, Int64, DynamicsConstants, ImplicitPrimitiveEq}","page":"Function and type index","title":"SpeedyWeather.linear_pressure_gradient!","text":"linear_pressure_gradient!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n surface::SpeedyWeather.PrognosticSurfaceTimesteps,\n lf::Int64,\n C::DynamicsConstants,\n I::ImplicitPrimitiveEq\n) -> LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat\n\n\nAdd the linear contribution of the pressure gradient to the geopotential. The pressure gradient in the divergence equation takes the form\n\n-∇⋅(Rd*Tᵥ*∇lnpₛ) = -∇⋅(Rd*Tᵥ'*∇lnpₛ) - ∇²(Rd*Tₖ*lnpₛ)\n\nSo that the second term inside the Laplace operator can be added to the geopotential. Rd is the gas constant, Tᵥ the virtual temperature and Tᵥ' its anomaly wrt to the average or reference temperature Tₖ, lnpₛ is the logarithm of surface pressure.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.linear_virtual_temperature!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, SpeedyWeather.PrognosticLayerTimesteps, DynamicsConstants, Int64}","page":"Function and type index","title":"SpeedyWeather.linear_virtual_temperature!","text":"linear_virtual_temperature!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n progn::SpeedyWeather.PrognosticLayerTimesteps,\n constants::DynamicsConstants,\n lf::Int64\n) -> Any\n\n\nCalculates a linearised virtual temperature Tᵥ as\n\nTᵥ = T + Tₖμq\n\nWith absolute temperature T, layer-average temperarture Tₖ (computed in temperature_average!), specific humidity q and\n\nμ = (1-ξ)/ξ, ξ = R_dry/R_vapour.\n\nin spectral space.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.linear_virtual_temperature!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, SpeedyWeather.PrognosticLayerTimesteps, PrimitiveDry, Integer}","page":"Function and type index","title":"SpeedyWeather.linear_virtual_temperature!","text":"linear_virtual_temperature!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n progn::SpeedyWeather.PrognosticLayerTimesteps,\n model::PrimitiveDry,\n lf::Integer\n) -> LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat\n\n\nLinear virtual temperature for model::PrimitiveDry: Just copy over arrays from temp to temp_virt at timestep lf in spectral space as humidity is zero in this model.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.load_trajectory-Tuple{Union{String, Symbol}, SpeedyWeather.ModelSetup}","page":"Function and type index","title":"SpeedyWeather.load_trajectory","text":"load_trajectory(\n var_name::Union{String, Symbol},\n model::SpeedyWeather.ModelSetup\n) -> Any\n\n\nLoads a var_name trajectory of the model M that has been saved in a netCDF file during the time stepping.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.moist_static_energy!-Tuple{ColumnVariables, SpeedyWeather.Thermodynamics}","page":"Function and type index","title":"SpeedyWeather.moist_static_energy!","text":"moist_static_energy!(\n column::ColumnVariables,\n thermodynamics::SpeedyWeather.Thermodynamics\n)\n\n\nCompute the moist static energy\n\nMSE = SE + Lc*Q = cₚT + Φ + Lc*Q\n\nwith the static energy SE, the latent heat of condensation Lc, the geopotential Φ. As well as the saturation moist static energy which replaces Q with Q_sat\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.nans-Tuple","page":"Function and type index","title":"SpeedyWeather.nans","text":"A = nans(dims...)\n\nAllocate A::Array{Float64} with NaNs.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.nans-Union{Tuple{T}, Tuple{Type{T}, Vararg{Any}}} where T","page":"Function and type index","title":"SpeedyWeather.nans","text":"A = nans(T,dims...)\n\nAllocate array A with NaNs of type T. Similar to zeros(T,dims...).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.nar_detection!-Tuple{Feedback, PrognosticVariables}","page":"Function and type index","title":"SpeedyWeather.nar_detection!","text":"nar_detection!(\n feedback::Feedback,\n progn::PrognosticVariables\n) -> Union{Nothing, Bool}\n\n\nDetect NaR (Not-a-Real) in the prognostic variables.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.parameterization_tendencies!-Tuple{DiagnosticVariables, Dates.DateTime, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.parameterization_tendencies!","text":"parameterization_tendencies!(\n diagn::DiagnosticVariables,\n time::Dates.DateTime,\n model::PrimitiveEquation\n) -> Any\n\n\nCompute tendencies for u,v,temp,humid from physical parametrizations. Extract for each vertical atmospheric column the prognostic variables (stored in diagn as they are grid-point transformed), loop over all grid-points, compute all parametrizations on a single-column basis, then write the tendencies back into a horizontal field of tendencies.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.pressure_on_orography!-Tuple{PrognosticVariables, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.pressure_on_orography!","text":"pressure_on_orography!(\n progn::PrognosticVariables,\n model::PrimitiveEquation\n)\n\n\nInitialize surface pressure on orography by integrating the hydrostatic equation with the reference temperature lapse rate.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.print_fields-Tuple{IO, Any, Any}","page":"Function and type index","title":"SpeedyWeather.print_fields","text":"print_fields(io::IO, A, keys; arrays)\n\n\nPrints to io all fields of a struct A identified by their keys.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.progress!-Tuple{Feedback}","page":"Function and type index","title":"SpeedyWeather.progress!","text":"progress!(feedback::Feedback)\n\n\nCalls the progress meter and writes every 5% progress increase to txt.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.progress_finish!-Tuple{Feedback}","page":"Function and type index","title":"SpeedyWeather.progress_finish!","text":"progress_finish!(F::Feedback)\n\n\nFinalises the progress meter and the progress txt file.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.readable_secs-Tuple{Real}","page":"Function and type index","title":"SpeedyWeather.readable_secs","text":"readable_secs(secs::Real) -> Dates.CompoundPeriod\n\n\nReturns Dates.CompoundPeriod rounding to either (days, hours), (hours, minutes), (minutes, seconds), or seconds with 1 decimal place accuracy for >10s and two for less. E.g.\n\njulia> readable_secs(12345)\n3 hours, 26 minutes\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.remaining_time-Tuple{ProgressMeter.Progress}","page":"Function and type index","title":"SpeedyWeather.remaining_time","text":"remaining_time(p::ProgressMeter.Progress) -> String\n\n\nEstimates the remaining time from a ProgresssMeter.Progress. Adapted from ProgressMeter.jl\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.reset_column!-Union{Tuple{ColumnVariables{NF}}, Tuple{NF}} where NF","page":"Function and type index","title":"SpeedyWeather.reset_column!","text":"reset_column!(column::ColumnVariables{NF})\n\n\nSet the accumulators (tendencies but also vertical sums and similar) back to zero for column to be reused at other grid points.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.run!-Tuple{SpeedyWeather.Simulation}","page":"Function and type index","title":"SpeedyWeather.run!","text":"run!(\n simulation::SpeedyWeather.Simulation;\n initialize,\n n_days,\n startdate,\n output\n) -> PrognosticVariables\n\n\nRun a SpeedyWeather.jl simulation. The simulation.model is assumed to be initialized, otherwise use initialize=true as keyword argument.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.saturation_humidity!-Tuple{ColumnVariables, SpeedyWeather.Thermodynamics}","page":"Function and type index","title":"SpeedyWeather.saturation_humidity!","text":"saturation_humidity!(\n column::ColumnVariables,\n thermodynamics::SpeedyWeather.Thermodynamics\n)\n\n\nCompute (1) the saturation vapour pressure as a function of temperature using the August-Roche-Magnus formula,\n\neᵢ(T) = e₀ * exp(Cᵢ * (T - T₀) / (T - Tᵢ)),\n\nwhere T is in Kelvin and i = 1,2 for saturation with respect to water and ice, respectively. And (2) the saturation specific humidity according to the formula,\n\n0.622 * e / (p - (1 - 0.622) * e),\n\nwhere e is the saturation vapour pressure, p is the pressure, and 0.622 is the ratio of the molecular weight of water to dry air.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.scale!-Tuple{PrognosticVariables, Real}","page":"Function and type index","title":"SpeedyWeather.scale!","text":"scale!(progn::PrognosticVariables, scale::Real) -> Real\n\n\nScales the prognostic variables vorticity and divergence with the Earth's radius which is used in the dynamical core.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.scale!-Union{Tuple{NF}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Symbol, Real}} where NF","page":"Function and type index","title":"SpeedyWeather.scale!","text":"scale!(\n progn::PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF},\n var::Symbol,\n scale::Real\n)\n\n\nScale the variable var inside progn with scalar scale.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_divergence!-Tuple{PrognosticVariables, Vararg{Any}}","page":"Function and type index","title":"SpeedyWeather.set_divergence!","text":"set_divergence!(progn::PrognosticVariables, varargs...; kwargs...)\n\nSee set_var!\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_humidity!-Tuple{PrognosticVariables, Vararg{Any}}","page":"Function and type index","title":"SpeedyWeather.set_humidity!","text":"set_humidity!(progn::PrognosticVariables, varargs...; kwargs...)\n\nSee set_var!\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_pressure!-Tuple{PrognosticVariables, AbstractMatrix}","page":"Function and type index","title":"SpeedyWeather.set_pressure!","text":"set_pressure!(progn::PrognosticVariables{NF}, \n pressure::AbstractMatrix, \n Grid::Type{<:AbstractGrid}, \n lf::Integer=1) where NF\n\nSets the prognostic variable with the surface pressure in grid space at leapfrog index lf.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_pressure!-Tuple{PrognosticVariables, LowerTriangularMatrix}","page":"Function and type index","title":"SpeedyWeather.set_pressure!","text":"set_pressure!(progn::PrognosticVariables{NF}, \n pressure::LowerTriangularMatrix;\n lf::Integer=1) where NF\n\nSets the prognostic variable with the surface pressure in spectral space at leapfrog index lf.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_pressure!-Tuple{PrognosticVariables, SpeedyWeather.RingGrids.AbstractGrid, SpeedyWeather.ModelSetup}","page":"Function and type index","title":"SpeedyWeather.set_pressure!","text":"set_pressure!(progn::PrognosticVariables{NF}, \n pressure::AbstractGrid, \n M::ModelSetup;\n lf::Integer=1) where NF\n\nSets the prognostic variable with the surface pressure in grid space at leapfrog index lf.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_pressure!-Tuple{PrognosticVariables, SpeedyWeather.RingGrids.AbstractGrid}","page":"Function and type index","title":"SpeedyWeather.set_pressure!","text":"set_pressure!(progn::PrognosticVariables{NF}, \n pressure::AbstractGrid, \n lf::Integer=1) where NF\n\nSets the prognostic variable with the surface pressure in grid space at leapfrog index lf.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_temperature!-Tuple{PrognosticVariables, Vararg{Any}}","page":"Function and type index","title":"SpeedyWeather.set_temperature!","text":"set_temperature!(progn::PrognosticVariables, varargs...; kwargs...)\n\nSee set_var!\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_var!-Union{Tuple{NF}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Symbol, Number}} where NF","page":"Function and type index","title":"SpeedyWeather.set_var!","text":"function set_var!(progn::PrognosticVariables{NF}, \n varname::Symbol, \n s::Number;\n lf::Integer=1) where NF\n\nSets all values of prognostic variable varname at leapfrog index lf to the scalar s.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_var!-Union{Tuple{NF}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Symbol, Vector{<:AbstractMatrix}}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Symbol, Vector{<:AbstractMatrix}, Type{<:SpeedyWeather.RingGrids.AbstractGrid}}} where NF","page":"Function and type index","title":"SpeedyWeather.set_var!","text":"set_var!(progn::PrognosticVariables{NF}, \n varname::Symbol, \n var::Vector{<:AbstractMatrix}, \n Grid::Type{<:AbstractGrid}=FullGaussianGrid;\n lf::Integer=1) where NF\n\nSets the prognostic variable with the name varname in all layers at leapfrog index lf with values given in var a vector with all information for all layers in grid space.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_var!-Union{Tuple{NF}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Symbol, Vector{<:LowerTriangularMatrix}}} where NF","page":"Function and type index","title":"SpeedyWeather.set_var!","text":"set_var!(progn::PrognosticVariables{NF}, \n varname::Symbol, \n var::Vector{<:LowerTriangularMatrix};\n lf::Integer=1) where NF\n\nSets the prognostic variable with the name varname in all layers at leapfrog index lf with values given in var a vector with all information for all layers in spectral space.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_var!-Union{Tuple{NF}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Symbol, Vector{<:SpeedyWeather.RingGrids.AbstractGrid}, SpeedyWeather.ModelSetup}} where NF","page":"Function and type index","title":"SpeedyWeather.set_var!","text":"set_var!(progn::PrognosticVariables{NF}, \n varname::Symbol, \n var::Vector{<:AbstractGrid}, \n M::ModelSetup;\n lf::Integer=1) where NF\n\nSets the prognostic variable with the name varname in all layers at leapfrog index lf with values given in var a vector with all information for all layers in grid space.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_var!-Union{Tuple{NF}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Symbol, Vector{<:SpeedyWeather.RingGrids.AbstractGrid}}} where NF","page":"Function and type index","title":"SpeedyWeather.set_var!","text":"set_var!(progn::PrognosticVariables{NF}, \n varname::Symbol, \n var::Vector{<:AbstractGrid};\n lf::Integer=1) where NF\n\nSets the prognostic variable with the name varname in all layers at leapfrog index lf with values given in var a vector with all information for all layers in grid space.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_vorticity!-Tuple{PrognosticVariables, Vararg{Any}}","page":"Function and type index","title":"SpeedyWeather.set_vorticity!","text":"set_vorticity!(progn::PrognosticVariables, varargs...; kwargs...)\n\nSee set_var!\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.sigma_okay-Tuple{Integer, AbstractVector}","page":"Function and type index","title":"SpeedyWeather.sigma_okay","text":"sigma_okay(nlev::Integer, σ_half::AbstractVector) -> Bool\n\n\nCheck that nlev and σ_half match.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.speedstring-Tuple{Any, Any}","page":"Function and type index","title":"SpeedyWeather.speedstring","text":"speedstring(sec_per_iter, dt_in_sec) -> String\n\n\ndefine a ProgressMeter.speedstring method that also takes a time step dt_in_sec to translate sec/iteration to days/days-like speeds.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.static_energy_diffusion!-Union{Tuple{NF}, Tuple{ColumnVariables{NF}, SpeedyWeather.StaticEnergyDiffusion}} where NF","page":"Function and type index","title":"SpeedyWeather.static_energy_diffusion!","text":"static_energy_diffusion!(\n column::ColumnVariables{NF},\n scheme::SpeedyWeather.StaticEnergyDiffusion\n)\n\n\nApply dry static energy diffusion.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.surface_pressure_tendency!-Tuple{SpeedyWeather.SurfaceVariables, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.surface_pressure_tendency!","text":"surface_pressure_tendency!( Prog::PrognosticVariables,\n Diag::DiagnosticVariables,\n lf::Int,\n M::PrimitiveEquation)\n\nComputes the tendency of the logarithm of surface pressure as\n\n-(ū*px + v̄*py) - D̄\n\nwith ū,v̄ being the vertically averaged velocities; px, py the gradients of the logarithm of surface pressure ln(p_s) and D̄ the vertically averaged divergence.\n\nCalculate ∇ln(p_s) in spectral space, convert to grid.\nMultiply ū,v̄ with ∇ln(p_s) in grid-point space, convert to spectral.\nD̄ is subtracted in spectral space.\nSet tendency of the l=m=0 mode to 0 for better mass conservation.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.temperature_anomaly!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, ImplicitPrimitiveEq}","page":"Function and type index","title":"SpeedyWeather.temperature_anomaly!","text":"Convert absolute and virtual temperature to anomalies wrt to the reference profile\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.temperature_average!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, LowerTriangularMatrix, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.temperature_average!","text":"temperature_average!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n temp::LowerTriangularMatrix,\n S::SpectralTransform\n) -> Any\n\n\nCalculates the average temperature of a layer from the l=m=0 harmonic and stores the result in diagn.temp_average\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.temperature_relaxation!-Tuple{ColumnVariables, JablonowskiRelaxation}","page":"Function and type index","title":"SpeedyWeather.temperature_relaxation!","text":"temperature_relaxation!(\n column::ColumnVariables,\n scheme::JablonowskiRelaxation\n)\n\n\nApply HeldSuarez-like temperature relaxation to the Jablonowski and Williamson vertical profile.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.temperature_relaxation!-Tuple{ColumnVariables, NoTemperatureRelaxation}","page":"Function and type index","title":"SpeedyWeather.temperature_relaxation!","text":"temperature_relaxation!(\n column::ColumnVariables,\n scheme::NoTemperatureRelaxation\n)\n\n\njust passes.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.temperature_relaxation!-Union{Tuple{NF}, Tuple{ColumnVariables{NF}, HeldSuarez}} where NF","page":"Function and type index","title":"SpeedyWeather.temperature_relaxation!","text":"temperature_relaxation!(\n column::ColumnVariables{NF},\n scheme::HeldSuarez\n)\n\n\nApply temperature relaxation following Held and Suarez 1996, BAMS.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.temperature_tendency!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, DynamicsConstants, Geometry, SpectralTransform, ImplicitPrimitiveEq}","page":"Function and type index","title":"SpeedyWeather.temperature_tendency!","text":"temperature_tendency!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n C::DynamicsConstants,\n G::Geometry,\n S::SpectralTransform,\n I::ImplicitPrimitiveEq\n)\n\n\nCompute the temperature tendency\n\n∂T/∂t += -∇⋅((u,v)*T') + T'D + κTᵥ*Dlnp/Dt\n\n+= because the tendencies already contain parameterizations and vertical advection. T' is the anomaly with respect to the reference/average temperature. Tᵥ is the virtual temperature used in the adiabatic term κTᵥ*Dlnp/Dt.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.temperature_tendency!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.temperature_tendency!","text":"temperature_tendency!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n model::PrimitiveEquation\n)\n\n\nFunction barrier to unpack model.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.time_stepping!-Tuple{PrognosticVariables, DiagnosticVariables, SpeedyWeather.ModelSetup}","page":"Function and type index","title":"SpeedyWeather.time_stepping!","text":"time_stepping!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::SpeedyWeather.ModelSetup\n) -> PrognosticVariables\n\n\nMain time loop that that initializes output and feedback, loops over all time steps and calls the output and feedback functions.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.timestep!","page":"Function and type index","title":"SpeedyWeather.timestep!","text":"timestep!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n dt::Real,\n model::Barotropic\n)\ntimestep!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n dt::Real,\n model::Barotropic,\n lf1::Int64\n)\ntimestep!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n dt::Real,\n model::Barotropic,\n lf1::Int64,\n lf2::Int64\n)\n\n\nCalculate a single time step for the model <: Barotropic.\n\n\n\n\n\n","category":"function"},{"location":"functions/#SpeedyWeather.timestep!-Union{Tuple{NF}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, DiagnosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Real, PrimitiveEquation}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, DiagnosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Real, PrimitiveEquation, Int64}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, DiagnosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Real, PrimitiveEquation, Int64, Int64}} where NF<:AbstractFloat","page":"Function and type index","title":"SpeedyWeather.timestep!","text":"timestep!(\n progn::PrognosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n diagn::DiagnosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n dt::Real,\n model::PrimitiveEquation\n) -> Any\ntimestep!(\n progn::PrognosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n diagn::DiagnosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n dt::Real,\n model::PrimitiveEquation,\n lf1::Int64\n) -> Any\ntimestep!(\n progn::PrognosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n diagn::DiagnosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n dt::Real,\n model::PrimitiveEquation,\n lf1::Int64,\n lf2::Int64\n) -> Any\n\n\nCalculate a single time step for the model<:PrimitiveEquation\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.timestep!-Union{Tuple{NF}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, DiagnosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Real, ShallowWater}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, DiagnosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Real, ShallowWater, Int64}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, DiagnosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Real, ShallowWater, Int64, Int64}} where NF<:AbstractFloat","page":"Function and type index","title":"SpeedyWeather.timestep!","text":"timestep!(\n progn::PrognosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n diagn::DiagnosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n dt::Real,\n model::ShallowWater\n) -> Union{Nothing, SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat}\ntimestep!(\n progn::PrognosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n diagn::DiagnosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n dt::Real,\n model::ShallowWater,\n lf1::Int64\n) -> Union{Nothing, SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat}\ntimestep!(\n progn::PrognosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n diagn::DiagnosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n dt::Real,\n model::ShallowWater,\n lf1::Int64,\n lf2::Int64\n) -> Union{Nothing, SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat}\n\n\nCalculate a single time step for the model <: ShallowWater.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.underflow!-Union{Tuple{T}, Tuple{AbstractArray{T}, Real}} where T","page":"Function and type index","title":"SpeedyWeather.underflow!","text":"underflow!(A::AbstractArray,ϵ::Real)\n\nUnderflows element a in A to zero if abs(a) < ϵ.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.unscale!-Tuple{AbstractArray, Real}","page":"Function and type index","title":"SpeedyWeather.unscale!","text":"unscale!(variable::AbstractArray, scale::Real) -> Any\n\n\nUndo the radius-scaling for any variable. Method used for netcdf output.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.unscale!-Tuple{PrognosticVariables}","page":"Function and type index","title":"SpeedyWeather.unscale!","text":"unscale!(progn::PrognosticVariables) -> Int64\n\n\nUndo the radius-scaling of vorticity and divergence from scale!(progn,scale::Real).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.vertical_integration!-Union{Tuple{NF}, Tuple{DiagnosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Int64, Geometry{NF}}} where NF","page":"Function and type index","title":"SpeedyWeather.vertical_integration!","text":"vertical_integration!(Diag::DiagnosticVariables,G::Geometry)\n\nCalculates the vertically averaged (weighted by the thickness of the σ level) velocities (*coslat) and divergence. E.g.\n\nu_mean = ∑_k=1^nlev Δσ_k * u_k\n\nu,v are averaged in grid-point space, divergence in spectral space.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.virtual_temperature!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, LowerTriangularMatrix, DynamicsConstants}","page":"Function and type index","title":"SpeedyWeather.virtual_temperature!","text":"virtual_temperature!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n temp::LowerTriangularMatrix,\n constants::DynamicsConstants\n)\n\n\nCalculates the virtual temperature Tᵥ as\n\nTᵥ = T(1+μq)\n\nWith absolute temperature T, specific humidity q and\n\nμ = (1-ξ)/ξ, ξ = R_dry/R_vapour.\n\nin grid-point space.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.virtual_temperature!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, LowerTriangularMatrix, PrimitiveDry}","page":"Function and type index","title":"SpeedyWeather.virtual_temperature!","text":"virtual_temperature!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n temp::LowerTriangularMatrix,\n model::PrimitiveDry\n) -> SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat\n\n\nVirtual temperature in grid-point space: For the PrimitiveDry temperature and virtual temperature are the same (humidity=0). Just copy over the arrays.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.volume_flux_divergence!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, SpeedyWeather.SurfaceVariables, SpeedyWeather.AbstractOrography, DynamicsConstants, Geometry, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.volume_flux_divergence!","text":"volume_flux_divergence!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n surface::SpeedyWeather.SurfaceVariables,\n orog::SpeedyWeather.AbstractOrography,\n constants::DynamicsConstants,\n G::Geometry,\n S::SpectralTransform\n)\n\n\nComputes the (negative) divergence of the volume fluxes uh,vh for the continuity equation, -∇⋅(uh,vh).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.vordiv_tendencies!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, SpeedyWeather.SurfaceVariables, DynamicsConstants, Geometry, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.vordiv_tendencies!","text":"vordiv_tendencies!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n surf::SpeedyWeather.SurfaceVariables,\n C::DynamicsConstants,\n G::Geometry,\n S::SpectralTransform\n)\n\n\nTendencies for vorticity and divergence. Excluding Bernoulli potential with geopotential and linear pressure gradient inside the Laplace operator, which are added later in spectral space.\n\nu_tend += v*(f+ζ) - RTᵥ'*∇lnp_x\nv_tend += -u*(f+ζ) - RTᵥ'*∇lnp_y\n\n+= because the tendencies already contain the parameterizations and vertical advection. f is coriolis, ζ relative vorticity, R the gas constant Tᵥ' the virtual temperature anomaly, ∇lnp the gradient of surface pressure and _x and _y its zonal/meridional components. The tendencies are then curled/dived to get the tendencies for vorticity/divergence in spectral space\n\n∂ζ/∂t = ∇×(u_tend,v_tend)\n∂D/∂t = ∇⋅(u_tend,v_tend) + ...\n\n+ ... because there's more terms added later for divergence.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.vordiv_tendencies!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, SpeedyWeather.SurfaceVariables, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.vordiv_tendencies!","text":"vordiv_tendencies!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n surf::SpeedyWeather.SurfaceVariables,\n model::PrimitiveEquation\n)\n\n\nFunction barrier to unpack model.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.vorticity_flux!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, Barotropic}","page":"Function and type index","title":"SpeedyWeather.vorticity_flux!","text":"vorticity_flux!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n model::Barotropic\n)\n\n\nVorticity flux tendency in the barotropic vorticity equation\n\n∂ζ/∂t = ∇×(u_tend,v_tend)\n\nwith\n\nu_tend = Fᵤ + v*(ζ+f) v_tend = Fᵥ - u*(ζ+f)\n\nwith Fᵤ,Fᵥ the forcing from forcing! already in u_tend_grid/v_tend_grid and vorticity ζ, coriolis f.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.vorticity_flux!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, ShallowWater}","page":"Function and type index","title":"SpeedyWeather.vorticity_flux!","text":"vorticity_flux!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n model::ShallowWater\n)\n\n\nVorticity flux tendency in the shallow water equations\n\n∂ζ/∂t = ∇×(u_tend,v_tend) ∂D/∂t = ∇⋅(u_tend,v_tend)\n\nwith\n\nu_tend = Fᵤ + v*(ζ+f) v_tend = Fᵥ - u*(ζ+f)\n\nwith Fᵤ,Fᵥ the forcing from forcing! already in u_tend_grid/v_tend_grid and vorticity ζ, coriolis f.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.vorticity_flux_curldiv!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, DynamicsConstants, Geometry, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.vorticity_flux_curldiv!","text":"vorticity_flux_curldiv!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n C::DynamicsConstants,\n G::Geometry,\n S::SpectralTransform;\n div,\n add\n)\n\n\nCompute the vorticity advection as the curl/div of the vorticity fluxes\n\n∂ζ/∂t = ∇×(u_tend,v_tend) ∂D/∂t = ∇⋅(u_tend,v_tend)\n\nwith\n\nu_tend = Fᵤ + v*(ζ+f) v_tend = Fᵥ - u*(ζ+f)\n\nwith Fᵤ,Fᵥ from u_tend_grid/v_tend_grid that are assumed to be alread set in forcing!. Set div=false for the BarotropicModel which doesn't require the divergence tendency.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.workgroup_size-Tuple{SpeedyWeather.AbstractDevice}","page":"Function and type index","title":"SpeedyWeather.workgroup_size","text":"workgroup_size(dev::AbstractDevice)\n\nReturns a workgroup size depending on dev. WIP: Will be expanded in the future to also include grid information. \n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.write_column_tendencies!-Tuple{DiagnosticVariables, ColumnVariables, Int64}","page":"Function and type index","title":"SpeedyWeather.write_column_tendencies!","text":"write_column_tendencies!(\n D::DiagnosticVariables,\n C::ColumnVariables,\n ij::Int64\n)\n\n\nWrite the parametrization tendencies from C::ColumnVariables into the horizontal fields of tendencies stored in D::DiagnosticVariables at gridpoint index ij.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.write_netcdf_time!-Tuple{OutputWriter, Dates.DateTime}","page":"Function and type index","title":"SpeedyWeather.write_netcdf_time!","text":"write_netcdf_time!(\n output::OutputWriter,\n time::Dates.DateTime\n)\n\n\nWrite the current time time::DateTime to the netCDF file in output::OutputWriter.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.write_netcdf_variables!-Union{Tuple{Model}, Tuple{Grid}, Tuple{NF}, Tuple{OutputWriter, DiagnosticVariables{NF, Grid, Model}}} where {NF, Grid, Model}","page":"Function and type index","title":"SpeedyWeather.write_netcdf_variables!","text":"write_netcdf_variables!(\n output::OutputWriter,\n diagn::DiagnosticVariables{NF, Grid, Model}\n)\n\n\nWrite diagnostic variables from diagn to the netCDF file in output::OutputWriter.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.write_output!-Tuple{OutputWriter, Dates.DateTime, DiagnosticVariables}","page":"Function and type index","title":"SpeedyWeather.write_output!","text":"write_output!(\n outputter::OutputWriter,\n time::Dates.DateTime,\n diagn::DiagnosticVariables\n)\n\n\nWrites the variables from diagn of time step i at time time into outputter.netcdf_file. Simply escapes for no netcdf output of if output shouldn't be written on this time step. Interpolates onto output grid and resolution as specified in outputter, converts to output number format, truncates the mantissa for higher compression and applies lossless compression.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.write_restart_file-Tuple{PrognosticVariables, OutputWriter}","page":"Function and type index","title":"SpeedyWeather.write_restart_file","text":"write_restart_file(\n progn::PrognosticVariables,\n output::OutputWriter\n) -> Union{Nothing, String}\n\n\nA restart file restart.jld2 with the prognostic variables is written to the output folder (or current path) that can be used to restart the model. restart.jld2 will then be used as initial conditions. The prognostic variables are bitrounded for compression and the 2nd leapfrog time step is discarded. Variables in restart file are unscaled.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.zero_tendencies!-Union{Tuple{DiagnosticVariables{NF, Grid, Model}}, Tuple{Model}, Tuple{Grid}, Tuple{NF}} where {NF, Grid, Model<:Barotropic}","page":"Function and type index","title":"SpeedyWeather.zero_tendencies!","text":"zero_tendencies!(\n diagn::DiagnosticVariables{NF, Grid, Model<:Barotropic}\n)\n\n\nSet the tendencies in diagn to zero.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.zero_tendencies!-Union{Tuple{DiagnosticVariables{NF, Grid, Model}}, Tuple{Model}, Tuple{Grid}, Tuple{NF}} where {NF, Grid, Model<:PrimitiveDry}","page":"Function and type index","title":"SpeedyWeather.zero_tendencies!","text":"zero_tendencies!(\n diagn::DiagnosticVariables{NF, Grid, Model<:PrimitiveDry}\n)\n\n\nSet the tendencies in diagn to zero.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.zero_tendencies!-Union{Tuple{DiagnosticVariables{NF, Grid, Model}}, Tuple{Model}, Tuple{Grid}, Tuple{NF}} where {NF, Grid, Model<:PrimitiveWet}","page":"Function and type index","title":"SpeedyWeather.zero_tendencies!","text":"zero_tendencies!(\n diagn::DiagnosticVariables{NF, Grid, Model<:PrimitiveWet}\n)\n\n\nSet the tendencies in diagn to zero.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.zero_tendencies!-Union{Tuple{DiagnosticVariables{NF, Grid, Model}}, Tuple{Model}, Tuple{Grid}, Tuple{NF}} where {NF, Grid, Model<:ShallowWater}","page":"Function and type index","title":"SpeedyWeather.zero_tendencies!","text":"zero_tendencies!(\n diagn::DiagnosticVariables{NF, Grid, Model<:ShallowWater}\n)\n\n\nSet the tendencies in diagn to zero.\n\n\n\n\n\n","category":"method"},{"location":"how_to_run_speedy/#How-to-run-SpeedyWeather.jl","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"Creating a SpeedyWeather.jl simulation and running it consists conceptually of 4 steps. In contrast to many other models, these steps are bottom-up rather then top-down. There is no monolithic interface to SpeedyWeather.jl, instead all options that a user may want to adjust are chosen and live in their respective model components.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"Create a SpectralGrid which defines the grid and spectral resolution.\nCreate model components and combine to a model.\nInitialize the model to create a simulation.\nRun that simulation.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"In the following we will describe these steps in more detail. If you want to start with some examples first, see Model setups which has easy and more advanced examples of how to set up SpeedyWeather.jl to run the simulation you want.","category":"page"},{"location":"how_to_run_speedy/#SpectralGrid","page":"How to run SpeedyWeather.jl","title":"SpectralGrid","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"The life of every SpeedyWeather.jl simulation starts with a SpectralGrid object. A SpectralGrid defines the physical domain of the simulation and its discretization. This domain has to be a sphere because of the spherical harmonics, but it can have a different radius. The discretization is for spectral, grid-point space and the vertical as this determines the size of many arrays for preallocation, for which als the number format is essential to know. That's why SpectralGrid is the beginning of every SpeedyWeather.jl simulation and that is why it has to be passed on to (most) model components.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"The default SpectralGrid is","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"using SpeedyWeather\nspectral_grid = SpectralGrid()","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"You can also get the help prompt by typing ?SpectralGrid. Let's explain the details: The spectral resolution is T31, so the largest wavenumber in spectral space is 31, and all the complex spherical harmonic coefficients of a given 2D field (see Spherical Harmonic Transform) are stored in a LowerTriangularMatrix in the number format Float32. The radius of the sphere is 6371km by default, which is the radius of the Earth.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"This spectral resolution is combined with an octahedral Gaussian grid of 3168 grid points. This grid has 48 latitude rings, 20 longitude points around the poles and up to 96 longitude points around the Equator. Data on that grid is also stored in Float32. The resolution is therefore on average about 400km. In the vertical 8 levels are used, using Sigma coordinates.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"The resolution of a SpeedyWeather.jl simulation is adjusted using the trunc argument, this defines the spectral resolution and the grid resolution is automatically adjusted to keep the aliasing between spectral and grid-point space constant (see Matching spectral and grid resolution).","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"spectral_grid = SpectralGrid(trunc=85)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"Typical values are 31,42,63,85,127,170,... although you can technically use any integer, see Available horizontal resolutions for details. Now with T85 (which is a common notation for trunc=85) the grid is of higher resolution too. You may play with the dealiasing factor, a larger factor increases the grid resolution that is matched with a given spectral resolution. You don't choose the resolution of the grid directly, but using the Grid argument you can change its type (see Grids)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"spectral_grid = SpectralGrid(trunc=85,dealiasing=3,Grid=HEALPixGrid)","category":"page"},{"location":"how_to_run_speedy/#Vertical-coordinates-and-resolution","page":"How to run SpeedyWeather.jl","title":"Vertical coordinates and resolution","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"The number of vertical layers or levels (we use both terms often interchangeably) is determined through the nlev argument. Especially for the BarotropicModel and the ShallowWaterModel you want to set this to","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"spectral_grid = SpectralGrid(nlev=1)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"For a single vertical level the type of the vertical coordinates does not matter, but in general you can change the spacing of the sigma coordinates which have to be discretized in 01","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"vertical_coordinates = SigmaCoordinates(0:0.2:1)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"These are regularly spaced Sigma coordinates, defined through their half levels. The cell centers or called full levels are marked with an ×.","category":"page"},{"location":"how_to_run_speedy/#create_model_components","page":"How to run SpeedyWeather.jl","title":"Creating model components","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"SpeedyWeather.jl deliberately avoids the namelists that are the main user interface to many old school models. Instead, we employ a modular approach whereby every non-default model component is created with its respective parameters to finally build the whole model from these components.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"If you know which components you want to adjust you would create them right away, however, a new user might first want to know which components there are, so let's flip the logic around and assume you know you want to run a ShallowWaterModel. You can create a default ShallowWaterModel like so and inspect its components","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"model = ShallowWaterModel()","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"So by default the ShallowWaterModel uses a Leapfrog time_stepping, which you can inspect like so","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"model.time_stepping","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"Model components often contain parameters from the SpectralGrid as they are needed to determine the size of arrays and other internal reasons. You should, in most cases, just ignore those. But the Leapfrog time stepper comes with Δt_at_T31 which is the parameter used to scale the time step automatically. This means at a spectral resolution of T31 it would use 30min steps, at T63 it would be ~half that, 15min, etc. Meaning that if you want to have a shorter or longer time step you can create a new Leapfrog time stepper. But remember that every model component depends on a SpectralGrid as first argument.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"spectral_grid = SpectralGrid(trunc=63,nlev=1)\ntime_stepping = Leapfrog(spectral_grid,Δt_at_T31=15)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"The actual time step at the given resolution (here T63) is then Δt_sec, there's also Δt which is a scaled time step used internally, because SpeedyWeather.jl scales the equations with the radius of the Earth, but this is largely hidden (except here) from the user. With this new Leapfrog time stepper constructed we can create a model by passing on the components (they are keyword arguments so either use ;time_stepping for which the naming must match, or time_stepping=my_time_stepping with any name)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"model = ShallowWaterModel(;spectral_grid,time_stepping)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"This logic continues for all model components. See the Model setups for examples. All model components are also subtype (i.e. <:) of some abstract supertype, this feature can be made use of to redefine not just constant parameters of existing model components but also to define new ones. This is more elaborated in Extending SpeedyWeather.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"A model in SpeedyWeather.jl is understood as a collection of model components that roughly belong into one of three groups. ","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"Components (numerics, dynamics, or physics) that have parameters, possibly contain some data (immutable, precomputed) and some functions associated with them. For example, a forcing term may be defined through some parameters, but also require precomputed arrays, or data to be loaded from file and a function that instructs how to calculate this forcing on every time step.\nComponents that are merely a collection of parameters that conceptually belong together. For example, Earth is an AbstractPlanet that contains all the orbital parameters important for weather and climate (rotation, gravity, etc).\nComponents for output purposes. For example, OutputWriter controls the NetCDF output, and Feedback controls the information printed to the REPL and to file.","category":"page"},{"location":"how_to_run_speedy/#create_model","page":"How to run SpeedyWeather.jl","title":"Creating a model","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"SpeedyWeather.jl implements (currently) 4 different models","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"BarotropicModel, see Barotropic vorticity model\nShallowWaterModel, see Shallow water model\nPrimitiveDryModel, see Primitive equation model but with zero humidity.\nPrimitiveWetModel, see Primitive equation model.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"Overall they are kept quite similar, but there are still fundamental differences arising from the different equations. For example, the barotropic and shallow water models do not have any physical parameterizations. Conceptually you construct these different models with","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"spectral_grid = SpectralGrid(trunc=...,...)\ncomponent1 = SomeComponent(spectral_grid,parameter1=...,...)\ncomponent2 = SomeOtherComponent(spectral_grid,parameter2=...,...)\nmodel = BarotropicModel(;spectral_grid,all_other_components...,...)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"or model = ShallowWaterModel(;spectral_grid,...), etc.","category":"page"},{"location":"how_to_run_speedy/#initialize","page":"How to run SpeedyWeather.jl","title":"Model initialization","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"In the previous section the model was created, but this is conceptually just gathering all its components together. However, many components need to be initialized. This step is used to precompute arrays, load necessary data from file or to communicate those between components. Furthermore, prognostic and diagnostic variables are allocated. It is (almost) all that needs to be done before the model can be run (exception being the output initialization). Many model components have a initialize! function associated with them that it executed here. However, from a user perspective all that needs to be done here is","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"simulation = initialize!(model)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"and we have initialized the ShallowWaterModel we have defined earlier. After this step you can continue to tweak your model setup but note that some model components are immutable, or that your changes may not be propagated to other model components that rely on it. But you can, for example, change the output time step (in hours) like so","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"simulation.model.output.output_dt = 1","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"Now, if there's output, it will be every hour. Furthermore the initial conditions can be set with the initial_conditions model component which are then set during initialize!(::ModelSetup), but you can also change them now, before the model runs","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"simulation.prognostic_variables.layers[1].timesteps[1].vor[1] = 0","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"So with this we have set the zero mode of vorticity of the first (and only) layer in the shallow water to zero. Because the leapfrogging is a 2-step time stepping scheme we set here the first. As it is often tricky to set the initial conditions in spectral space, it is generally advised to do so through the initial_conditions model component.","category":"page"},{"location":"how_to_run_speedy/#run","page":"How to run SpeedyWeather.jl","title":"Run a simulation","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"After creating a model, initializing it to a simulation, all that is left is to run the simulation.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"run!(simulation)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"By default this runs for 10 days without output. These are the options left to change, so with","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"model.output.id = \"test\" # hide\nrun!(simulation,n_days=5,output=true)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"You would continue this simulation (the previous run! call already integrated 10 days!) for another 5 days and storing default NetCDF output.","category":"page"},{"location":"speedytransforms/#SpeedyTransforms","page":"Submodule: SpeedyTransforms","title":"SpeedyTransforms","text":"","category":"section"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"SpeedyTransforms is a submodule that has been developed for SpeedyWeather.jl which is technically independent (SpeedyWeather.jl however imports it) and can also be used without running simulations. It is just not put into its own respective repository for now.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"The SpeedyTransforms are based on RingGrids and LowerTriangularMatrices to hold data in either grid-point space or in spectral space. So you want to read these sections first for clarifications how to work with these. We will also not discuss mathematical details of the Spherical Harmonic Transform here, but will focus on the usage of the SpeedyTransforms module.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"The SpeedyTransforms module also implements the gradient operators nabla nabla cdot nabla times nabla^2 nabla^-2 in spectral space. Combined with the spectral transform, you could for example start with a velocity field in grid-point space, transform to spectral, compute its divergence and transform back to obtain the divergence in grid-point space. Examples are outlined in Gradient operators.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"info: SpeedyTransforms assumes a unit sphere\nThe operators in SpeedyTransforms generally assume a sphere of radius R=1. For the transforms themselves that does not make a difference, but the gradient operators div,curl,∇,∇²,∇⁻² omit the radius scaling. You will have to do this manually. Also note that the meridional derivate expects a cos^-1(theta) scaling. More in Gradient operators.","category":"page"},{"location":"speedytransforms/#Example-transform","page":"Submodule: SpeedyTransforms","title":"Example transform","text":"","category":"section"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Lets start with a simple transform. We could be using SpeedyWeather but to be more verbose these are the modules required to load","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"using SpeedyWeather.RingGrids\nusing SpeedyWeather.LowerTriangularMatrices\nusing SpeedyWeather.SpeedyTransforms","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"As an example, we want to transform the l=m=1 spherical harmonic from spectral space in alms to grid-point space.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"alms = zeros(LowerTriangularMatrix{ComplexF64},6,6) # spectral coefficients\nalms[2,2] = 1 # only l=1,m=1 harmonic\nalms","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Now gridded is the function that takes spectral coefficients alms and converts them a grid-point space map","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"map = gridded(alms)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"By default, the gridded transforms onto a FullGaussianGrid unravelled here into a vector west to east, starting at the prime meridian, then north to south, see RingGrids. We can visualize map quickly with a UnicodePlot via plot (see Visualising RingGrid data)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"plot(map)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Yay! This is the what the l=m=1 spherical harmonic is supposed to look like! Now let's go back to spectral space with spectral","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"alms2 = spectral(map)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Comparing with alms from above you can see that the transform is exact up to a typical rounding error from Float64. ","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"alms ≈ alms2","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"YAY! The transform is typically idempotent, meaning that either space may hold information that is not exactly representable in the other but the first two-way transform will remove that so that subsequent transforms do not change this any further. However, also note here that the default FullGaussianGrid is an exact grid, inexact grids usually have a transform error that is larger than the rounding error from floating-point arithmetic.","category":"page"},{"location":"speedytransforms/#Transform-onto-another-grid","page":"Submodule: SpeedyTransforms","title":"Transform onto another grid","text":"","category":"section"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"While the default grid for SpeedyTransforms is the FullGaussianGrid we can transform onto other grids by specifying Grid too","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"map = gridded(alms,Grid=HEALPixGrid)\nplot(map)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"which, if transformed back, however, can yield a larger transform error as discussed above","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"spectral(map)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"On such a coarse grid the transform error (absolute and relative) is about 10^-2, this decreases for higher resolution. The gridded and spectral functions will choose a corresponding grid-spectral resolution (see Matching spectral and grid resolution) following quadratic truncation, but you can always truncate/interpolate in spectral space with spectral_truncation, spectral_interpolation which takes trunc = l_max = m_max as second argument","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"spectral_truncation(alms,2)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Yay, we just chopped off l 2 from alms which contained the harmonics up to degree and order 5 before. If the second argument in spectral_truncation is larger than alms then it will automatically call spectral_interpolation and vice versa. Also see Interpolation on RingGrids to interpolate directly between grids. If you want to control directly the resolution of the grid gridded is supposed to transform onto you have to provide a SpectralTransform instance. More on that now.","category":"page"},{"location":"speedytransforms/#The-SpectralTransform-struct","page":"Submodule: SpeedyTransforms","title":"The SpectralTransform struct","text":"","category":"section"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Both spectral and gridded create an instance of SpectralTransform under the hood. This object contains all precomputed information that is required for the transform, either way: The Legendre polynomials, pre-planned Fourier transforms, precomputed gradient, divergence and curl operators, the spherical harmonic eigenvalues among others. Maybe the most intuitive way to create a SpectralTransform is to start with a SpectralGrid, which already defines which spectral resolution is supposed to be combined with a given grid.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(Float32,trunc=5,Grid=OctahedralGaussianGrid,dealiasing=3)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"(We using SpeedyWeather here as SpectralGrid is exported therein). We also specify the number format Float32 here to be used for the transform although this is the default anyway. From spectral_grid we now construct a SpectralTransform as follows","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"S = SpectralTransform(spectral_grid)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Note that because we chose dealiasing=3 (cubic truncation) we now match a T5 spectral field with a 12-ring octahedral Gaussian grid, instead of the 8 rings as above. So going from dealiasing=2 (default) to dealiasing=3 increased our resolution on the grid while the spectral resolution remains the same. The SpectralTransform also has options for the recomputation or pre-computation of the Legendre polynomials, fore more information see (P)recompute Legendre polynomials.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Passing on S the SpectralTransform now allows us to transform directly on the grid defined therein.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"map = gridded(alms,S)\nplot(map)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Yay, this is again the l=m=1 harmonic, but this time on a slightly higher resolution OctahedralGaussianGrid as specified in the SpectralTransform S. Note that also the number format was converted on the fly to Float32 because that is the number format we specified in S! And from grid to spectral","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"alms2 = spectral(map,S)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"As you can see the rounding error is now more like 10^-8 as we are using Float32 (the OctahedralGaussianGrid is another exact grid). Note, however, that the returned LowerTriangularMatrix is of size 7x6, not 6x6 what we started from. The underlying reason is that internally SpeedyWeather uses LowerTriangularMatrixs of size l_max + 2 times m_max + 1. One +1 on both degree and order for 0-based harmonics versus 1-based matrix sizes, but an additional +1 for the degrees which is required by the meridional derivative. For consistency, all LowerTriangularMatrixs in SpeedyWeather.jl carry this additional degree but only the vector quantities explicitly make use of it. See Meridional derivative for details.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"For this interface to SpeedyTransforms this means that on a grid-to-spectral transform you will get one more degree than orders of the spherical harmonics by default. You can, however, always truncate this additional degree, say to T5 (hence matrix size is 6x6)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"spectral_truncation(alms2,5,5)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"spectral_truncation(alms2,5) would have returned the same, a single argument is then assumed equal for both degrees and orders. Alternatively, you can also pass on the one_more_degree=false argument to the SpectralTransform constructor","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"S = SpectralTransform(spectral_grid,one_more_degree=false)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"As you can see the 7x6 LowerTriangularMatrix in the description above dropped down to 6x6 LowerTriangularMatrix, this is the size of the input that is expected (otherwise you will get a BoundsError).","category":"page"},{"location":"speedytransforms/#Power-spectrum","page":"Submodule: SpeedyTransforms","title":"Power spectrum","text":"","category":"section"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"How to take some data and compute a power spectrum with SpeedyTransforms you may ask. Say you have some global data in a matrix m that looks, for example, like","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"alms = randn(LowerTriangularMatrix{Complex{Float32}},32,32) # hide\nspectral_truncation!(alms,10) # hide\nmap = gridded(alms,Grid=FullClenshawGrid) # hide\nm = Matrix(map) # hide\nm","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"You hopefully know which grid this data comes on, let us assume it is a regular latitude-longitude grid, which we call the FullClenshawGrid. Note that for the spectral transform this should not include the poles, so the 96x47 matrix size here corresponds to ","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"We now wrap this matrix therefore to associate it with the necessary grid information","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"map = FullClenshawGrid(m)\nplot(map)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Now we transform into spectral space and call power_spectrum(::LowerTriangularMatrix)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"alms = spectral(map)\npower = SpeedyTransforms.power_spectrum(alms)\nnothing # hide","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Which returns a vector of power at every wavenumber. By default this is normalized as average power per degree, you can change that with the keyword argument normalize=false. Plotting this yields","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"using UnicodePlots\nk = 0:length(power)-1\nlineplot(k,power,yscale=:log10,ylim=(1e-15,10),xlim=extrema(k),\n ylabel=\"power\",xlabel=\"wavenumber\",height=10,width=60)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"The power spectrum of our data is about 1 up to wavenumber 10 and then close to zero for higher wavenumbers (which is in fact how we constructed this fake data). Let us turn this around and use SpeedyTransforms to create random noise in spectral space to be used in grid-point space!","category":"page"},{"location":"speedytransforms/#Example:-Creating-kn-distributed-noise","page":"Submodule: SpeedyTransforms","title":"Example: Creating k^n-distributed noise","text":"","category":"section"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"How would we construct random noise in spectral space that follows a certain power law and transform it back into grid-point space? Define the wavenumber k for T31, the spectral resolution we are interested in. (We start from 1 instead of 0 to avoid zero to the power of something negative). Now create some normally distributed spectral coefficients but scale them down for higher wavenumbers with k^-2","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"k = 1:32\nalms = randn(LowerTriangularMatrix{Complex{Float32}},32,32)\nalms .*= k.^-2","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Awesome. For higher degrees and orders the amplitude clearly decreases! Now to grid-point space and let us visualize the result","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"map = gridded(alms)\nplot(map)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"You can always access the underlying data in map via map.data in case you need to get rid of the wrapping into a grid again!","category":"page"},{"location":"speedytransforms/#(P)recompute-Legendre-polynomials","page":"Submodule: SpeedyTransforms","title":"(P)recompute Legendre polynomials","text":"","category":"section"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"The spectral transform uses a Legendre transform in meridional direction. For this the Legendre polynomials are required, at each latitude ring this is a l_max times m_max lower triangular matrix. Storing precomputed Legendre polynomials therefore quickly increase in size with resolution. One can recompute them to save memory, but that uses more arithmetic operations. There is therefore a memory-compute tradeoff.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"For a single transform, there is no need to precompute the polynomials as the SpectralTransform object will be garbage collected again anyway. For low resolution simulations with many repeated small transforms it makes sense to precompute the polynomials and SpeedyWeather.jl does that automatically anyway. At very high resolution the polynomials may, however, become prohibitively large. An example at T127, about 100km resolution","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"spectral_grid = SpectralGrid(trunc=127)\nSpectralTransform(spectral_grid,recompute_legendre=false)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"the polynomials are about 3MB in size. Easy that is not much. But at T1023 on the O1536 octahedral Gaussian grid, this is already 1.5GB, cubically increasing with the spectral truncation T. recompute_legendre=true (default false when constructing a SpectralTransform object which may be reused) would lower this to kilobytes","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"SpectralTransform(spectral_grid,recompute_legendre=true)","category":"page"},{"location":"speedytransforms/#Gradient-operators","page":"Submodule: SpeedyTransforms","title":"Gradient operators","text":"","category":"section"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"SpeedyTransforms also includes many gradient operators to take derivatives in spherical harmonics. These are in particular nabla nabla cdot nabla times nabla^2 nabla^-2. However, the actually implemented operators are, in contrast to the mathematical Derivatives in spherical coordinates due to reasons of scaling as follows. Let the implemented operators be hatnabla etc.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"hatnabla A = left(fracpartial Apartial lambda cos(theta)fracpartial Apartial theta right) =\nRcos(theta)nabla A","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"So the zonal derivative omits the radius and the cos^-1(theta) scaling. The meridional derivative adds a cos(theta) due to a recursion relation being defined that way, which, however, is actually convenient because the whole operator is therefore scaled by Rcos(theta). The curl and divergence operators expect the input velocity fields to be scaled by cos^-1(theta), i.e.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"beginaligned\nhatnabla cdot (cos^-1(theta)mathbfu) = fracpartial upartial lambda +\ncosthetafracpartial vpartial theta = Rnabla cdot mathbfu \nhatnabla times (cos^-1(theta)mathbfu) = fracpartial vpartial lambda -\ncosthetafracpartial upartial theta = Rnabla times mathbfu\nendaligned","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"And the Laplace operators omit a R^2 (radius R) scaling, i.e.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"hatnabla^-2A = frac1R^2nabla^-2A quad hatnabla^2A = R^2nabla^2A","category":"page"},{"location":"speedytransforms/#Example:-Geostrophy","page":"Submodule: SpeedyTransforms","title":"Example: Geostrophy","text":"","category":"section"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Now, we want to use the following example to illustrate their use: We have uv and want to calculate eta in the shallow water system from it following geostrophy. Analytically we have","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"-fv = -gpartial_lambda eta quad fu = -gpartial_theta eta","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"which becomes, if you take the divergence of these two equations","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"zeta = fracgfnabla^2 eta","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Meaning that if we start with uv we can obtain the relative vorticity zeta and, using Coriolis parameter f and gravity g, invert the Laplace operator to obtain displacement eta. How to do this with SpeedyTransforms? ","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Let us start by generating some data","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"using SpeedyWeather\n\nspectral_grid = SpectralGrid(trunc=31,nlev=1)\nforcing = SpeedyWeather.JetStreamForcing(spectral_grid)\ndrag = QuadraticDrag(spectral_grid)\nmodel = ShallowWaterModel(;spectral_grid,forcing,drag)\nmodel.feedback.verbose = false # hide\nsimulation = initialize!(model);\nrun!(simulation,n_days=30)\nnothing # hide","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Now pretend you only have u,v to get vorticity (which is actually the prognostic variable in the model, so calculated anyway...).","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"u = simulation.diagnostic_variables.layers[1].grid_variables.u_grid\nv = simulation.diagnostic_variables.layers[1].grid_variables.v_grid\nvor = SpeedyTransforms.curl(u,v) / spectral_grid.radius\nnothing # hide","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Here, u,v are the grid-point velocity fields, and the function curl takes in either LowerTriangularMatrixs (no transform needed as all gradient operators act in spectral space), or, as shown here, arrays of the same grid and size. In this case, the function actually runs through the following steps","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"RingGrids.scale_coslat⁻¹!(u_grid)\nRingGrids.scale_coslat⁻¹!(v_grid)\n\nS = SpectralTransform(u_grid,one_more_degree=true)\nus = spectral(u_grid,S)\nvs = spectral(v_grid,S)\n\nreturn curl(us,vs)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"(Copies of) the velocity fields are unscaled by the cosine of latitude (see above), then transformed into spectral space, and the returned vor requires a manual division by the radius. We always unscale vector fields by the cosine of latitude before any curl, or div operation, as these omit those.","category":"page"},{"location":"speedytransforms/#One-more-degree-for-spectral-fields","page":"Submodule: SpeedyTransforms","title":"One more degree for spectral fields","text":"","category":"section"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"The SpectralTransform in general takes a one_more_degree keyword argument, if otherwise the returned LowerTriangularMatrix would be of size 32x32, setting this to true would return 33x32. The reason is that while most people would expect square lower triangular matrices for a triangular spectral truncation, all vector quantities always need one more degree (= one more row) because of a recursion relation in the meridional gradient. So as we want to take the curl of us,vs here, they need this additional degree, but in the returned lower triangular matrix this row is set to zero.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"info: One more degree for vector quantities\nAll gradient operators expect the input lower triangular matrices of shape N+1 times N. This one more degree of the spherical harmonics is required for the meridional derivative. Scalar quantities contain this degree too for size compatibility but they should not make use of it. Use spectral_truncation to add or remove this degree manually.","category":"page"},{"location":"speedytransforms/#Example:-Geostrophy-(continued)","page":"Submodule: SpeedyTransforms","title":"Example: Geostrophy (continued)","text":"","category":"section"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Now we transfer vor into grid-point space, but specify that we want it on the grid that we also used in spectral_grid. The Coriolis parameter for a grid like vor_grid is obtained, and we do the following for fzetag.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"vor_grid = gridded(vor,Grid=spectral_grid.Grid)\nf = SpeedyWeather.coriolis(vor_grid)\nfζ_g = spectral_grid.Grid(vor_grid .* f ./ model.planet.gravity)\nnothing # hide","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"The last line is a bit awkward for now, as the element-wise multiplication between two grids escapes the grid and returns a vector that we wrap again into a grid. We will fix that in future releases. Now we need to apply the inverse Laplace operator to fzetag which we do as follows","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"fζ_g_spectral = spectral(fζ_g,one_more_degree=true);\nη = SpeedyTransforms.∇⁻²(fζ_g_spectral) * spectral_grid.radius^2\nη_grid = gridded(η,Grid=spectral_grid.Grid)\nnothing # hide","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Note the manual scaling with the radius R^2 here. We now compare the results","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"plot(η_grid)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Which is the interface displacement assuming geostrophy. The actual interface displacement contains also ageostrophy","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"plot(simulation.diagnostic_variables.surface.pres_grid)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Strikingly similar! The remaining differences are the ageostrophic motions but also note that the mean is off. This is because geostrophy only use/defines the gradient of eta not the absolute values itself. Our geostrophic eta_g has by construction a mean of zero (that is how we define the inverse Laplace operator) but the actual eta is some 1400m higher.","category":"page"},{"location":"speedytransforms/#Functions-and-type-index","page":"Submodule: SpeedyTransforms","title":"Functions and type index","text":"","category":"section"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Modules = [SpeedyWeather.SpeedyTransforms]","category":"page"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.SpectralTransform","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.SpectralTransform","text":"S = SpectralTransform{NF<:AbstractFloat}(...)\n\nSpectralTransform struct that contains all parameters and preallocated arrays for the spectral transform.\n\n\n\n\n\n","category":"type"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.SpectralTransform-Union{Tuple{AbstractArray{Complex{NF}, 2}}, Tuple{NF}} where NF","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.SpectralTransform","text":"S = SpectralTransform( alms::AbstractMatrix{Complex{NF}};\n recompute_legendre::Bool=true,\n Grid::Type{<:AbstractGrid}=DEFAULT_GRID)\n\nGenerator function for a SpectralTransform struct based on the size of the spectral coefficients alms and the grid Grid. Recomputes the Legendre polynomials by default.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.SpectralTransform-Union{Tuple{NF}, Tuple{Type{NF}, Type{<:SpeedyWeather.RingGrids.AbstractGrid}, Int64, Int64}} where NF","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.SpectralTransform","text":"SpectralTransform(\n ::Type{NF},\n Grid::Type{<:SpeedyWeather.RingGrids.AbstractGrid},\n lmax::Int64,\n mmax::Int64;\n recompute_legendre,\n legendre_shortcut,\n dealiasing\n) -> SpectralTransform\n\n\nGenerator function for a SpectralTransform struct. With NF the number format, Grid the grid type <:AbstractGrid and spectral truncation lmax,mmax this function sets up necessary constants for the spetral transform. Also plans the Fourier transforms, retrieves the colatitudes, and preallocates the Legendre polynomials (if recompute_legendre == false) and quadrature weights.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.SpectralTransform-Union{Tuple{SpeedyWeather.RingGrids.AbstractGrid{NF}}, Tuple{NF}} where NF","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.SpectralTransform","text":"S = SpectralTransform( map::AbstractGrid;\n recompute_legendre::Bool=true)\n\nGenerator function for a SpectralTransform struct based on the size and grid type of gridded field map. Recomputes the Legendre polynomials by default.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.RingGrids.get_nlat_half","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.RingGrids.get_nlat_half","text":"get_nlat_half(trunc::Integer) -> Any\nget_nlat_half(trunc::Integer, dealiasing::Real) -> Any\n\n\nFor the spectral truncation trunc (e.g. 31 for T31) return the grid resolution parameter nlat_half (number of latitude rings on one hemisphere including the Equator) following a dealiasing parameter (default 2) to match spectral and grid resolution.\n\n\n\n\n\n","category":"function"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.UV_from_vor!-Union{Tuple{NF}, Tuple{LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, SpectralTransform{NF}}} where NF<:AbstractFloat","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.UV_from_vor!","text":"UV_from_vor!(\n U::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n V::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n vor::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n S::SpectralTransform{NF<:AbstractFloat}\n)\n\n\nGet U,V (=(u,v)*coslat) from vorticity ζ spectral space (divergence D=0) Two operations are combined into a single linear operation. First, invert the spherical Laplace ∇² operator to get stream function from vorticity. Then compute zonal and meridional gradients to get U,V.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.UV_from_vordiv!-Union{Tuple{NF}, Tuple{LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, SpectralTransform{NF}}} where NF<:AbstractFloat","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.UV_from_vordiv!","text":"UV_from_vordiv!(\n U::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n V::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n vor::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n div::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n S::SpectralTransform{NF<:AbstractFloat}\n) -> Any\n\n\nGet U,V (=(u,v)*coslat) from vorticity ζ and divergence D in spectral space. Two operations are combined into a single linear operation. First, invert the spherical Laplace ∇² operator to get stream function from vorticity and velocity potential from divergence. Then compute zonal and meridional gradients to get U,V.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms._divergence!-Union{Tuple{NF}, Tuple{Any, LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, SpectralTransform{NF}}} where NF<:AbstractFloat","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms._divergence!","text":"_divergence!(\n kernel,\n div::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n u::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n v::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n S::SpectralTransform{NF<:AbstractFloat}\n)\n\n\nGeneric divergence function of vector u,v that writes into the output into div. Generic as it uses the kernel kernel such that curl, div, add or flipsign options are provided through kernel, but otherwise a single function is used.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.curl!-Tuple{LowerTriangularMatrix, LowerTriangularMatrix, LowerTriangularMatrix, SpectralTransform}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.curl!","text":"curl!(\n curl::LowerTriangularMatrix,\n u::LowerTriangularMatrix,\n v::LowerTriangularMatrix,\n S::SpectralTransform;\n flipsign,\n add\n)\n\n\nCurl of a vector u,v written into curl, curl = ∇×(u,v). u,v are expected to have a 1/coslat-scaling included, then curl is not scaled. flipsign option calculates -∇×(u,v) instead. add option calculates curl += ∇×(u,v) instead. flipsign and add can be combined. This functions only creates the kernel and calls the generic divergence function _divergence! subsequently with flipped u,v -> v,u for the curl.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.curl-Tuple{LowerTriangularMatrix, LowerTriangularMatrix}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.curl","text":"curl(\n u::LowerTriangularMatrix,\n v::LowerTriangularMatrix\n) -> Any\n\n\nCurl (∇×) of two vector components u,v of size (n+1)xn, the last row will be set to zero in the returned LowerTriangularMatrix. This function requires both u,v to be transforms of fields that are scaled with 1/cos(lat). An example usage is therefore\n\nRingGrids.scale_coslat⁻¹!(u_grid)\nRingGrids.scale_coslat⁻¹!(v_grid)\nu = spectral(u_grid)\nv = spectral(v_grid)\nvor = curl(u,v)\nvor_grid = gridded(div)\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.curl-Union{Tuple{Grid}, Tuple{Grid, Grid}} where Grid<:SpeedyWeather.RingGrids.AbstractGrid","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.curl","text":"curl(\n u::SpeedyWeather.RingGrids.AbstractGrid,\n v::SpeedyWeather.RingGrids.AbstractGrid\n) -> Any\n\n\nCurl (∇×) of two vector components u,v on a grid. Applies 1/coslat scaling, transforms to spectral space and returns the spectral curl, which is scaled with the radius of the sphere. Divide by radius for unscaling.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.divergence!-Tuple{LowerTriangularMatrix, LowerTriangularMatrix, LowerTriangularMatrix, SpectralTransform}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.divergence!","text":"divergence!(\n div::LowerTriangularMatrix,\n u::LowerTriangularMatrix,\n v::LowerTriangularMatrix,\n S::SpectralTransform;\n flipsign,\n add\n)\n\n\nDivergence of a vector u,v written into div, div = ∇⋅(u,v). u,v are expected to have a 1/coslat-scaling included, then div is not scaled. flipsign option calculates -∇⋅(u,v) instead. add option calculates div += ∇⋅(u,v) instead. flipsign and add can be combined. This functions only creates the kernel and calls the generic divergence function _divergence! subsequently.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.divergence-Tuple{LowerTriangularMatrix, LowerTriangularMatrix}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.divergence","text":"divergence(\n u::LowerTriangularMatrix,\n v::LowerTriangularMatrix\n) -> Any\n\n\nDivergence (∇⋅) of two vector components u,v which need to have size (n+1)xn, the last row will be set to zero in the returned LowerTriangularMatrix. This function requires both u,v to be transforms of fields that are scaled with 1/cos(lat). An example usage is therefore\n\nRingGrids.scale_coslat⁻¹!(u_grid)\nRingGrids.scale_coslat⁻¹!(v_grid)\nu = spectral(u_grid,one_more_degree=true)\nv = spectral(v_grid,one_more_degree=true)\ndiv = divergence(u,v)\ndiv_grid = gridded(div)\n\nBoth div and div_grid are scaled with the radius.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.divergence-Union{Tuple{Grid}, Tuple{Grid, Grid}} where Grid<:SpeedyWeather.RingGrids.AbstractGrid","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.divergence","text":"divergence(\n u::SpeedyWeather.RingGrids.AbstractGrid,\n v::SpeedyWeather.RingGrids.AbstractGrid\n) -> Any\n\n\nDivergence (∇⋅) of two vector components u,v on a grid. Applies 1/coslat scaling, transforms to spectral space and returns the spectral divergence, which is scaled with the radius of the sphere. Divide by radius for unscaling.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.get_recursion_factors-Union{Tuple{NF}, Tuple{Type{NF}, Int64, Int64}} where NF<:AbstractFloat","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.get_recursion_factors","text":"get_recursion_factors( ::Type{NF}, # number format NF\n lmax::Int, # max degree l of spherical harmonics (0-based here)\n mmax::Int # max order m of spherical harmonics\n ) where {NF<:AbstractFloat}\n\nReturns a matrix of recursion factors ϵ up to degree lmax and order mmax of number format NF.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.get_truncation","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.get_truncation","text":"get_truncation(nlat_half::Integer) -> Any\nget_truncation(nlat_half::Integer, dealiasing::Real) -> Any\n\n\nFor the grid resolution parameter nlat_half (e.g. 24 for a 48-ring FullGaussianGrid) return the spectral truncation trunc (max degree of spherical harmonics) following a dealiasing parameter (default 2) to match spectral and grid resolution.\n\n\n\n\n\n","category":"function"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.gridded!-Union{Tuple{NF}, Tuple{SpeedyWeather.RingGrids.AbstractGrid{NF}, LowerTriangularMatrix{Complex{NF}}, SpectralTransform{NF}}} where NF<:AbstractFloat","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.gridded!","text":"gridded!( map::AbstractGrid,\n alms::LowerTriangularMatrix,\n S::SpectralTransform)\n\nSpectral transform (spectral to grid) of the spherical harmonic coefficients alms to a gridded field map. The spectral transform is number format-flexible as long as the parametric types of map, alms, S are identical. The spectral transform is grid-flexible as long as the typeof(map)<:AbstractGrid. Uses the precalculated arrays, FFT plans and other constants in the SpectralTransform struct S.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.gridded-Union{Tuple{AbstractMatrix{T}}, Tuple{T}, Tuple{NF}} where {NF, T<:Complex{NF}}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.gridded","text":"gridded(\n alms::AbstractArray{T<:Complex{NF}, 2};\n recompute_legendre,\n Grid,\n kwargs...\n) -> Any\n\n\nSpectral transform (spectral to grid space) from spherical coefficients alms to a newly allocated gridded field map. Based on the size of alms the grid type grid, the spatial resolution is retrieved based on the truncation defined for grid. SpectralTransform struct S is allocated to execute gridded(alms,S).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.gridded-Union{Tuple{NF}, Tuple{AbstractMatrix, SpectralTransform{NF}}} where NF","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.gridded","text":"gridded(\n alms::AbstractMatrix,\n S::SpectralTransform{NF};\n kwargs...\n) -> Any\n\n\nSpectral transform (spectral to grid space) from spherical coefficients alms to a newly allocated gridded field map with precalculated properties based on the SpectralTransform struct S. alms is converted to a LowerTriangularMatrix to execute the in-place gridded!.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.is_power_2-Tuple{Integer}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.is_power_2","text":"true/false = is_power_2(i::Integer)\n\nChecks whether an integer i is a power of 2, i.e. i = 2^k, with k = 0,1,2,3,....\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.roundup_fft-Union{Tuple{Integer}, Tuple{T}} where T<:Integer","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.roundup_fft","text":"m = roundup_fft(n::Int;\n small_primes::Vector{Int}=[2,3,5])\n\nReturns an integer m >= n with only small prime factors 2, 3 (default, others can be specified with the keyword argument small_primes) to obtain an efficiently fourier-transformable number of longitudes, m = 2^i * 3^j * 5^k >= n, with i,j,k >=0.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral!-Union{Tuple{NF}, Tuple{LowerTriangularMatrix{Complex{NF}}, SpeedyWeather.RingGrids.AbstractGrid{NF}, SpectralTransform{NF}}} where NF<:AbstractFloat","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.spectral!","text":"spectral!( alms::LowerTriangularMatrix,\n map::AbstractGrid,\n S::SpectralTransform)\n\nSpectral transform (grid to spectral space) from the gridded field map on a grid<:AbstractGrid to a LowerTriangularMatrix of spherical harmonic coefficients alms. Uses FFT in the zonal direction, and a Legendre Transform in the meridional direction exploiting symmetries. The spectral transform is number format-flexible as long as the parametric types of map, alms, S are identical. The spectral transform is grid-flexible as long as the typeof(map)<:AbstractGrid. Uses the precalculated arrays, FFT plans and other constants in the SpectralTransform struct S.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral-Tuple{AbstractMatrix}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.spectral","text":"spectral(\n map::AbstractMatrix;\n Grid,\n kwargs...\n) -> LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat\n\n\nConverts map to grid(map) to execute spectral(map::AbstractGrid;kwargs...).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral-Union{Tuple{NF}, Tuple{SpeedyWeather.RingGrids.AbstractGrid, SpectralTransform{NF}}} where NF","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.spectral","text":"spectral(\n map::SpeedyWeather.RingGrids.AbstractGrid,\n S::SpectralTransform{NF}\n) -> LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat\n\n\nSpectral transform (grid to spectral) map to grid(map) to execute spectral(map::AbstractGrid;kwargs...).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral-Union{Tuple{SpeedyWeather.RingGrids.AbstractGrid{NF}}, Tuple{NF}} where NF","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.spectral","text":"spectral(\n map::SpeedyWeather.RingGrids.AbstractGrid{NF};\n recompute_legendre,\n one_more_degree\n) -> LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat\n\n\nConverts map to Grid(map) to execute spectral(map::AbstractGrid;kwargs...).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_interpolation-Union{Tuple{NF}, Tuple{Type{NF}, LowerTriangularMatrix, Integer, Integer}} where NF","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.spectral_interpolation","text":"alms_interp = spectral_interpolation( ::Type{NF},\n alms::LowerTriangularMatrix,\n ltrunc::Integer,\n mtrunc::Integer\n ) where NF\n\nReturns a spectral coefficient matrix alms_interp that is alms padded with zeros to interpolate in spectral space. If trunc is smaller or equal to the implicit truncation in alms obtained from its size than spectral_truncation is automatically called instead, returning alms_trunc, a coefficient matrix that is smaller than alms, implicitly setting higher degrees and orders to zero.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_smoothing!-Tuple{LowerTriangularMatrix, Real}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.spectral_smoothing!","text":"spectral_smoothing!(A::LowerTriangularMatrix,c;power=1)\n\nSmooth the spectral field A following A = (1-(1-c)∇²ⁿ) with power n of a normalised Laplacian so that the highest degree lmax is dampened by multiplication with c. Anti-diffusion for c>1.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_smoothing-Tuple{LowerTriangularMatrix, Real}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.spectral_smoothing","text":"A_smooth = spectral_smoothing(A::LowerTriangularMatrix,c;power=1)\n\nSmooth the spectral field A following A_smooth = (1-c*∇²ⁿ)A with power n of a normalised Laplacian so that the highest degree lmax is dampened by multiplication with c. Anti-diffusion for c<0.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_truncation!-Tuple{AbstractMatrix, Int64}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.spectral_truncation!","text":"spectral_truncation!(alms,trunc)\n\nTruncate spectral coefficients alms in-place by setting (a) the upper right triangle to zero and (b) all coefficients for which the degree l is larger than the truncation trunc.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_truncation!-Tuple{AbstractMatrix}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.spectral_truncation!","text":"spectral_truncation!(alms)\n\nTruncate spectral coefficients alms in-place by setting the upper right triangle to zero. This is to enforce that all coefficients for which the degree l is larger than order m are zero.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_truncation!-Union{Tuple{NF}, Tuple{AbstractMatrix{NF}, Integer, Integer}} where NF","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.spectral_truncation!","text":"spectral_truncation!(alms::AbstractMatrix,ltrunc::Integer,mtrunc::Integer)\n\nTruncate spectral coefficients alms in-place by setting (a) the upper right triangle to zero and (b) all coefficients for which the degree l is larger than the truncation ltrunc or order m larger than the truncaction mtrunc.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_truncation!-Union{Tuple{NF}, Tuple{LowerTriangularMatrix{NF}, Integer, Integer}} where NF","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.spectral_truncation!","text":"spectral_truncation!(alms::LowerTriangularMatrix,ltrunc::Integer,mtrunc::Integer)\n\nTruncate spectral coefficients alms in-place by setting all coefficients for which the degree l is larger than the truncation ltrunc or order m larger than the truncaction mtrunc. Similar to spectral_truncation!(::AbstractMatrix, ...) but skips the upper triangle which is zero by design for LowerTriangularMatrix.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_truncation-Union{Tuple{NF}, Tuple{Type{NF}, LowerTriangularMatrix, Integer, Integer}} where NF","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.spectral_truncation","text":"alms_trunc = spectral_truncation(alms,trunc)\n\nReturns a spectral coefficient matrix alms_trunc that is truncated from alms to the size (trunc+1)². alms_trunc only contains those coefficient of alms for which m,l ≤ trunc, and l ≥ m are zero anyway. If trunc is larger than the implicit truncation in alms obtained from its size than spectral_interpolation is automatically called instead, returning alms_interp, a coefficient matrix that is larger than alms with padded zero coefficients.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.ϵlm-Tuple{Int64, Int64}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.ϵlm","text":"ϵ = ϵ(l,m)\n\nRecursion factors ϵ as a function of degree l and order m (0-based) of the spherical harmonics. ϵ(l,m) = sqrt((l^2-m^2)/(4*l^2-1)) with default number format Float64.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.ϵlm-Union{Tuple{NF}, Tuple{Type{NF}, Int64, Int64}} where NF","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.ϵlm","text":"ϵ = ϵ(NF,l,m)\n\nRecursion factors ϵ as a function of degree l and order m (0-based) of the spherical harmonics. ϵ(l,m) = sqrt((l^2-m^2)/(4*l^2-1)) and then converted to number format NF.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇!-Union{Tuple{NF}, Tuple{LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, SpectralTransform{NF}}} where NF<:AbstractFloat","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.∇!","text":"∇!(\n dpdx::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n dpdy::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n p::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n S::SpectralTransform{NF<:AbstractFloat}\n) -> Tuple{LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat, LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat}\n\n\nApplies the gradient operator ∇ applied to input p and stores the result in dpdx (zonal derivative) and dpdy (meridional derivative). The gradient operator acts on the unit sphere and therefore omits the radius scaling\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇²!-Union{Tuple{NF}, Tuple{LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, SpectralTransform{NF}}} where NF<:AbstractFloat","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.∇²!","text":"∇²!( ∇²alms::LowerTriangularMatrix,\n alms::LowerTriangularMatrix,\n S::SpectralTransform;\n add::Bool=false,\n flipsign::Bool=false,\n inverse::Bool=false)\n\nLaplace operator ∇² applied to the spectral coefficients alms in spherical coordinates. The radius R is omitted in the eigenvalues which are precomputed in S. ∇²! is the in-place version which directly stores the output in the first argument ∇²alms.\n\nKeyword arguments\n\nadd=true adds the ∇²(alms) to the output\nflipsign=true computes -∇²(alms) instead\ninverse=true computes ∇⁻²(alms) instead\n\nDefault is add=false, flipsign=false, inverse=false. These options can be combined.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇²-Tuple{LowerTriangularMatrix, SpectralTransform}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.∇²","text":"∇²(alms::LowerTriangularMatrix, S::SpectralTransform) -> Any\n\n\nLaplace operator ∇² applied to input alms, using precomputed eigenvalues from S. The Laplace operator acts on the unit sphere and therefore omits the 1/radius^2 scaling\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇²-Tuple{LowerTriangularMatrix}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.∇²","text":"∇²(alms::LowerTriangularMatrix) -> Any\n\n\nReturns the Laplace operator ∇² applied to input alms. The Laplace operator acts on the unit sphere and therefore omits the 1/radius^2 scaling\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇⁻²!-Union{Tuple{NF}, Tuple{LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, SpectralTransform{NF}}} where NF<:AbstractFloat","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.∇⁻²!","text":"∇⁻²!( ∇⁻²alms::LowerTriangularMatrix,\n alms::LowerTriangularMatrix,\n S::SpectralTransform;\n add::Bool=false,\n flipsign::Bool=false)\n\nCalls ∇²!(∇⁻²alms, alms, S; add, flipsign, inverse=true).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇⁻²-Tuple{LowerTriangularMatrix, SpectralTransform}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.∇⁻²","text":"∇⁻²(\n ∇²alms::LowerTriangularMatrix,\n S::SpectralTransform\n) -> Any\n\n\nInverseLaplace operator ∇⁻² applied to input alms, using precomputed eigenvalues from S. The Laplace operator acts on the unit sphere and therefore omits the radius^2 scaling\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇⁻²-Tuple{LowerTriangularMatrix}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.∇⁻²","text":"∇⁻²(∇²alms::LowerTriangularMatrix) -> Any\n\n\nReturns the inverse Laplace operator ∇⁻² applied to input alms. The Laplace operator acts on the unit sphere and therefore omits the radius^2 scaling\n\n\n\n\n\n","category":"method"},{"location":"grids/#Grids","page":"Grids","title":"Grids","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"The spectral transform (the Spherical Harmonic Transform) in SpeedyWeather.jl supports any ring-based equi-longitude grid. Several grids are already implemented but other can be added. The following pages will describe an overview of these grids and but let's start but how they can be used","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(Grid = FullGaussianGrid)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"The life of every SpeedyWeather.jl simulation starts with a SpectralGrid object which defines the resolution in spectral and in grid-point space. The generator SpectralGrid() can take as a keyword argument Grid which can be any of the grids described below. The resolution of the grid, however, is not directly chosen, but determined from the spectral resolution trunc and the dealiasing factor. More in SpectralGrid and Matching spectral and grid resolution.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"info: RingGrids is a module too!\nWhile RingGrids is the underlying module that SpeedyWeather.jl uses for data structs on the sphere, the module can also be used independently of SpeedyWeather, for example to interpolate between data on different grids. See RingGrids","category":"page"},{"location":"grids/#Ring-based-equi-longitude-grids","page":"Grids","title":"Ring-based equi-longitude grids","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"SpeedyWeather.jl's spectral transform supports all ring-based equi-longitude grids. These grids have their grid points located on rings with constant latitude and on these rings the points are equi-spaced in longitude. There is technically no constrain on the spacing of the latitude rings, but the Legendre transform requires a quadrature to map those to spectral space and back. Common choices for latitudes are the Gaussian latitudes which use the Gaussian quadrature, or equi-angle latitudes (i.e. just regular latitudes but excluding the poles) that use the Clenshaw-Curtis quadrature. The longitudes have to be equi-spaced on every ring, which is necessary for the fast Fourier transform, as one would otherwise need to use a non-uniform Fourier transform. In SpeedyWeather.jl the first grid point on any ring can have a longitudinal offset though, for example by spacing 4 points around the globe at 45˚E, 135˚E, 225˚E, and 315˚E. In this case the offset is 45˚E as the first point is not at 0˚E.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"info: Is the FullClenshawGrid a longitude-latitude grid?\nShort answer: Yes. The FullClenshawGrid is a specific longitude-latitude grid with equi-angle spacing. The most common grids for geoscientific data use regular spacings for 0-360˚E in longitude and 90˚N-90˚S. The FullClenshawGrid does that too, but it does not have a point on the North or South pole, and the central latitude ring sits exactly on the Equator. We name it Clenshaw following the Clenshaw-Curtis quadrature that is used in the Legendre transfrom in the same way as Gaussian refers to the Gaussian quadrature.","category":"page"},{"location":"grids/#Implemented-grids","page":"Grids","title":"Implemented grids","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"All grids in SpeedyWeather.jl are a subtype of AbstractGrid, i.e. <: AbstractGrid. We further distinguish between full, and reduced grids. Full grids have the same number of longitude points on every latitude ring (i.e. points converge towards the poles) and reduced grids reduce the number of points towards the poles to have them more evenly spread out across the globe. More evenly does not necessarily mean that a grid is equal-area, meaning that every grid cell covers exactly the same area (although the shape changes).","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"Currently the following full grids <: AbstractFullGrid are implemented","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"FullGaussianGrid, a full grid with Gaussian latitudes\nFullClenshawGrid, a full grid with equi-angle latitudes","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"and additionally we have FullHEALPixGrid and FullOctaHEALPixGrid which are the full grid equivalents to the HEALPix grid and the OctaHEALPix grid discussed below. Full grid equivalent means that they have the same latitude rings, but no reduction in the number of points per ring towards the poles and no longitude offset. Other implemented reduced grids are","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"OctahedralGaussianGrid, a reduced grid with Gaussian latitudes based on an octahedron\nOctahedralClenshawGrid, similar but based on equi-angle latitudes\nHEALPixGrid, an equal-area grid based on a dodecahedron with 12 faces\nOctaHEALPixGrid, an equal-area grid from the class of HEALPix grids but based on an octahedron.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"An overview of these grids is visualised here, and a more detailed description follows below.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"(Image: Overview of implemented grids in SpeedyWeather.jl)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"Visualised are each grid's grid points (white dots) and grid faces (white lines). All grids shown have 16 latitude rings on one hemisphere, Equator included. The total number of grid points is denoted in the top left of every subplot. The sphere is shaded with grey, orange and turquoise regions to denote the hemispheres in a and b, the 8 octahedral faces c, d,f and the 12 dodecahedral faces (or base pixels) in e. Coastlines are added for orientation.","category":"page"},{"location":"grids/#Grid-resolution","page":"Grids","title":"Grid resolution","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"All grids use the same resolution parameter nlat_half, i.e. the number of rings on one hemisphere, Equator included. The Gaussian grids (full and reduced) do not have a ring on the equator, so their total number of rings nlat is always even and twice nlat_half. Clenshaw-Curtis grids and the HEALPix grids have a ring on the equator such their total number of rings is always odd and one less than the Gaussian grids at the same nlat_half. ","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"info: HEALPix grids do not use Nside as resolution parameter\nThe original formulation for HEALPix grids use N_side, the number of grid points along the edges of each basepixel (8 in the figure above), SpeedyWeather.jl uses nlat_half, the number of rings on one hemisphere, Equator included, for all grids. This is done for consistency across grids. We may use N_side for the documentation or within functions though.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"Related: Effective grid resolution and Available horizontal resolutions.","category":"page"},{"location":"grids/#Matching-spectral-and-grid-resolution","page":"Grids","title":"Matching spectral and grid resolution","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"A given spectral resolution can be matched to a variety of grid resolutions. A cubic grid, for example, combines a spectral truncation T with a grid resolution N (=nlat_half) such that T + 1 = N. Using T31 and an O32 is therefore often abbreviated as Tco31 meaning that the spherical harmonics are truncated at l_max=31 in combination with N=32, i.e. 64 latitude rings in total on an octahedral Gaussian grid. In SpeedyWeather.jl the choice of the order of truncation is controlled with the dealiasing parameter in the SpectralGrid construction.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"Let J be the total number of rings. Then we have","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"T approx J for linear truncation, i.e. dealiasing = 1\nfrac32T approx J for quadratic truncation, i.e. dealiasing = 2\n2T approx J for cubic truncation, , i.e. dealiasing = 3","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"and in general fracm+12T approx J for m-th order truncation. So the higher the truncation order the more grid points are used in combination with the same spectral resolution. A higher truncation order therefore makes all grid-point calculations more expensive, but can represent products of terms on the grid (which will have higher wavenumber components) to a higher accuracy as more grid points are available within a given wavelength. Using a sufficiently high truncation is therefore one way to avoid aliasing. A quick overview of how the grid resolution changes when dealiasing is passed onto SpectralGrid on the FullGaussianGrid","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"trunc dealiasing FullGaussianGrid size\n31 1 64x32\n31 2 96x48\n31 3 128x64\n42 1 96x48\n42 2 128x64\n42 3 192x96\n... ... ...","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"You will obtain this information every time you create a SpectralGrid(;Grid,trunc,dealiasing).","category":"page"},{"location":"grids/#FullGaussianGrid","page":"Grids","title":"Full Gaussian grid","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"(called FullGaussianGrid)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"The full Gaussian grid is a grid that uses regularly spaced longitudes which points that do not reduce in number towards the poles. That means for every latitude theta the longitudes phi are","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"phi_i = frac2pi (i-1)N_phi","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"with i = 1N_phi the in-ring index (1-based, counting from 0˚ eastward) and N_phi the number of longitudinal points on the grid. The first longitude is therefore 0˚, meaning that there is no longitudinal offset on this grid. There are always twice as many points in zonal direction as there are in meridional, i.e. N_phi = 2N_theta. The latitudes, however, are not regular, but chosen from the j-th zero crossing z_j(l) of the l-th Legendre polynomial. For theta in latitudes","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"sin(theta_j) = z_j(l) ","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"As it can be easy to mix up latitudes, colatitudes and as the Legendre polynomials are defined in 01 an overview of the first Gaussian latitudes (approximated for l2 for brevity)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"l Zero crossings z_j Latitudes [˚N]\n2 pm tfrac1sqrt3 pm 353\n4 pm 034 pm 086 pm 199 pm 5944\n6 pm 024 pm 066 pm 093 pm 138 pm 414 pm 688","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"Only even Legendre polynomials are used, such that there is always an even number of latitudes, with no latitude on the Equator. As you can already see from this short table, the Gaussian latitudes do not nest, i.e. different resolutions through different l do not share latitudes. The latitudes are also only approximately evenly spaced. Due to the Gaussian latitudes, a spectral transform with a full Gaussian grid is exact as long as the truncation is at least quadratic, see Matching spectral and grid resolution. Exactness here means that only rounding errors occur in the transform, meaning that the transform error is very small compared to other errors in a simulation. This property arises from that property of the Gauss-Legendre quadrature, which is used in the Spherical Harmonic Transform with a full Gaussian grid.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"On the full Gaussian grid there are in total N_phi N_theta grid points, which are squeezed towards the poles, making the grid area smaller and smaller following a cosine. But no points are on the poles as z=-1 or 1 is never a zero crossing of the Legendre polynomials.","category":"page"},{"location":"grids/#OctahedralGaussianGrid","page":"Grids","title":"Octahedral Gaussian grid","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"(called OctahedralGaussianGrid)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"The octahedral Gaussian grid is a reduced grid, i.e. the number of longitudinal points reduces towards the poles. It still uses the Gaussian latitudes from the full Gaussian grid so the exactness property of the spherical harmonic transform also holds for this grid. However, the longitudes phi_i with i = 116+4j on the j-th latitude ring (starting with 1 around the north pole), j=1tfracN_theta2, are now, on the northern hemisphere,","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"phi_i = frac2pi (i-1)16 + 4j","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"We start with 20 points, evenly spaced, starting at 0˚E, around the first latitude ring below the north pole. The next ring has 24 points, then 28, and so on till reaching the Equator (which is not a ring). For the southern hemisphere all points are mirrored around the Equator. For more details see Malardel, 2016[M16].","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"Note that starting with 20 grid points on the first ring is a choice that ECMWF made with their grid for accuracy reasons. An octahedral Gaussian grid can also be defined starting with fewer grid points on the first ring. However, in SpeedyWeather.jl we follow ECMWF's definition. ","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"The grid cells of an octahedral Gaussian grid are not exactly equal area, but are usually within a factor of two. This largely solves the efficiency problem of having too many grid points near the poles for computational, memory and data storage reasons.","category":"page"},{"location":"grids/#FullClenshawGrid","page":"Grids","title":"Full Clenshaw-Curtis grid","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"(called FullClenshawGrid)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"The full Clenshaw-Curtis grid is a regular longitude-latitude grid, but a specific one: The colatitudes theta_j, and the longitudes phi_i are","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"theta_j = fracjN_theta + 1pi quad phi_i = frac2pi (i-1)N_phi","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"with i the in-ring zonal index i = 1N_phi and j = 1 N_theta the ring index starting with 1 around the north pole. There is no grid point on the poles, but in contrast to the Gaussian grids there is a ring on the Equator. The longitudes are shared with the full Gaussian grid. Being a full grid, also the full Clenshaw-Curtis grid suffers from too many grid points around the poles, this is addressed with the octahedral Clenshaw-Curtis grid.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"The full Clenshaw-Curtis grid gets its name from the Clenshaw-Curtis quadrature that is used in the Legendre transform (see Spherical Harmonic Transform). This quadrature relies on evenly spaced latitudes, which also means that this grid nests, see Hotta and Ujiie[HU18]. More importantly for our application, the Clenshaw-Curtis grids (including the octahedral described below) allow for an exact transform with cubic truncation (see Matching spectral and grid resolution). Recall that the Gaussian latitudes allow for an exact transform with quadratic truncation, so the Clenshaw-Curtis grids require more grid points for the same spectral resolution to be exact. But compared to other errors during a simulation this error may be masked anyway.","category":"page"},{"location":"grids/#OctahedralClenshawGrid","page":"Grids","title":"Octahedral Clenshaw-Curtis grid","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"(called OctahedralClenshawGrid)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"In the same as we constructed the octahedral Gaussian grid from the full Gaussian grid, the octahedral Clenshaw-Curtis grid can be constructed from the full Clenshaw-Curtis grid. It therefore shares the latitudes with the full grid, but the longitudes with the octahedral Gaussian grid.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"theta_j = fracjN_theta + 1pi quad phi_i = frac2pi (i-1)16 + 4j","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"Notation as before, but note that the definition for phi_i only holds for the northern hemisphere, Equator included. The southern hemisphere is mirrored. The octahedral Clenshaw-Curtis grid inherits the exactness properties from the full Clenshaw-Curtis grid, but as it is a reduced grid, it is more efficient in terms of computational aspects and memory than the full grid. Hotta and Ujiie[HU18] describe this grid in more detail.","category":"page"},{"location":"grids/#HEALPixGrid","page":"Grids","title":"HEALPix grid","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"(called HEALPixGrid)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"Technically, HEALPix grids are a class of grids that tessalate the sphere into faces that are often called basepixels. For each member of this class there are N_varphi basepixels in zonal direction and N_theta basepixels in meridional direction. For N_varphi = 4 and N_theta = 3 we obtain the classical HEALPix grid with N_varphi N_theta = 12 basepixels shown above in Implemented grids. Each basepixel has a quadratic number of grid points in them. There's an equatorial zone where the number of zonal grid points is constant (always 2N, so 32 at N=16) and there are polar caps above and below the equatorial zone with the border at cos(theta) = 23 (theta in colatitudes).","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"Following Górski, 2004[G04], the z=cos(theta) colatitude of the j-th ring in the north polar cap, j=1N_side with 2N_side = N is ","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"z = 1 - fracj^23N_side^2","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"and on that ring, the longitude phi of the i-th point (i is the in-ring-index) is at","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"phi = fracpi2j(i-tfrac12)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"The in-ring index i goes from i=14 for the first (i.e. northern-most) ring, i=18 for the second ring and i = 14j for the j-th ring in the northern polar cap.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"In the north equatorial belt j=N_side2N_side this changes to","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"z = frac43 - frac2j3N_side","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"and the longitudes change to (i is always i = 14N_side in the equatorial belt meaning the number of longitude points is constant here)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"phi = fracpi2N_side(i - fracs2) quad s = (j - N_side + 1) mod 2","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"The modulo function comes in as there is an alternating longitudinal offset from the prime meridian (see Implemented grids). For the southern hemisphere the grid point locations can be obtained by mirror symmetry.","category":"page"},{"location":"grids/#Grid-cell-boundaries","page":"Grids","title":"Grid cell boundaries","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"The cell boundaries are obtained by setting i = k + 12 or i = k + 12 + j (half indices) into the equations above, such that z(phik), a function for the cosine of colatitude z of index k and the longitude phi is obtained. These are then (northern polar cap)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"z = 1 - frack^23N_side^2left(fracpi2phi_tright)^2 quad z = 1 - frack^23N_side^2left(fracpi2phi_t - piright)^2","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"with phi_t = phi mod tfracpi2 and in the equatorial belt","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"z = frac23-frac4k3N_side pm frac8phi3pi","category":"page"},{"location":"grids/#OctaHEALPixGrid","page":"Grids","title":"OctaHEALPix grid","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"(called OctaHEALPixGrid)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"While the classic HEALPix grid is based on a dodecahedron, other choices for N_varphi and N_theta in the class of HEALPix grids will change the number of faces there are in zonal/meridional direction. With N_varphi = 4 and N_theta = 1 we obtain a HEALPix grid that is based on an octahedron, which has the convenient property that there are twice as many longitude points around the equator than there are latitude rings between the poles. This is a desirable for truncation as this matches the distances too, 2pi around the Equator versus pi between the poles. N_varphi = 6 N_theta = 2 or N_varphi = 8 N_theta = 3 are other possible choices for this, but also more complicated. See Górski, 2004[G04] for further examples and visualizations of these grids.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"We call the N_varphi = 4 N_theta = 1 HEALPix grid the OctaHEALPix grid, which combines the equal-area property of the HEALPix grids with the octahedron that's also used in the OctahedralGaussianGrid or the OctahedralClenshawGrid. As N_theta = 1 there is no equatorial belt which simplifies the grid. The latitude of the j-th isolatitude ring on the OctaHEALPixGrid is defined by","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"z = 1 - fracj^2N^2","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"with j=1N, and similarly for the southern hemisphere by symmetry. On this grid N_side = N where N= nlat_half, the number of latitude rings on one hemisphere, Equator included, because each of the 4 basepixels spans from pole to pole and covers a quarter of the sphere. The longitudes with in-ring- index i = 14j are","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"phi = fracpi2j(i - tfrac12)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"and again, the southern hemisphere grid points are obtained by symmetry.","category":"page"},{"location":"grids/#Grid-cell-boundaries-2","page":"Grids","title":"Grid cell boundaries","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"Similar to the grid cell boundaries for the HEALPix grid, the OctaHEALPix grid's boundaries are","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"z = 1 - frack^2N^2left(fracpi2phi_tright)^2 quad z = 1 - frack^2N^2left(fracpi2phi_t - piright)^2","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"The 3N_side^2 in the denominator of the HEALPix grid came simply N^2 for the OctaHEALPix grid and there's no separate equation for the equatorial belt (which doesn't exist in the OctaHEALPix grid).","category":"page"},{"location":"grids/#References","page":"Grids","title":"References","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"[G04]: Górski, Hivon, Banday, Wandelt, Hansen, Reinecke, Bartelmann, 2004. HEALPix: A FRAMEWORK FOR HIGH-RESOLUTION DISCRETIZATION AND FAST ANALYSIS OF DATA DISTRIBUTED ON THE SPHERE, The Astrophysical Journal. doi:10.1086/427976","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"[M16]: S Malardel, et al., 2016: A new grid for the IFS, ECMWF Newsletter 146. https://www.ecmwf.int/sites/default/files/elibrary/2016/17262-new-grid-ifs.pdf","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"[HU18]: Daisuke Hotta and Masashi Ujiie, 2018: A nestable, multigrid-friendly grid on a sphere for global spectralmodels based on Clenshaw–Curtis quadrature, Quarterly Journal of the Royal Meteorological Society, DOI: 10.1002/qj.3282","category":"page"},{"location":"primitiveequation/#Primitive-equation-model","page":"Primitive equation model","title":"Primitive equation model","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The primitive equations are a hydrostatic approximation of the compressible Navier-Stokes equations for an ideal gas on a rotating sphere. We largely follow the idealised spectral dynamical core developed by GFDL[GFDL1] and documented therein[GFDL2].","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The primitive equations solved by SpeedyWeather.jl for relative vorticity zeta, divergence mathcalD, logarithm of surface pressure ln p_s, temperature T and specific humidity q are","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"beginaligned\nfracpartial zetapartial t = nabla times (mathbfmathcalP_mathbfu\n+ (f+zeta)mathbfu_perp - W(mathbfu) - R_dT_vnabla ln p_s) \nfracpartial mathcalDpartial t = nabla cdot (mathcalP_mathbfu\n+ (f+zeta)mathbfu_perp - W(mathbfu) - R_dT_vnabla ln p_s) - nabla^2(frac12(u^2 + v^2) + Phi) \nfracpartial ln p_spartial t = -frac1p_s nabla cdot int_0^p_s mathbfudp \nfracpartial Tpartial t = mathcalP_T -nablacdot(mathbfuT) + TmathcalD - W(T) + kappa T_v fracD ln pDt \nfracpartial qpartial t = mathcalP_q -nablacdot(mathbfuq) + qmathcalD - W(q)\nendaligned","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"with velocity mathbfu = (uv), rotated velocity mathbfu_perp = (v-u), Coriolis parameter f, W the Vertical advection operator, dry air gas constant R_d, Virtual temperature T_v, Geopotential Phi, pressure p and surface pressure p_s, thermodynamic kappa = R_dc_p with c_p the heat capacity at constant pressure. Horizontal hyper diffusion of the form (-1)^n+1nunabla^2n with coefficient nu and power n is added for every variable that is advected, meaning zeta mathcalD T q, but left out here for clarity, see Horizontal diffusion.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The parameterizations for the tendencies of uvTq from physical processes are denoted as mathcalP_mathbfu = (mathcalP_u mathcalP_v) mathcalP_T mathcalP_q and are further described in the corresponding sections, see Parameterizations.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"SpeedyWeather.jl implements a PrimitiveWet and a PrimitiveDry dynamical core. For a dry atmosphere, we have q = 0 and the virtual temperature T_v = T equals the temperature (often called absolute to distinguish from the virtual temperature). The terms in the primitive equations and their discretizations are discussed in the following sections. ","category":"page"},{"location":"primitiveequation/#Virtual-temperature","page":"Primitive equation model","title":"Virtual temperature","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"info: In short: Virtual temperature\nVirtual temperature is the temperature dry air would need to have to be as light as moist air. It is used in the dynamical core to include the effect of humidity on the density while replacing density through the ideal gas law with temperature.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"We assume the atmosphere to be composed of two ideal gases: Dry air and water vapour. Given a specific humidity q both gases mix, their pressures p_d, p_w (d for dry, w for water vapour), and densities rho_d rho_w add in a given air parcel that has temperature T. The ideal gas law then holds for both gases","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"beginaligned\np_d = rho_d R_d T \np_w = rho_w R_w T \nendaligned","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"with the respective specific gas constants R_d = Rm_d and R_w = Rm_w obtained from the universal gas constant R divided by the molecular masses of the gas. The total pressure p in the air parcel is","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"p = p_d + p_w = (rho_d R_d + rho_w R_w)T","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"We ultimately want to replace the density rho = rho_w + rho_d in the dynamical core, using the ideal gas law, with the temperature T, so that we never have to calculate the density explicitly. However, in order to not deal with two densities (dry air and water vapour) we would like to replace temperature with a virtual temperature that includes the effect of humidity on the density. So, wherever we use the ideal gas law to replace density with temperature, we would use the virtual temperature, which is a function of the absolute temperature and specific humidity, instead. A higher specific humidity in an air parcel lowers the density as water vapour is lighter than dry air. Consequently, the virtual temperature of moist air is higher than its absolute temperature because warmer air is lighter too at constant pressure. We therefore think of the virtual temperature as the temperature dry air would need to have to be as light as moist air.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Starting with the last equation, with some manipulation we can write the ideal gas law as total density rho times a gas constant times the virtual temperature that is supposed to be a function of absolute temperature, humidity and some constants","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"p = (rho R_d + rho_w (R_w - R_d)) T = rho R_d (1 +\nfrac1 - tfracR_dR_wtfracR_dR_w fracrho_wrho_w + rho_d)T","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Now we identify","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"mu = frac1 - tfracR_dR_wtfracR_dR_w","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"as some constant that is positive for water vapour being lighter than dry air (tfracR_dR_w = tfracm_wm_d 1) and","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"q = fracrho_wrho_w + rho_d","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"as the specific humidity. Given temperature T and specific humidity q, we can therefore calculate the virtual temperature T_v as","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"T_v = (1 + mu q)T","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"For completeness we want to mention here that the above product, because it is a product of two variables qT has to be computed in grid-point space, see Spherical Harmonic Transform. To obtain an approximation to the virtual temperature in spectral space without expensive transforms one can linearize","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"T_v approx T + mu qbarT","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"with a global constant temperature barT, for example obtained from the l=m=0 mode, barT = T_00frac1sqrt4pi but depending on the normalization of the spherical harmonics that factor needs adjustment. We call this the linear virtual temperature which is used for the geopotential calculation, see #254.","category":"page"},{"location":"primitiveequation/#Vertical-coordinates","page":"Primitive equation model","title":"Vertical coordinates","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"We start with some general considerations that apply when changing the vertical coordinate from height z to something else. Let Psi(xyzt) be some variable that depends on space and time. Now we want to express Psi using some other coordinate eta in the vertical. Regardless of the coordinate system the value of Psi at the to z corresponding eta (and vice versa) has to be the same as we only want to change the coordinate, not Psi itself.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Psi(xyetat) = Psi(xyz(xyetat)t)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"So you can think of z as a function of eta and eta as a function of z. The chain rule lets us differentiate Psi with respect to z or eta","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial Psipartial z = fracpartial Psipartial etafracpartial etapartial z\nqquad fracpartial Psipartial eta = fracpartial Psipartial zfracpartial zpartial eta","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"But for derivatives with respect to xyt we have to apply the multi-variable chain-rule as both Psi and eta depend on it. So a derivative with respect to x on eta levels (where eta constant) becomes","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"left fracpartial Psipartial xrightvert_eta = \nleft fracpartial Psipartial xrightvert_z +\nfracpartial Psipartial z\nleft fracpartial zpartial xrightvert_eta","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"So we first take the derivative of Psi with respect to x, but then also have to account for the fact that, at a given eta, z depends on x which is again dealt with using the univariate chain rule from above. We will make use of that for the Pressure gradient.","category":"page"},{"location":"primitiveequation/#Sigma-coordinates","page":"Primitive equation model","title":"Sigma coordinates","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The problem with pure pressure coordinates is that they are not terrain-following. For example, the 1000 hPa level in the Earth's atmosphere cuts through mountains. A flow field on such a level is therefore not continuous and one would need to deal with boundaries. Especially with spherical harmonics we need a terrain-following vertical coordinate to transform between continuous fields in grid-point space and spectral space.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"SpeedyWeather.jl currently uses so-called sigma coordinates for the vertical. This coordinate system uses fraction of surface pressure in the vertical, i.e.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"sigma = fracpp_s","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"with sigma = 01 and sigma = 0 being the top (zero pressure) and sigma = 1 the surface (at surface pressure). As a consequence the vertical dimension is also indexed from top to surface.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"info: Vertical indexing\nPressure, sigma, or hybrid coordinates in the vertical range from lowest values at the top to highest values at the surface. Consistently, we also index the vertical dimension top to surface. This means that k=1 is the top-most layer, and k=N_lev (or similar) is the layer that sits directly above the surface.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Sigma coordinates are therefore terrain-following, as sigma = 1 is always at surface pressure and so this level bends itself around every mountain, although the actual pressure on this level can vary. For a visualisation see #329.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"One chooses sigma levels associated with the k-th layer and the pressure can be reobtained from the surface pressure p_s","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"p_k = sigma_kp_s","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The layer thickness in terms of pressure is","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Delta p_k = p_k+tfrac12 - p_k-tfrac12 =\n(sigma_k+tfrac12 - sigma_k-tfrac12) p_s = Delta sigma_k p_s","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"which can also be expressed with the layer thickness in sigma coordinates Delta sigma_k times the surface pressure. In SpeedyWeather.jl one chooses the half levels sigma_k+tfrac12 first and then obtains the full levels through averaging","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"sigma_k = fracsigma_k+tfrac12 + sigma_k-tfrac122","category":"page"},{"location":"primitiveequation/#Geopotential","page":"Primitive equation model","title":"Geopotential","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"In the hydrostatic approximation the vertical momentum equation becomes","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial ppartial z = -rho g","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"meaning that the (negative) vertical pressure gradient is given by the density in that layer times the gravitational acceleration. The heavier the fluid the more the pressure will increase below. Inserting the ideal gas law","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial gzpartial p = -fracR_dT_vp","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"with the geopotential Phi = gz we can write this in terms of the logarithm of pressure","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial Phipartial ln p = -R_dT_v","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Note that we use the Virtual temperature here as we replaced the density through the ideal gas law with temperature. Given a vertical temperature profile T_v and the (constant) surface geopotential Phi_s = gz_s where z_s is the orography, we can integrate this equation from the surface to the top to obtain Phi_k on every layer k. The surface is at k = N+tfrac12 (see Vertical coordinates) with N vertical levels. We can integrate the geopotential onto half levels as (T_k^v is the virtual temperature at layer k, the subscript v has been moved to be a superscript)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Phi_k-tfrac12 = Phi_k+tfrac12 + R_dT^v_k(ln p_k+12 - ln p_k-12)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"or onto full levels with","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Phi_k = Phi_k+tfrac12 + R_dT^v_k(ln p_k+12 - ln p_k)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"We use this last formula first to get from Phi_s to Phi_N, and then for every k twice to get from Phi_k to Phi_k-1 via Phi_k-tfrac12. For the first half-level integration we use T_k for the second T_k-1.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"warning: Semi-implicit time integration: Geopotential\nWith the semi-implicit time integration in SpeedyWeather the Geopotential is not calculated from the spectral temperature at the current, but at the previous time step. This is because this is a linear term that we solve implicitly to avoid instabilities from gravity waves. For details see section Semi-implicit time stepping.","category":"page"},{"location":"primitiveequation/#Surface-pressure-tendency","page":"Primitive equation model","title":"Surface pressure tendency","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The surface pressure increases with a convergence of the flow above. Written in terms of the surface pressure directly, and not its logarithm","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial p_spartial t = -nabla cdot int_0^p_s mathbfudp","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"For k discrete layers from 1 at the top to N at the surface layer this can be written as","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial p_spartial t = - sum_k=1^N nabla cdot (mathbfu_k Delta p_k)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"which can be thought of as a vertical integration of the pressure thickness-weighted divergence. In sigma-coordinates with Delta p_k = Delta sigma_k p_s (see Vertical coordinates) this becomes","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial p_spartial t = - sum_k=1^N sigma_k nabla cdot (mathbfu_k p_s)\n= -sum_k=1^N sigma_k (mathbfu_k cdot nabla p_s + p_s nabla cdot mathbfu_k)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Using the logarithm of pressure ln p as the vertical coordinate this becomes","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial ln p_spartial t = \n-sum_k=1^N sigma_k (mathbfu_k cdot nabla ln p_s + nabla cdot mathbfu_k)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The second term is the divergence mathcalD_k at layer k. We introduce bara = sum_k Delta sigma_k a_k, the sigma-weighted vertical integration operator applied to some variable a. This is essentially an average as sum_k Delta sigma_k = 1. The surface pressure tendency can then be written as","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial ln p_spartial t = \n-mathbfbaru cdot nabla ln p_s - barmathcalD","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"which is form used by SpeedyWeather.jl to calculate the tendency of (the logarithm of) surface pressure.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"As we will have ln p_s available in spectral space at the beginning of a time step, the gradient can be easily computed (see Derivatives in spherical coordinates). However, we then need to transform both gradients to grid-point space for the scalar product with the (vertically sigma-averaged) velocity vector mathbfbaru before transforming it back to spectral space where the tendency is needed. In general, we can do the sigma-weighted average in spectral or in grid-point space, although it is computationally cheaper in spectral space. We therefore compute - barmathcalD entirely in spectral space. With () denoting spectral space and grid-point space (hence, () and () are the transforms in the respective directions) we therefore do","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"left(fracpartial ln p_spartial tright) = \nleft(-mathbfoverlineu cdot nabla (ln p_s)right) - overline(mathcalD)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"But note that it would also be possible to do","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"left(fracpartial ln p_spartial tright) = \nleft(-mathbfoverlineu cdot nabla (ln p_s) - overlinemathcalDright)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Meaning that we would compute the vertical average in grid-point space, subtract from the pressure gradient flux before transforming to spectral space. The same amount of transforms are performed but in the latter, the vertical averaging is done in grid-point space.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"warning: Semi-implicit time integration: Surface pressure tendency\nWith the semi-implicit time integration in SpeedyWeather the - overline(mathcalD) term is not evaluated from the spectral divergence mathcalD at the current, but at the previous time step. This is because this is a linear term that we solve implicitly to avoid instabilities from gravity waves. For details see section Semi-implicit time stepping.","category":"page"},{"location":"primitiveequation/#Vertical-advection","page":"Primitive equation model","title":"Vertical advection","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The advection equation tfracDTDt = 0 for a tracer T is, in flux form, for layer k","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial (T_k Delta p_k)partial t = - nabla cdot (mathbfu_k T_k Delta p_k)\n- (M_k+tfrac12T_k+tfrac12 - M_k-tfrac12T_k-tfrac12)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"which can be through the gradient product rule, and using the conservation of mass (see Vertical velocity) transformed into an advective form. In sigma coordinates this simplifies to","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial T_kpartial t = - mathbfu_k cdot nabla T_k\n- frac1Delta sigma_kleft(dotsigma_k+tfrac12(T_k+tfrac12 - T_k) - dotsigma_k-tfrac12(T_k - T_k-tfrac12)right)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"With the reconstruction at the faces, T_k+tfrac12, and T_k-tfrac12 depending on one's choice of the advection scheme. For a second order centered scheme, we choose T_k+tfrac12 = tfrac12(T_k + T_k+1) and obtain","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial T_kpartial t = - mathbfu_k cdot nabla T_k\n- frac12Delta sigma_kleft(dotsigma_k+tfrac12(T_k+1 - T_k) + dotsigma_k-tfrac12(T_k - T_k-1)right)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"However, note that this scheme is dispersive and easily leads to instabilities at higher resolution, where a more advanced vertical advection scheme becomes necessary. For convenience, we may write W(T) to denote the vertical advection term dotsigmapartial_sigma T, without specifying which schemes is used. The vertical velocity dotsigma is calculated as described in the following.","category":"page"},{"location":"primitiveequation/#Vertical-velocity","page":"Primitive equation model","title":"Vertical velocity","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"In the section Surface pressure tendency we used that the surface pressure changes with the convergence of the flow above, which derives from the conservation of mass. Similarly, the conservation of mass for layer k can be expressed as (setting T=1 in the advection equation in section Vertical advection)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial Delta p_kpartial t = -nabla cdot (mathbfu_k Delta p_k)\n- (M_k+tfrac12 - M_k-tfrac12)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Meaning that the pressure thickness Delta p_k of layer k changes with a horizontal divergence -nabla cdot (mathbfu_k Delta p_k) if not balanced by a net vertical mass flux M into of the layer through the bottom and top boundaries of k at kpmtfrac12. M is defined positive downward as this is the direction in which both pressure and sigma coordinates increase. The boundary conditions are M_tfrac12 = M_N+tfrac12 = 0, such that there is no mass flux into the top layer from above or out of the surface layer N and into the ground or ocean.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"When integrating from the top down to layer k we obtain the mass flux downwards out of layer k","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"M_k+tfrac12 = - sum_r=1^k nabla cdot (mathbfu_k Delta p_k) - fracpartial p_k+tfrac12partial t","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"In sigma coordinates we have M_k+tfrac12 = p_s dotsigma_k+tfrac12 with dotsigma being the vertical velocity in sigma coordinates, also defined at interfaces between layers. To calculate dotsigma we therefore compute","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"dotsigma_k+tfrac12 = fracM_k+tfrac12p_s = \n- sum_r=1^k Delta sigma_r (mathbfu_k cdot nabla ln p_s + mathcalD_r) \n+ sigma_k+tfrac12(-mathbfbaru cdot nabla ln p_s - barmathcalD)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"With barA denoting a sigma thickness-weighted vertical average as in section Surface pressure tendency. Now let barA_k be that average from r=1 to r=k only and not necessarily down to the surface, as required in the equation above, then we can also write","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"dotsigma_k+tfrac12 = \n- overlinemathbfu_k cdot nabla ln p_s - barmathcalD_k\n+ sigma_k+tfrac12(-mathbfbaru cdot nabla ln p_s - barmathcalD)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"See also Hoskins and Simmons, 1975[HS75]. These vertical averages are the same as required by the Surface pressure tendency and in the Temperature equation, they are therefore all calculated at once, storing the partial averages overlinemathbfu_k cdot nabla ln p_s and barmathcalD_k on the fly.","category":"page"},{"location":"primitiveequation/#Pressure-gradient","page":"Primitive equation model","title":"Pressure gradient","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The pressure gradient term in the primitive equations is","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"-frac1rhonabla_z p","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"with density rho and pressure p. The gradient here is taken at constant z hence the subscript. If we move to a pressure-based vertical coordinate system we will need to evaluate gradients on constant levels of pressure though, i.e. nabla_p. There is, by definition, no gradient of pressure on constant levels of pressure, but we can use the chain rule (see Vertical coordinates) to rewrite this as (use only x but y is equivalent)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"0 = left fracpartial ppartial x rightvert_p =\nleft fracpartial ppartial x rightvert_z +\nfracpartial ppartial zleft fracpartial zpartial x rightvert_p","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Using the hydrostatic equation partial_z p = -rho g this becomes","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"left fracpartial ppartial x rightvert_z = rho g left fracpartial zpartial x rightvert_p","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Or, in terms of the geopotential Phi = gz","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"frac1rhonabla_z p = nabla_p Phi","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"which is the actual reason why we use pressure coordinates: As density rho also depends on the pressure p the left-hand side means an implicit system when solving for pressure p. To go from pressure to sigma coordinates we apply the chain rule from section Vertical coordinates again and obtain","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"nabla_p Phi = nabla_sigma Phi - fracpartial Phipartial pnabla_sigma p\n= nabla_sigma Phi + frac1rhonabla_sigma p","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"where the last step inserts the hydrostatic equation again. With the ideal gas law, and note that we use Virtual temperature T_v everywhere where the ideal gas law is used, but in combination with the dry gas constant R_d","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"nabla_p Phi = nabla_sigma Phi + fracR_dT_vp nabla_sigma p","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Combining the pressure in denominator and gradient to the logarithm and with nabla ln p = nabla ln p_s in Sigma coordinates (the logarithm of sigma_k adds a constant that drops out in the gradient) we therefore have","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"- frac1rhonabla_z p = -nabla_p Phi = -nabla_sigma Phi - R_dT_v nabla_sigma ln p_s","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"From left to right: The pressure gradient force in z-coordinates; in pressure coordinates; and in sigma coordinates. Each denoted with the respective subscript on gradients. SpeedyWeather.jl uses the latter. In sigma coordinates we may drop the sigma subscript on gradients, but still meaning that the gradient is evaluated on a surface of our vertical coordinate. In vorticity-divergence formulation of the momentum equations the nabla_sigma Phi drops out in the vorticity equation (nabla times nabla Phi = 0), but becomes a -nabla^2 Phi in the divergence equation, which is therefore combined with the kinetic energy term -nabla^2(tfrac12(u^2 + v^2)) similar as it is done in the Shallow water equations. You can think of tfrac12(u^2 + v^2) + Phi as the Bernoulli potential in the primitive equations. However, due to the change into sigma coordinates the surface pressure gradient also has to be accounted for. Now highlighting only the pressure gradient force, we have in total","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"beginaligned\nfracpartial zetapartial t = nabla times ( - R_dT_vnabla ln p_s) + \nfracpartial mathcalDpartial t = nabla cdot ( - R_dT_vnabla ln p_s) - nabla^2Phi + \nendaligned","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"In our vorticity-divergence formulation and with sigma coordinates.","category":"page"},{"location":"primitiveequation/#Semi-implicit-pressure-gradient","page":"Primitive equation model","title":"Semi-implicit pressure gradient","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"With the semi-implicit time integration in SpeedyWeather.jl the pressure gradient terms are further modified as follows. See that section for details why, but here is just to mention that we need to split the terms into linear and non-linear terms. The linear terms are then evaluated at the previous time step for the implicit scheme such that we can avoid instabilities from gravity waves.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"We split the (virtual) temperature into a reference vertical profile T_k and its anomaly, T_v = T_k + T_v. The reference profile T_k has to be a global constant for the spectral transform but can depend on the vertical. With this, the previous equation becomes","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"beginaligned\nfracpartial zetapartial t = nabla times ( - R_dT_vnabla ln p_s) + \nfracpartial mathcalDpartial t = nabla cdot ( - R_dT_vnabla ln p_s) - nabla^2(Phi + R_d T_k ln p_s) + \nendaligned","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"In the vorticity equation the term with the reference profile drops out as nabla times nabla = 0, and in the divergence equation we move it into the Laplace operator. Now the linear terms are gathered with the Laplace operator and for the semi-implicit scheme we calculate both the Geopotential Phi and the contribution to the \"linear pressure gradient\" R_dT_k ln p_s at the previous time step for the semi-implicit time integration for details see therein.","category":"page"},{"location":"primitiveequation/#Vorticity-advection","page":"Primitive equation model","title":"Vorticity advection","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Vorticity advection in the primitive equation takes the form","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"beginaligned\nfracpartial upartial t = (f+zeta)v \nfracpartial vpartial t = -(f+zeta)u \nendaligned","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Meaning that we add the Coriolis parameter f and the relative vorticity zeta and multiply by the respective velocity component. While the primitive equations here are written with vorticity and divergence, we use uv here as other tendencies will be added and the curl and divergence are only taken once after transform into spectral space. To obtain a tendency for vorticity and divergence, we rewrite this as","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"beginaligned\nfracpartial zetapartial t = nabla times (f+zeta)mathbfu_perp \nfracpartial mathcalDpartial t = nabla cdot (f+zeta)mathbfu_perp \nendaligned","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"with mathbfu_perp = (v-u) the rotated velocity vector, see Barotropic vorticity equation.","category":"page"},{"location":"primitiveequation/#Humidity-equation","page":"Primitive equation model","title":"Humidity equation","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The dynamical core treats humidity as an (active) tracer, meaning that after the physical parameterizations for humidity mathcalP are calculated in grid-point space, humidity is only advected with the flow. The only exception is the Virtual temperature as high levels of humidity will lower the effective density, which is why we use the virtual instead of the absolute temperature. The equation to be solved for humidity is therefore,","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"left( fracpartial qpartial t right) = left(leftmathcalP_q - W_q +\nqmathcalD rightright) -nablacdot(mathbfuq)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"With () denoting spectral space and grid-point space, so that () and () are the transforms in the respective directions. To avoid confusion with that notation, we write the tendency of humidity due to Vertical advection as W_q. This equation is identical to a tracer equation, with mathcalP_q denoting sources and sinks. Note that Horizontal diffusion should be applied to every advected variable.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"A very similar equation is solved for (absolute) temperature as described in the following.","category":"page"},{"location":"primitiveequation/#Temperature-equation","page":"Primitive equation model","title":"Temperature equation","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The first law of thermodynamic states that the internal energy I is increased by the heat Q applied minus the work W done by the system. We neglect changes in chemical composition ([Vallis], chapter 1.5). For an ideal gas, the internal energy is c_vT with c_v the heat capacity at constant volume and temperature T. The work done is pV, with pressure p and the specific volume V","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"dI = Q - p dV","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"For fluids we replace the differential d here with the material derivative tfracDDt. With V = tfrac1rho and density rho we then have","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"c_v fracDTDt = -p fracD (1rho)Dt + Q","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Using the ideal gas law to replace tfrac1rho with tfracRT_vp (we are using the Virtual temperature again), and using","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"pfracD (1p)Dt = -frac1p fracDpDt","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"we have","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"(c_v + R)fracDTDt = fracRT_vpfracDpDt + Q","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"And further, with c_p = c_v + R the heat capacity at constant pressure, kappa = tfracRc_p, and using the logarithm of pressure","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracDTDt = kappa T_vfracD ln pDt + fracQc_p","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"This is the form of the temperature equation that SpeedyWeather.jl uses. Temperature is advected through the material derivative and first term on the right-hand side represents an adiabatic conversion term describing how the temperature changes with changes in pressure. Recall that this term originated from the work term in the first law of thermodynamics. The forcing term tfracQc_p is here identified as the physical parameterizations changing the temperature, for example radiation, and hence we will call it P_T.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Similar to the Humidity equation we write the equation for (absolute) temperature T as","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"left( fracpartial Tpartial t right) = left(leftmathcalP_T - W_T +\nTmathcalD + kappa T_v fracD ln pDt rightright) -nablacdot(mathbfuT)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"W_T is the Vertical advection of temperature. We evaluate the adiabatic conversion term completely in grid-point space following Simmons and Burridge, 1981[SB81] Equation 3.12 and 3.13. Leaving out the kappa T_v for clarity, the term at level k is","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"left(fracD ln pD tright)_k = mathbfu_k cdot nabla ln p_k\n- frac1Delta p_k leftleft( ln fracp_k+tfrac12p_k-tfrac12right)\nsum_r=1^k-1nabla cdot (mathbfu_k Delta p_k) + alpha_k nabla cdot (mathbfu_k Delta p_k) right","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"with","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"alpha_k = 1 - fracp_k-tfrac12Delta p_k ln fracp_k+tfrac12p_k-tfrac12","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"In sigma coordinates this simplifies to, following similar steps as in Surface pressure tendency","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"beginaligned\nleft(fracD ln pD tright)_k = mathbfu_k cdot nabla ln p_s \n- frac1Delta sigma_k left( ln fracsigma_k+tfrac12sigma_k-tfrac12right)\nsum_r=1^k-1Delta sigma_r (mathcalD_r + mathbfu_r cdot nabla ln p_s) -\nalpha_k (mathcalD_k + mathbfu_k cdot nabla ln p_s)\nendaligned","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Let A_k = mathcalD_k + mathbfu_k cdot nabla ln p_s and beta_k = tfrac1Delta sigma_k left( ln tfracsigma_k+tfrac12sigma_k-tfrac12right), then this can also be summarised as","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"left(fracD ln pD tright)_k = mathbfu_k cdot nabla ln p_s\n- beta_k sum_r=1^k-1Delta sigma_r A_r - alpha_k A_k","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The alpha_k beta_k are constants and can be precomputed. The surface pressure flux mathbfu_k cdot nabla ln p_s has to be computed, so does the vertical sigma-weighted average from top to k-1, which is done when computing other vertical averages for the Surface pressure tendency.","category":"page"},{"location":"primitiveequation/#Semi-implicit-temperature-equation","page":"Primitive equation model","title":"Semi-implicit temperature equation","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"For the semi-implicit scheme we need to split the temperature equation into linear and non-linear terms, as the linear terms need to be evaluated at the previous time step. Decomposing temperature T into T = T_k + T with the reference profile T_k and its anomaly T, the temperature equation becomes","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"left( fracpartial Tpartial t right) = mathcalP_T - W_T +\nTmathcalD + kappa T_v fracD ln pDt -nablacdot(mathbfuT)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Note that we do not change the adiabatic conversion term. While its linear component kappa T_k^v tfracD ln p_sD t (the subscript v for Virtual temperature as been raised) would need to be evaluated at the previous time step, we still evaluate this term at the current time step and move it within the semi-implicit corrections to the previous time step afterwards.","category":"page"},{"location":"primitiveequation/#implicit_primitive","page":"Primitive equation model","title":"Semi-implicit time stepping","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Conceptually, the semi-implicit time stepping in the Primitive equation model is the same as in the Shallow water model, but","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"tendencies for divergence mathcalD, logarithm of surface pressure ln p_s but also temperature T are computed semi-implicitly,\nthe vertical layers are coupled, creating a linear equation system that is solved via matrix inversion.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The linear terms of the primitive equations follow a linearization around a state of rest without orography and a reference vertical temperature profile. The scheme described here largely follows Hoskins and Simmons [HS75], which has also been used in Simmons and Burridge [SB81].","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"As before, let delta V = tfracV_i+1 - V_i-12Delta t be the tendency we need for the Leapfrog time stepping. With the implicit time step xi = 2alphaDelta t, alpha in tfrac121 we have","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"delta V = N_E(V_i) + N_I(V_i-1) + xi N_I(delta V)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"with N_E being the explicitly-treated non-linear terms and N_I the implicitly-treated linear terms, such that N_I is a linear operator. We can therefore solve for delta V by inverting N_I, ","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"delta V = (1-xi N_I)^-1G","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"where we gathered the uncorrected right-hand side as G","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"G = N_E(V_i) + N_I(V_i-1) = N(V_i) + N_I(V_i-1 - V_i)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"So for every linear term in N_I we have two options corresponding to two sides of this equation","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Evaluate it at the previous time step i-1\nOr, evaluate it at the current time step i as N(V_i), but then move it back to the previous time step i-1 by adding (in spectral space) the linear operator N_I evaluated with the difference between the two time steps.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"If there is a tendency that is easily evaluated in spectral space it is easier to follow 1. However, a term that is costly to evaluate in grid-point space should usually follow the latter. The reason is that the previous time step is generally not available in grid-point space (unless recalculated through a costly transform or stored with additional memory requirements) so it is easier to follow 2 where the N_I is available in spectral space. For the adiabatic conversion term in the Temperature equation we follow 2 as one would otherwise need to split this term into a non-linear and linear term, evaluating it essentially twice in grid-point space. ","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"So what is G in the Primitive equation model? ","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"beginaligned\nG_mathcalD = N^E_mathcalD - nabla^2(Phi^i-1 + R_dT_k^v (ln p_s)^i-1)\n= N^E_mathcalD - nabla^2( mathbfRT^i-1 + mathbfUln p_s^i-1) \nG_ln p_s = N_ln p_s^E - overlinemathcalD^i-1\n= N_ln p_s^E + mathbfWmathcalD^i-1 \nG_T = N_T + mathbfL(mathcalD^i-1 - mathcalD^i) \nendaligned","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"G is for the divergence, pressure and temperature equation the \"uncorrected\" tendency. Moving time step i - 1 to i we would be back with a fully explicit scheme. In the divergence equation the Geopotential Phi is calculated from temperature T at the previous time step i-1 (denoted as superscript) and the \"linear\" Pressure gradient from the logarithm of surface pressure at the previous time step. One can think of these two calculations as linear operators, mathbfR and mathbfU. We will shortly discuss their properties. While we could combine them with the Laplace operator nabla^2 (which is also linear) we do not do this as mathbfR U do not depend on the degree and order of the spherical harmonics (their wavenumber) but on the vertical, but nabla^2 does not depend on the vertical, only on the wavenumber. All other terms are gathered in N_mathcalD^E (subscript E has been raised) and calculated as described in the respective section at the current time step i.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"For the pressure tendency, the subtraction with the thickness-weighted vertical average barmathcalD is the linear term that is treated implicitly. We call this operator mathbfW. For the temperature tendency, we evaluate all terms explicitly at the current time step in N_T but then move the linear term in the adiabatic conversion term with the operator mathbfL back to the previous time step. For details see Semi-implicit temperature equation.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The operators mathbfR U L W are all linear, meaning that we can apply them in spectral space to each spherical harmonic independently – the vertical is coupled however. With N being the number of vertical levels and the prognostic variables like temperature for a given degree l and order m being a column vector in the vertical, T_lm in mathbbR^N, these operators have the following shapes","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"beginaligned\nmathbfR in mathbbR^Ntimes N \nmathbfU in mathbbR^Ntimes 1 \nmathbfL in mathbbR^Ntimes N \nmathbfW in mathbbR^1times N \nendaligned","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"mathbfR is an integration in the vertical hence it is an upper triangular matrix such that the first (an top-most) k=1 element of the resulting vector depends on all vertical levels of the temperature mode T_lm, but the surface k=N only on the temperature mode at the surface. mathbfU takes the surface value of the lm mode of the logarithm of surface pressure (ln p_s)_lm and multiplies it element-wise with the reference temperature profile and the dry gas constant. So the result is a column vector. mathbfL is an N times N matrix as the adiabatic conversion term couples all layers. mathbfW is a row vector as it represents the vertical averaging of the spherical harmonics of a divergence profile. So, mathbfWmathcalD is a scalar product for every lm giving a contribution of all vertical layers in divergence to the (single-layer!) logarithm of surface pressure tendency.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"With the Gs defined we can now write the semi-implicit tendencies delta mathcalD, delta T, delta ln p_s as (first equation in this section)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"beginaligned\ndelta mathcalD = G_D - xi nabla^2(mathbfRdelta T + mathbfU delta ln p_s)\ndelta T = G_T + xi mathbfLdelta mathcalD \ndelta ln p_s = G_ln p_s + xi mathbfWdelta mathcalD\nendaligned","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Solving for delta mathcalD with the \"combined\" tendency","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"G = G_D - xi nabla^2(mathbfRG_T + mathbfUG_ln p_s)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"via","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"delta mathcalD = G - xi^2nabla^2(mathbfRL + UW)delta mathcalD","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"(mathbfUW is a matrix of size N times N) yields","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"delta D = left( 1 + xi^2nabla^2(mathbfRL + UW) right)^-1G = mathbfS^-1G","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The other tendencies delta T and delta ln p_s are then obtained through insertion above. We may call the operator to be inverted mathbfS which is of size l_max times N times N, hence for every degree l of the spherical harmonics (which the Laplace operator depends on) a N times N matrix coupling the N vertical levels. Furthermore, S depends","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"through xi on the time step Delta t,\nthrough mathbfRWL on the vertical level spacing Delta sigma_k\nthrough mathbfU on the reference temperature profile T_k","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"so for any changes of these the matrix inversion of mathbfS has to be recomputed. Otherwise the algorithm for the semi-implicit scheme is as follows","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"0. Precompute the linear operators mathbfRULW and with them the matrix inversion mathbfS^-1.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Then for every time step","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Compute the uncorrected tendencies evaluated at the current time step for the explicit terms and the previous time step for the implicit terms.\nException in SpeedyWeather.jl is the adiabatic conversion term, which is, using mathbfL moved afterwards from the current i to the previous time step i-1.\nCompute the combined tendency G from the uncorrected tendencies G_mathcalD, G_T, G_ln p_s.\nWith the inverted operator get the corrected tendency for divergence, delta mathcalD = mathbfS^-1G.\nObtain the corrected tendencies for temperature delta T and surface pressure delta ln p_s from delta mathcalD.\nApply Horizontal diffusion (which is only mentioned here as it further updates the tendencies).\nUse delta mathcalD, delta T and delta ln p_s in the Leapfrog time integration.","category":"page"},{"location":"primitiveequation/#Horizontal-diffusion","page":"Primitive equation model","title":"Horizontal diffusion","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Horizontal diffusion in the primitive equations is applied to vorticity zeta, divergence mathcalD, temperature T and humidity q. In short, all variables that are advected. For the dry equations, q=0 and no diffusion has to be applied.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The horizontal diffusion is applied implicitly in spectral space, as already described in Horizontal diffusion for the barotropic vorticity equation.","category":"page"},{"location":"primitiveequation/#Algorithm","page":"Primitive equation model","title":"Algorithm","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The following algorithm describes a time step of the PrimitiveWetModel, for the PrimitiveDryModel humidity can be set to zero and respective steps skipped.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"0. Start with initial conditions of relative vorticity zeta_lm, divergence D_lm, temperature T_lm, humidity q_lm and the logarithm of surface pressure (ln p_s)_lm in spectral space. Variables zeta D T q are defined on all vertical levels, the logarithm of surface pressure only at the surface. Transform this model state to grid-point space, obtaining velocities is done as in the shallow water model","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Invert the Laplacian of zeta_lm to obtain the stream function Psi_lm in spectral space\nInvert the Laplacian of D_lm to obtain the velocity potential Phi_lm in spectral space\nobtain velocities U_lm = (cos(theta)u)_lm V_lm = (cos(theta)v)_lm from nabla^perpPsi_lm + nablaPhi_lm\nTransform velocities U_lm, V_lm to grid-point space UV\nUnscale the cos(theta) factor to obtain uv","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Additionally we","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Transform zeta_lm, D_lm, T_lm (ln p_s)_lm to zeta D eta T ln p_s in grid-point space\nCompute the (non-linearized) Virtual temperature in grid-point space.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Now loop over","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Compute all tendencies of u v T q due to physical parameterizations in grid-point space.\nCompute the gradient of the logarithm of surface pressure nabla (ln p_s)_lm in spectral space and convert the two fields to grid-point space. Unscale the cos(theta) on the fly.\nFor every layer k compute the pressure flux mathbfu_k cdot nabla ln p_s in grid-point space. \nFor every layer k compute a linearized Virtual temperature in spectral space.\nFor every layer k compute a temperature anomaly (virtual and absolute) relative to a vertical reference profile T_k in grid-point space.\nCompute the Geopotential Phi by integrating the virtual temperature vertically in spectral space from surface to top.\nIntegrate uvD vertically to obtain barubarvbarD in grid-point space and also barD_lm in spectral space. Store on the fly also for every layer k the partial integration from 1 to k-1 (top to layer above). These will be used in the adiabatic term of the Temperature equation.\nCompute the Surface pressure tendency with the vertical averages from the previous step. For the semi-implicit time stepping\nFor every layer k compute the Vertical velocity.\nFor every layer k add the linear contribution of the Pressure gradient RT_k (ln p_s)_lm to the geopotential Phi in spectral space.\nFor every layer k compute the Vertical advection for uvTq and add it to the respective tendency.\nFor every layer k compute the tendency of uv due to Vorticity advection and the Pressure gradient RT_v nabla ln p_s and add to the respective existing tendency. Unscale cos(theta), transform to spectral space, take curl and divergence to obtain tendencies for zeta_lmmathcalD_lm.\nFor every layer k compute the adiabatic term and the horizontal advection in the Temperature equation in grid-point space, add to existing tendency and transform to spectral.\nFor every layer k compute the horizontal advection of humidity q in the Humidity equation in grid-point space, add to existing tendency and transform to spectral.\nFor every layer k compute the kinetic energy tfrac12(u^2 + v^2), transform to spectral and add to the Geopotential. For the semi-implicit time stepping also add the linear pressure gradient calculated from the previous time step. Now apply the Laplace operator and subtract from the divergence tendency.\nCorrect the tendencies following the semi-implicit time integration to prevent fast gravity waves from causing numerical instabilities.\nCompute the horizontal diffusion for the advected variables zetamathcalDTq\nCompute a leapfrog time step as described in Time integration with a Robert-Asselin and Williams filter\nTransform the new spectral state of zeta_lm, mathcalD_lm, T_lm, q_lm and (ln p_s)_lm to grid-point uvzetamathcalDTqln p_s as described in 0.\nPossibly do some output\nRepeat from 1.","category":"page"},{"location":"primitiveequation/#Scaled-primitive-equations","page":"Primitive equation model","title":"Scaled primitive equations","text":"","category":"section"},{"location":"primitiveequation/#References","page":"Primitive equation model","title":"References","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"[GFDL1]: Geophysical Fluid Dynamics Laboratory, Idealized models with spectral dynamics","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"[GFDL2]: Geophysical Fluid Dynamics Laboratory, The Spectral Dynamical Core","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"[Vallis]: GK Vallis, 2006. Atmopsheric and Ocean Fluid Dynamics, Cambridge University Press.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"[SB81]: Simmons and Burridge, 1981. An Energy and Angular-Momentum Conserving Vertical Finite-Difference Scheme and Hybrid Vertical Coordinates, Monthly Weather Review. DOI: 10.1175/1520-0493(1981)109<0758:AEAAMC>2.0.CO;2.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"[HS75]: Hoskins and Simmons, 1975. A multi-layer spectral model and the semi-implicit method, Quart. J. R. Met. Soc. DOI: 10.1002/qj.49710142918","category":"page"},{"location":"lowertriangularmatrices/#lowertriangularmatrices","page":"Submodule: LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"LowerTriangularMatrices 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.","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"This module defines LowerTriangularMatrix, a lower triangular matrix, which in contrast to LinearAlgebra.LowerTriangular does not store the entries above the diagonal. SpeedyWeather.jl uses LowerTriangularMatrix which is defined as a subtype of AbstractMatrix to store the spherical harmonic coefficients (see Spectral packing). ","category":"page"},{"location":"lowertriangularmatrices/#Creation-of-LowerTriangularMatrix","page":"Submodule: LowerTriangularMatrices","title":"Creation of LowerTriangularMatrix","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"A LowerTriangularMatrix can be created using zeros,ones,rand, or randn","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"julia> using SpeedyWeather.LowerTriangularMatrices\n\njulia> L = rand(LowerTriangularMatrix{Float32},5,5)\n5×5 LowerTriangularMatrix{Float32}:\n 0.912744 0.0 0.0 0.0 0.0\n 0.0737592 0.230592 0.0 0.0 0.0\n 0.799679 0.0765255 0.888098 0.0 0.0\n 0.670835 0.997938 0.505276 0.492966 0.0\n 0.949321 0.193692 0.793623 0.152817 0.357968","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"or the undef initializor LowerTriangularMatrix{Float32}(undef,3,3). The element type is arbitrary though, you can use any type T too.","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"Alternatively, it can be created through conversion from Matrix, which drops the upper triangle entries and sets them to zero","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"julia> M = rand(Float16,3,3)\n3×3 Matrix{Float16}:\n 0.2222 0.694 0.3452\n 0.2158 0.04443 0.274\n 0.9746 0.793 0.6294\n\njulia> LowerTriangularMatrix(M)\n3×3 LowerTriangularMatrix{Float16}:\n 0.2222 0.0 0.0\n 0.2158 0.04443 0.0\n 0.9746 0.793 0.6294","category":"page"},{"location":"lowertriangularmatrices/#Indexing-LowerTriangularMatrix","page":"Submodule: LowerTriangularMatrices","title":"Indexing LowerTriangularMatrix","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"LowerTriangularMatrix supports two types of indexing: 1) by denoting two indices, column and row [l,m] or 2) by denoting a single index [lm]. The double index works as expected","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"julia> L\n3×3 LowerTriangularMatrix{Float16}:\n 0.1499 0.0 0.0\n 0.1177 0.478 0.0\n 0.1709 0.756 0.3223\n\njulia> L[2,2]\nFloat16(0.478)","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"But the single index skips the zero entries in the upper triangle, i.e.","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"julia> L[4]\nFloat16(0.478)","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"which, important, is different from single indices of an AbstractMatrix","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"julia> Matrix(L)[4]\nFloat16(0.0)","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"In performance-critical code a single index should be used, as this directly maps to the index of the underlying data vector. The double index is somewhat slower as it first has to be converted to the corresponding single index.","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"Consequently, many loops in SpeedyWeather.jl are build with the following structure","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"n,m = size(L)\nij = 0\nfor j in 1:m\n for i in j:n\n ij += 1\n L[ij] = i+j\n end\nend","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"which loops over all lower triangle entries of L::LowerTriangularMatrix and the single index ij is simply counted up. However, one could also use [i,j] as indices in the loop body or to perform any calculation (i+j here). An iterator over all entries in the lower triangle can be created by","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"for ij in eachindex(L)\n # do something\nend","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"The setindex! functionality of matrixes will throw a BoundsError when trying to write into the upper triangle of a LowerTriangularMatrix, for example","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"julia> L[2,1] = 0 # valid index\n0\n\njulia> L[1,2] = 0 # invalid index in the upper triangle\nERROR: BoundsError: attempt to access 3×3 LowerTriangularMatrix{Float32} at index [1, 2]","category":"page"},{"location":"lowertriangularmatrices/#Linear-algebra-with-LowerTriangularMatrix","page":"Submodule: LowerTriangularMatrices","title":"Linear algebra with LowerTriangularMatrix","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"The LowerTriangularMatrices module's main purpose is not linear algebra, and it's implementation may not be efficient, however, many operations work as expected","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"julia> L = rand(LowerTriangularMatrix{Float32},3,3)\n3×3 LowerTriangularMatrix{Float32}:\n 0.57649 0.0 0.0\n 0.348685 0.875371 0.0\n 0.881923 0.850552 0.998306\n\njulia> L + L\n3×3 LowerTriangularMatrix{Float32}:\n 1.15298 0.0 0.0\n 0.697371 1.75074 0.0\n 1.76385 1.7011 1.99661\n\njulia> L * L\n3×3 Matrix{Float32}:\n 0.332341 0.0 0.0\n 0.506243 0.766275 0.0\n 1.68542 1.59366 0.996616","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"Note, however, that the latter includes a conversion to Matrix, which is true for many operations, including inv or \\. Hence when trying to do more sophisticated linear algebra with LowerTriangularMatrix we quickly leave lower triangular-land and go back to normal matrix-land.","category":"page"},{"location":"lowertriangularmatrices/#Function-and-type-index","page":"Submodule: LowerTriangularMatrices","title":"Function and type index","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"Modules = [SpeedyWeather.LowerTriangularMatrices]","category":"page"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.LowerTriangularMatrix","page":"Submodule: LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.LowerTriangularMatrix","text":"L = LowerTriangularMatrix{T}(v::Vector{T},m::Int,n::Int)\n\nA lower triangular matrix implementation that only stores the non-zero entries explicitly. L<:AbstractMatrix although in general we have L[i] != Matrix(L)[i], the former skips zero entries, tha latter includes them.\n\n\n\n\n\n","category":"type"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.LowerTriangularMatrix-Union{Tuple{AbstractMatrix{T}}, Tuple{T}} where T","page":"Submodule: LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.LowerTriangularMatrix","text":"L = LowerTriangularMatrix(M)\n\nCreate a LowerTriangularMatrix L from Matrix M by copying over the non-zero elements in M.\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#Base.fill!-Union{Tuple{T}, Tuple{LowerTriangularMatrix{T}, Any}} where T","page":"Submodule: LowerTriangularMatrices","title":"Base.fill!","text":"fill!(L::LowerTriangularMatrix,x)\n\nFills the elements of L with x. Faster than fill!(::AbstractArray,x) as only the non-zero elements in L are assigned with x.\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.eachharmonic-Tuple{LowerTriangularMatrix}","page":"Submodule: LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.eachharmonic","text":"unit_range = eachharmonic(L::LowerTriangular)\n\ncreates unit_range::UnitRange to loop over all non-zeros in a LowerTriangularMatrix L. Like eachindex but skips the upper triangle with zeros in L.\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.eachharmonic-Tuple{Vararg{LowerTriangularMatrix}}","page":"Submodule: LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.eachharmonic","text":"unit_range = eachharmonic(Ls::LowerTriangularMatrix...)\n\ncreates unit_range::UnitRange to loop over all non-zeros in the LowerTriangularMatrices provided as arguments. Checks bounds first. All LowerTriangularMatrix's need to be of the same size. Like eachindex but skips the upper triangle with zeros in L.\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.ij2k-Tuple{Integer, Integer, Integer}","page":"Submodule: LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.ij2k","text":"k = ij2k( i::Integer, # row index of matrix\n j::Integer, # column index of matrix\n m::Integer) # number of rows in matrix\n\nConverts the index pair i,j of an mxn LowerTriangularMatrix L to a single index k that indexes the same element in the corresponding vector that stores only the lower triangle (the non-zero entries) of L.\n\n\n\n\n\n","category":"method"},{"location":"setups/#Model-setups","page":"Model setups","title":"Model setups","text":"","category":"section"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"The following is a collection of model setups, starting with an easy setup of the Barotropic vorticity equation and continuing with more complicated setups.","category":"page"},{"location":"setups/#D-turbulence-on-a-non-rotating-sphere","page":"Model setups","title":"2D turbulence on a non-rotating sphere","text":"","category":"section"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"info: Setup script\nusing SpeedyWeather\nspectral_grid = SpectralGrid(trunc=63,nlev=1)\nstill_earth = Earth(rotation=0)\ninitial_conditions = StartWithRandomVorticity()\nmodel = BarotropicModel(;spectral_grid, initial_conditions, planet=still_earth)\nsimulation = initialize!(model)\nrun!(simulation,n_days=20)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"We want to use the barotropic model to simulate some free-decaying 2D turbulence on the sphere without rotation. We start by defining the SpectralGrid object. To have a resolution of about 200km, we choose a spectral resolution of T63 (see Available horizontal resolutions) and nlev=1 vertical levels. The SpectralGrid object will provide us with some more information","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=63,nlev=1)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"Next step we create a planet that's like Earth but not rotating","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"still_earth = Earth(rotation=0)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"There are other options to create a planet but they are irrelevant for the barotropic vorticity equations. We also want to specify the initial conditions, randomly distributed vorticity is already defined","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"using Random # hide\nRandom.seed!(1234) # hide\ninitial_conditions = StartWithRandomVorticity()","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"By default, the power of vorticity is spectrally distributed with k^-3, k being the horizontal wavenumber, and the amplitude is 10^-5texts^-1.","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"Now we want to construct a BarotropicModel with these","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"model = BarotropicModel(;spectral_grid, initial_conditions, planet=still_earth)\nnothing # hide","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"The model contains all the parameters, but isn't initialized yet, which we can do with and then run it. The run! command will always return the prognostic variables, which, by default, are plotted for surface relative vorticity with a unicode plot. The resolution of the plot is not necessarily representative but it lets us have a quick look at the result","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"simulation = initialize!(model)\nrun!(simulation,n_days=20)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"Woohoo! Something is moving! You could pick up where this simulation stopped by simply doing run!(simulation,n_days=50) again. We didn't store any output, which you can do by run!(simulation,output=true), which will switch on NetCDF output with default settings. More options on output in NetCDF output.","category":"page"},{"location":"setups/#Shallow-water-with-mountains","page":"Model setups","title":"Shallow water with mountains","text":"","category":"section"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"info: Setup script\nusing SpeedyWeather\nspectral_grid = SpectralGrid(trunc=63,nlev=1)\norography = NoOrography(spectral_grid)\ninitial_conditions = ZonalJet()\nmodel = ShallowWaterModel(;spectral_grid, orography, initial_conditions)\nsimulation = initialize!(model)\nrun!(simulation,n_days=6)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"As a second example, let's investigate the Galewsky et al.[G04] test case for the shallow water equations with and without mountains. As the shallow water system has also only one level, we can reuse the SpectralGrid from Example 1.","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=63,nlev=1)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"Now as a first simulation, we want to disable any orography, so we create a NoOrography","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"orography = NoOrography(spectral_grid)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"Although the orography is zero, you have to pass on spectral_grid so that it can still initialize zero-arrays of the correct size and element type. Awesome. This time the initial conditions should be set the the Galewsky et al.[G04] zonal jet, which is already defined as","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"initial_conditions = ZonalJet()","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"The jet sits at 45˚N with a maximum velocity of 80m/s and a perturbation as described in their paper. Now we construct a model, but this time a ShallowWaterModel","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"model = ShallowWaterModel(;spectral_grid, orography, initial_conditions)\nsimulation = initialize!(model)\nrun!(simulation,n_days=6)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"Oh yeah. That looks like the wobbly jet in their paper. Let's run it again for another 6 days but this time also store NetCDF output.","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"run!(simulation,n_days=6,output=true)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"The progress bar tells us that the simulation run got the identification \"0001\" (which just counts up, so yours might be higher), meaning that data is stored in the folder /run_0001, so let's plot that data properly (and not just using UnicodePlots).","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"using PythonPlot, NCDatasets\nioff() # hide\nds = NCDataset(\"run_0001/output.nc\")\nds[\"vor\"]","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"Vorticity vor is stored as a lon x lat x vert x time array, we may want to look at the first time step, which is the end of the previous simulation (time=6days) which we didn't store output for.","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"t = 1\nvor = Matrix{Float32}(ds[\"vor\"][:,:,1,t]) # convert from Matrix{Union{Missing,Float32}} to Matrix{Float32}\nlat = ds[\"lat\"][:]\nlon = ds[\"lon\"][:]\n\nfig,ax = subplots(1,1,figsize=(10,6))\nax.pcolormesh(lon,lat,vor')\nax.set_xlabel(\"longitude\")\nax.set_ylabel(\"latitude\")\nax.set_title(\"Relative vorticity\")\ntight_layout() # hide\nsavefig(\"galewsky1.png\") # hide\nnothing # hide","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"(Image: Galewsky jet pyplot1)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"You see that in comparison the unicode plot heavily coarse-grains the simulation, well it's unicode after all! And now the last time step, that means time = 12days is","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"t = ds.dim[\"time\"]\nvor = Matrix{Float32}(ds[\"vor\"][:,:,1,t])\nax.pcolormesh(lon,lat,vor')\nsavefig(\"galewsky2.png\") # hide\nnothing # hide","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"(Image: Galewsky jet pyplot2)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"The jet broke up into many small eddies, but the turbulence is still confined to the northern hemisphere, cool! How this may change when we add mountains (we had NoOrography above!), say Earth's orography, you may ask? Let's try it out! We create an EarthOrography struct like so","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"orography = EarthOrography(spectral_grid)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"It will read the orography from file as shown, and there are some smoothing options too, but let's not change them. Same as before, create a model, initialize into a simulation, run. This time directly for 12 days so that we can compare with the last plot","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"model = ShallowWaterModel(;spectral_grid, orography, initial_conditions)\nsimulation = initialize!(model)\nrun!(simulation,n_days=12,output=true)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"This time the run got a new run id, which you see in the progress bar, but can also always check after the run! call (the automatic run id is only determined just before the main time loop starts) with model.output.id, but otherwise we do as before.","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"id = model.output.id","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"ds = NCDataset(\"run_$id/output.nc\")\ntime = 49\nvor = Matrix{Float32}(ds[\"vor\"][:,:,1,time])\n\nfig,ax = subplots(1,1,figsize=(10,6))\nax.pcolormesh(lon,lat,vor')\nax.set_xlabel(\"longitude\")\nax.set_ylabel(\"latitude\")\nax.set_title(\"Relative vorticity\")\ntight_layout() # hide\nsavefig(\"galewsky3.png\") # hide\nnothing # hide","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"(Image: Galewsky jet pyplot3)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"Interesting! The initial conditions have zero velocity in the southern hemisphere, but still, one can see some imprint of the orography on vorticity. You can spot the coastline of Antarctica; the Andes and Greenland are somewhat visible too. Mountains also completely changed the flow after 12 days, probably not surprising!","category":"page"},{"location":"setups/#Polar-jet-streams-in-shallow-water","page":"Model setups","title":"Polar jet streams in shallow water","text":"","category":"section"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"Setup script:","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=63,nlev=1)\nforcing = JetStreamForcing(spectral_grid,latitude=60)\ndrag = QuadraticDrag(spectral_grid)\noutput = OutputWriter(spectral_grid,ShallowWater,output_dt=6,output_vars=[:u,:v,:pres,:orography])\nmodel = ShallowWaterModel(;spectral_grid,output,drag,forcing)\nsimulation = initialize!(model)\nmodel.feedback.verbose = false # hide\nrun!(simulation,n_days=20) # discard first 20 days \nrun!(simulation,n_days=20,output=true)\nnothing # hide","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"We want to simulate polar jet streams in the shallow water model. We add a JetStreamForcing that adds momentum at 60˚N to inject kinetic energy into the model. This energy needs to be removed (the diffusion is likely not sufficient) through a drag, we have implemented a QuadraticDrag and use the default drag coefficient. Outputting uveta (called :pres, as it is the pressure equivalent in the shallow water system) we run 20 days without output to give the system some time to adapt to the forcing. And visualize zonal wind after another 20 days with","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"using PythonPlot, NCDatasets\nioff() # hide\n\nid = model.output.id\nds = NCDataset(\"run_$id/output.nc\")\ntimestep = ds.dim[\"time\"]\nu = Matrix{Float32}(ds[\"u\"][:,:,1,timestep])\nlat = ds[\"lat\"][:]\nlon = ds[\"lon\"][:]\n\nfig,ax = subplots(1,1,figsize=(10,6))\nq = ax.pcolormesh(lon,lat,u')\nax.set_xlabel(\"longitude\")\nax.set_ylabel(\"latitude\")\nax.set_title(\"Zonal wind [m/s]\")\ncolorbar(q,ax=ax)\ntight_layout() # hide\nsavefig(\"polar_jets.png\") # hide\nnothing # hide","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"(Image: Polar jets pyplot)","category":"page"},{"location":"setups/#Gravity-waves-on-the-sphere","page":"Model setups","title":"Gravity waves on the sphere","text":"","category":"section"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"Setup script:","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"using Random # hide\nRandom.seed!(1234) # hide\nusing SpeedyWeather\nspectral_grid = SpectralGrid(trunc=127,nlev=1)\ntime_stepping = SpeedyWeather.Leapfrog(spectral_grid,Δt_at_T31=30)\nimplicit = SpeedyWeather.ImplicitShallowWater(spectral_grid,α=0.5)\norography = EarthOrography(spectral_grid,smoothing=false)\ninitial_conditions = SpeedyWeather.RandomWaves()\noutput = OutputWriter(spectral_grid,ShallowWater,output_dt=12,output_vars=[:u,:pres,:div,:orography])\nmodel = ShallowWaterModel(;spectral_grid,orography,output,initial_conditions,implicit,time_stepping)\nsimulation = initialize!(model)\nmodel.feedback.verbose = false # hide\nrun!(simulation,n_days=2,output=true)\nnothing # hide","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"How are gravity waves propagating around the globe? We want to use the shallow water model to start with some random perturbations of the interface displacement (the \"sea surface height\") but zero velocity and let them propagate around the globe. We set the alpha parameter of the semi-implicit time integration to 05 to have a centred implicit scheme which dampens the gravity waves less than a backward implicit scheme would do. But we also want to keep orography, and particularly no smoothing on it, to have the orography as rough as possible. The initial conditions are set to RandomWaves which set the spherical harmonic coefficients of eta to between given wavenumbers to some random values","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"SpeedyWeather.RandomWaves()","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"so that the amplitude A is as desired, here 2000m. Our layer thickness is by default","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"model.atmosphere.layer_thickness","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"8.5km so those waves are with an amplitude of 2000m quite strong. But the semi-implicit time integration can handle that even with fairly large time steps of","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"model.time_stepping.Δt_sec","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"seconds. Note that the gravity wave speed here is sqrtgH so almost 300m/s. Let us also output divergence, as gravity waves are quite pronounced in that variable. But given the speed of gravity waves we don't have to integrate for long. Visualise with","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"using PythonPlot, NCDatasets\nioff() # hide\n\nid = model.output.id\nds = NCDataset(\"run_$id/output.nc\")\ntimestep = ds.dim[\"time\"]\ndiv = Matrix{Float32}(ds[\"div\"][:,:,1,timestep])\nlat = ds[\"lat\"][:]\nlon = ds[\"lon\"][:]\n\nfig,ax = subplots(1,1,figsize=(10,6))\nax.pcolormesh(lon,lat,div')\nax.set_xlabel(\"longitude\")\nax.set_ylabel(\"latitude\")\nax.set_title(\"Divergence\")\ntight_layout() # hide\nsavefig(\"gravity_waves.png\") # hide\nnothing # hide","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"(Image: Gravity waves pyplot)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"Can you spot the Himalayas or the Andes?","category":"page"},{"location":"setups/#Jablonowski-Williamson-baroclinic-wave","page":"Model setups","title":"Jablonowski-Williamson baroclinic wave","text":"","category":"section"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=31,nlev=8,Grid=FullGaussianGrid,dealiasing=3)\norography = ZonalRidge(spectral_grid)\ninitial_conditions = ZonalWind()\nmodel = PrimitiveDryModel(;spectral_grid,orography,initial_conditions,physics=false)\nsimulation = initialize!(model)\nmodel.feedback.verbose = false # hide\nrun!(simulation,n_days=9,output=true)\nnothing # hide","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"The Jablonowski-Williamson baroclinic wave test case[JW06] using the Primitive equation model particularly the dry model, as we switch off all physics with physics=false. We want to use 8 vertical levels, and a lower resolution of T31 on a full Gaussian grid. The Jablonowski-Williamson initial conditions are in ZonalWind, the orography is just a ZonalRidge. There is no forcing and the initial conditions are baroclinically unstable which kicks off a wave propagating eastward. This wave becomes obvious when visualised with","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"using PythonPlot, NCDatasets\nioff() # hide\n\nid = model.output.id\nds = NCDataset(\"run_$id/output.nc\")\ntimestep = ds.dim[\"time\"]\nsurface = ds.dim[\"lev\"]\nvor = Matrix{Float32}(ds[\"vor\"][:,:,surface,timestep])\nlat = ds[\"lat\"][:]\nlon = ds[\"lon\"][:]\n\nfig,ax = subplots(1,1,figsize=(10,6))\nax.pcolormesh(lon,lat,vor')\nax.set_xlabel(\"longitude\")\nax.set_ylabel(\"latitude\")\nax.set_title(\"Surface relative vorticity\")\ntight_layout() # hide\nsavefig(\"jablonowski.png\") # hide\nnothing # hide","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"(Image: Jablonowski pyplot)","category":"page"},{"location":"setups/#References","page":"Model setups","title":"References","text":"","category":"section"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"[G04]: Galewsky, Scott, Polvani, 2004. An initial-value problem for testing numerical models of the global shallow-water equations, Tellus A. DOI: 10.3402/tellusa.v56i5.14436","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"[JW06]: Jablonowski, C. and Williamson, D.L. (2006), A baroclinic instability test case for atmospheric model dynamical cores. Q.J.R. Meteorol. Soc., 132: 2943-2975. 10.1256/qj.06.12","category":"page"},{"location":"shallowwater/#Shallow-water-model","page":"Shallow water model","title":"Shallow water model","text":"","category":"section"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"The shallow water model describes the evolution of a 2D flow described by its velocity and an interface height that conceptually represents pressure. A divergent flow affects the interface height which in turn can impose a pressure gradient force onto the flow. The dynamics include advection, forces, dissipation, and continuity.","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"The following description of the shallow water model largely follows the idealized models with spectral dynamics developed at the Geophysical Fluid Dynamics Laboratory[1]: The Shallow Water Equations[2].","category":"page"},{"location":"shallowwater/#Shallow-water-equations","page":"Shallow water model","title":"Shallow water equations","text":"","category":"section"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"The shallow water equations of velocity mathbfu = (uv) and interface height eta (i.e. the deviation from the fluid's rest height H) are, formulated in terms of relative vorticity zeta = nabla times mathbfu, divergence mathcalD = nabla cdot mathbfu","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"beginaligned\nfracpartial zetapartial t + nabla cdot (mathbfu(zeta + f)) =\nF_zeta + nabla times mathbfF_mathbfu + (-1)^n+1nunabla^2nzeta \nfracpartial mathcalDpartial t - nabla times (mathbfu(zeta + f)) =\nF_mathcalD + nabla cdot mathbfF_mathbfu\n-nabla^2(tfrac12(u^2 + v^2) + geta) + (-1)^n+1nunabla^2nmathcalD \nfracpartial etapartial t + nabla cdot (mathbfuh) = F_eta\nendaligned","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"We denote time t, Coriolis parameter f, hyperdiffusion (-1)^n+1 nu nabla^2n (n is the hyperdiffusion order, see Horizontal diffusion), gravitational acceleration g, dynamic layer thickness h, and a forcing for the interface height F_eta. In the shallow water model the dynamics layer thickness h is","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"h = eta + H - H_b","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"that is, the layer thickness at rest H plus the interface height eta minus orography H_b. We also add various possible forcing terms F_zeta F_mathcalD F_eta mathbfF_mathbfu = (F_uF_v) which can be defined to force the respective variables, see Extending SpeedyWeather. ","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"In the shallow water system the flow can be described through uv or zetamathcalD which are related through the stream function Psi and the velocity potential Phi (which is zero in the Barotropic vorticity equation).","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"beginaligned\nzeta = nabla^2 Psi \nmathcalD = nabla^2 Phi \nmathbfu = nabla^perp Psi + nabla Phi\nendaligned","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"With nabla^perp being the rotated gradient operator, in cartesian coordinates xy: nabla^perp = (-partial_y partial_x). See Derivatives in spherical coordinates for further details. Especially because the inversion of the Laplacian and the gradients of Psi Phi can be computed in a single pass, see U,V from vorticity and divergence.","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"The divergence/curl of the vorticity flux mathbfu(zeta + f) are combined with the divergence/curl of the forcing vector mathbfF, as","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"beginaligned\n- nabla cdot (mathbfu(zeta + f)) + nabla times mathbfF =\nnabla times (mathbfF + mathbfu_perp(zeta + f)) \nnabla times (mathbfu(zeta + f)) + nabla cdot mathbfF =\nnabla cdot (mathbfF + mathbfu_perp(zeta + f))\nendaligned","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"equivalently to how this is done in the Barotropic vorticity equation with mathbfu_perp = (v-u).","category":"page"},{"location":"shallowwater/#Algorithm","page":"Shallow water model","title":"Algorithm","text":"","category":"section"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"0. Start with initial conditions of relative vorticity zeta_lm, divergence D_lm, and interface height eta_lm in spectral space and transform this model state to grid-point space:","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"Invert the Laplacian of zeta_lm to obtain the stream function Psi_lm in spectral space\nInvert the Laplacian of D_lm to obtain the velocity potential Phi_lm in spectral space\nobtain velocities U_lm = (cos(theta)u)_lm V_lm = (cos(theta)v)_lm from nabla^perpPsi_lm + nablaPhi_lm\nTransform velocities U_lm, V_lm to grid-point space UV\nUnscale the cos(theta) factor to obtain uv\nTransform zeta_lm, D_lm, eta_lm to zeta D eta in grid-point space","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"Now loop over","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"Compute the forcing (or drag) terms F_zeta F_mathcalD F_eta mathbfF_mathbfu\nMultiply uv with zeta+f in grid-point space\nAdd A = F_u + v(zeta + f) and B = F_v - u(zeta + f)\nTransform these vector components to spectral space A_lm, B_lm\nCompute the curl of (AB)_lm in spectral space and add to forcing of zeta_lm\nCompute the divergence of (AB)_lm in spectral space and add to forcing of divergence mathcalD_lm\nCompute the kinetic energy frac12(u^2 + v^2) and transform to spectral space\nAdd to the kinetic energy the \"geopotential\" geta_lm in spectral space to obtain the Bernoulli potential\nTake the Laplacian of the Bernoulli potential and subtract from the divergence tendency\nCompute the volume fluxes uhvh in grid-point space via h = eta + H - H_b\nTransform to spectral space and take the divergence for -nabla cdot (mathbfuh). Add to forcing for eta.\nCorrect the tendencies following the semi-implicit time integration to prevent fast gravity waves from causing numerical instabilities\nCompute the horizontal diffusion based on the zetamathcalD tendencies\nCompute a leapfrog time step as described in Time integration with a Robert-Asselin and Williams filter\nTransform the new spectral state of zeta_lm, mathcalD_lm, eta_lm to grid-point uvzetamathcalDeta as described in 0.\nPossibly do some output\nRepeat from 1.","category":"page"},{"location":"shallowwater/#implicit_swm","page":"Shallow water model","title":"Semi-implicit time integration","text":"","category":"section"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"Probably the biggest advantage of a spectral model is its ability to solve (parts of) the equations implicitly a low computational cost. The reason is that a linear operator can be easily inverted in spectral space, removing the necessity to solve large equation systems. An operation like Psi = nabla^-2zeta in grid-point space is costly because it requires a global communication, coupling all grid points. In spectral space nabla^2 is a diagonal operator, meaning that there is no communication between harmonics and its inversion is therefore easily done on a mode-by-mode basis of the harmonics.","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"This can be made use of when facing time stepping constraints with explicit schemes, where ridiculously small time steps to resolve fast waves would otherwise result in a horribly slow simulation. In the shallow water system there are gravity waves that propagate at a wave speed of sqrtgH (typically 300m/s), which, in order to not violate the CFL criterion for explicit time stepping, would need to be resolved. Therefore, treating the terms that are responsible for gravity waves implicitly would remove that time stepping constraint and allows us to run the simulation at the time step needed to resolve the advective motion of the atmosphere, which is usually one or two orders of magnitude longer than gravity waves.","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"In the following we will describe how the semi implicit time integration can be combined with the Leapfrog time stepping and the Robert-Asselin and Williams filter for a large increase in numerical stability with gravity waves. Let V_i be the model state of all prognostic variables at time step i, the leapfrog time stepping is then","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"fracV_i+1 - V_i-12Delta t = N(V_i)","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"with the right-hand side operator N evaluated at the current time step i. Now the idea is to split the terms in N into non-linear terms that are evaluated explicitly in N_E and into the linear terms N_I, solved implicitly, that are responsible for the gravity waves. Linearization happens around a state of rest without orography.","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"We could already assume to evaluate N_I at i+1, but in fact, we can introduce alpha in 01 so that for alpha=0 we use i-1 (i.e. explicit), for alpha=12 it is centred implicit tfrac12N_I(V_i-1) + tfrac12N_I(V_i+1), and for alpha=1 a fully backwards scheme N_I(V_i+1) evaluated at i+1.","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"fracV_i+1 - V_i-12Delta t = N_E(V_i) + alpha N_I(V_i+1) + (1-alpha)N_I(V_i-1)","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"Let delta V = tfracV_i+1 - V_i-12Delta t be the tendency we need for the Leapfrog time stepping. Introducing xi = 2alphaDelta t we have","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"delta V = N_E(V_i) + N_I(V_i-1) + xi N_I(delta V)","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"because N_I is a linear operator. This is done so that we can solve for delta V by inverting N_I, but let us gather the other terms as G first.","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"G = N_E(V_i) + N_I(V_i-1) = N(V_i) + N_I(V_i-1 - V_i)","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"For the shallow water equations we will only make use of the last formulation, meaning we first evaluate the whole right-hand side N(V_i) at the current time step as we would do with fully explicit time stepping but then add the implicit terms N_I(V_i-1 - V_i) afterwards to move those terms from i to i-1. Note that we could also directly evaluate the implicit terms at i-1 as it is suggested in the previous formulation N_E(V_i) + N_I(V_i-1), the result would be the same. But in general it can be more efficient to do it one or the other way, and in fact it is also possible to combine both ways. This will be discussed in the semi-implicit time stepping for the primitive equations.","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"We can now implicitly solve for delta V by","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"delta V = (1-xi N_I)^-1G","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"So what is N_I? In the shallow water system the gravity waves are caused by","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"beginaligned\nfracpartial mathcalDpartial t = -gnabla^2eta \nfracpartial etapartial t = -HmathcalD\nendaligned","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"which is a linearization of the equations around a state of rest with uniform constant layer thickness h = H. The continuity equation with the -nabla(mathbfuh) term, for example, is linearized to -nabla(mathbfuH) = -HmathcalD. The divergence and continuity equations can now be written following the delta V = G + xi N_I(delta V) formulation from above as a coupled system (The vorticity equation is zero for the linear gravity wave equation in the shallow water equations, hence no semi-implicit correction has to be made to the vorticity tendency).","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"beginaligned\ndelta mathcalD = G_mathcalD - xi g nabla^2 delta eta \ndelta eta = G_mathcaleta - xi H deltamathcalD\nendaligned","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"with","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"beginaligned\nG_mathcalD = N_mathcalD - xi g nabla^2 (eta_i-1 - eta_i) \nG_mathcaleta = N_eta - xi H (mathcalD_i-1 - mathcalD_i)\nendaligned","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"Inserting the second equation into the first, we can first solve for delta mathcalD, and then for delta eta. Reminder that we do this in spectral space to every harmonic independently, so the Laplace operator nabla^2 = -l(l+1) takes the form of its eigenvalue -l(l+1) (normalized to unit sphere, as are the scaled shallow water equations) and its inversion is therefore just the inversion of this scalar.","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"delta D = fracG_mathcalD - xi gnabla^2 G_eta1 - xi^2 H nabla^2 = S^-1(G_mathcalD - xi gnabla^2 G_eta) ","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"Where the last formulation just makes it clear that S = 1 - xi^2 H nabla^2 is the operator to be inverted. delta eta is then obtained via insertion as written above. Equivalently, by adding a superscript l for every degree of the spherical harmonics, we have","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"delta mathcalD^l = fracG_mathcalD^l + xi g l(l+1) G_eta^l1 + xi^2 H l(l+1)","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"The idea of the semi-implicit time stepping is now as follows:","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"Evaluate the right-hand side explicitly at time step i to obtain the explicit, preliminary tendencies N_mathcalDN_eta (and N_zeta without a need for semi-implicit correction)\nMove the implicit terms from i to i-1 when calculating G_mathcalD G_eta\nSolve for delta mathcalD, the new, corrected tendency for divergence.\nWith delta mathcalD obtain delta eta, the new, corrected tendency for eta.\nApply horizontal diffusion as a correction to N_zeta delta mathcalD as outlined in Horizontal diffusion.\nLeapfrog with tendencies that have been corrected for both semi-implicit and diffusion.","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"Some notes on the semi-implicit time stepping","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"The inversion of the semi-implicit time stepping depends on delta t, that means every time the time step changes, the inversion has to be recalculated.\nYou may choose alpha = 12 to dampen gravity waves but initialization shocks still usually kick off many gravity waves that propagate around the sphere for many days.\nWith increasing alpha 12 these waves are also slowed down, such that for alpha = 1 they quickly disappear in several hours.\nUsing the scaled shallow water equations the time step delta t has to be the scaled time step tildeDelta t = delta tR which is divided by the radius R. Then we use the normalized eigenvalues -l(l+1) which also omit the 1R^2 scaling, see scaled shallow water equations for more details.","category":"page"},{"location":"shallowwater/#scaled_swm","page":"Shallow water model","title":"Scaled shallow water equations","text":"","category":"section"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"Similar to the scaled barotropic vorticity equations, SpeedyWeather.jl scales in the shallow water equations. The vorticity and the divergence equation are scaled with R^2, the radius of the sphere squared, but the continuity equation is scaled with R. We also combine the vorticity flux and forcing into a single divergence/curl operation as mentioned in Shallow water equations above","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"beginaligned\nfracpartial tildezetapartial tildet =\ntildenabla times (tildemathbfF + mathbfu_perp(tildezeta + tildef)) +\n(-1)^n+1tildenutildenabla^2ntildezeta \nfracpartial tildemathcalDpartial tildet =\ntildenabla cdot (tildemathbfF + mathbfu_perp(tildezeta + tildef)) -\ntildenabla^2left(tfrac12(u^2 + v^2) + geta right) +\n(-1)^n+1tildenutildenabla^2ntildemathcalD \nfracpartial etapartial tildet =\n- tildenabla cdot (mathbfuh) + tildeF_eta\nendaligned","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"As in the scaled barotropic vorticity equations, one needs to scale the time step, the Coriolis force, the forcing and the diffusion coefficient, but then enjoys the luxury of working with dimensionless gradient operators. As before, SpeedyWeather.jl will scale vorticity and divergence just before the model integration starts and unscale them upon completion and for output. In the semi-implicit time integration we solve an equation that also has to be scaled. It is with radius squared scaling (because it is the tendency for the divergence equation which is also scaled with R^2)","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"R^2 delta D = R^2fracG_mathcalD - xi gnabla^2 G_eta1 - xi^2 H nabla^2","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"As G_eta is only scaled with R we have","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"tildedelta D = fractildeG_mathcalD - tildexi gtildenabla^2 tildeG_eta1 - tildexi^2 H tildenabla^2","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"The R^2 normalizes the Laplace operator in the numerator, but using the scaled G_eta we also scale xi (which is convenient, because the time step within is the one we use anyway). The denominator S does not actually change because xi^2nabla^2 = tildexi^2tildenabla^2 as xi^2 is scaled with 1R^2, but the Laplace operator with R^2. So overall we just have to use the scaled time step tildeDelta t and normalized eigenvalues for tildenabla^2.","category":"page"},{"location":"shallowwater/#References","page":"Shallow water model","title":"References","text":"","category":"section"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"[1]: Geophysical Fluid Dynamics Laboratory, Idealized models with spectral dynamics","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"[2]: Geophysical Fluid Dynamics Laboratory, The Shallow Water Equations.","category":"page"},{"location":"extending/#Extending-SpeedyWeather","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"","category":"section"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Generally, SpeedyWeather is built in a very modular, extensible way. While that sounds fantastic in general, it does not save you from understanding its modular logic before you can extend SpeedyWeather.jl easily yourself. We highly recommend you to read the following sections if you would like to extend SpeedyWeather in some way, but it also gives you a good understanding of how we build SpeedyWeather in the first place. Because in the end there is no difference between internally or externally defined model components. Having said that, there is a question of the Scope of variables meaning that some functions or types require its module to be explicitly named, like SpeedyWeather.some_function instead of just some_function. But that's really it.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Before and especially after reading this section you are welcome to raise an issue about whatever you would like to do with SpeedyWeather. We are happy to help.","category":"page"},{"location":"extending/#logic","page":"Extending SpeedyWeather","title":"SpeedyWeather's modular logic","text":"","category":"section"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Almost every component in SpeedyWeather is implemented in three steps:","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Define a new type,\ndefine its initialization,\nextend a function that defines what it does.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"To be a bit more explicit (Julia always encourages you to think more abstractly, which can be difficult to get started...) we will use the example of defining a new forcing for the Barotropic or ShallowWater models. But the concept is the same whether you want to define a forcing, a drag, or a new parameterization for the primitive equations, etc. In general, you can define a new component also just in a notebook or in the Julia REPL, you do not have to branch off from the repository and write directly into it. However, note the Scope of variables if you define a component externally.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"To define a new forcing type, at the most basic level you would do","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"struct MyForcing <: AbstractForcing\n # define some parameters and work arrays here\n a::Float64\n v::Vector{Float64}\nend","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"In Julia this introduces a new (so-called compound) type that is a subtype of AbstractForcing, we have a bunch of these abstract super types defined and you want to piggy-back on them because of multiple-dispatch. This new type could also be a mutable struct, could have keywords defined with Base.@kwdef and can also be parametric with respect to the number format or grid, but let's skip those details for now. Conceptually you include into the type any parameters (example the float a here) that you may need and especially those that you want to change. This type will get a user-facing interface so that one can quickly create a new forcing but with altered parameters. Generally you should also include any kind of precomputed or work arrays (here a vector v). For example, you want to apply your forcing only in certain parts of the globe? Then you probably want to define a mask here that somehow includes the information of your region. For a more concrete example see Custom forcing: define.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"To define the new type's initialization, at the most basic level you need to extend the initialize! function for this new type","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"function initialize!(forcing::MyForcing,model::ModelSetup)\n # fill in/change any fields of your new forcing here\n forcing.v[1] = 1\n # you can use information from other model components too\n forcing.v[2] = model.planet.gravity\nend","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"This function is called once during model initialisation (which is in fact just the initialisation of all its components, like the forcing here) and it allows you to precompute values or arrays also based on parameters of other model components. Like in this example, we want to use the gravity that is defined in model.planet. If you need a value for gravity in your forcing you could add a gravity field therein, but then if you change planet.gravity this change would not propagate into your forcing! Another example would be to use model.geometry.coslat if you need to use the cosine of latitude for some precomputation, which, however, depends on the resolution and so should not be hardcoded into your forcing.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"As the last step we have to extend the forcing! function which is the function that is called on every step of the time integration. This new method for forcing! needs to have the following function signature","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"function forcing!( diagn::DiagnosticVariablesLayer,\n forcing::MyForcing,\n time::DateTime,\n model::ModelSetup)\n # whatever the forcing is supposed to do, in the end you want\n # to write into the tendency fields\n diagn.tendencies.u_tend_grid[1] = forcing.a\n diagn.tendencies.v_tend_grid[1] = forcing.a\n diagn.tendencies.vor_tend[1] = forcing.a\nend","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"DiagnosticVariablesLayer is the type of the first argument, because it contains the tendencies you will want to change, so this is supposed to be read and write. The other arguments should be treated read-only. You make use of the time or anything else in the model, but the latter likely comes with a performance penalty which is why we often unpack the model in a function barrier. But let's skip that detail for now. Generally, try to precompute what you can in initialize!. For the forcing you will need to force the velocities u,v in grid-point space or the vorticity vor, divergence div in spectral space. This is not a constrain in most applications we came across, but in case it is in yours please reach out.","category":"page"},{"location":"extending/#Scope-of-variables","page":"Extending SpeedyWeather","title":"Scope of variables","text":"","category":"section"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"The above (conceptual!) examples leave out some of the details, particularly around the scope of variables when you want to define a new forcing interactively inside a notebook or the REPL (which is actually the recommended way!!). To respect the scope of variables, a bunch of functions will need their module to be explicit specified. In general, you should be familiar with Julia's scope of variables logic.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Defining a new type for example as a subtype of AbstractForcing, you will actually want to do","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"struct MyForcing <: SpeedyWeather.AbstractForcing\n # fields\nend","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Because AbstractForcing is defined inside SpeedyWeather, but we do not export all functions/types in order to not flood your global scope to avoid naming conflicts. Rule of thumb: If you get an error hinting at something is not defined, make sure you are in the right scope!","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Then the initialize! function is a function inside the SpeedyWeather module, as we want to define a new method for it outside that can be called inside we actually need to write","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"function SpeedyWeather.initialize!(forcing::MyForcing,model::SpeedyWeather.ModelSetup)\n # how to initialize it\nend","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"And similar for SpeedyWeather.forcing!.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"You also probably want to make use of functions that are already defined inside SpeedyWeather or its submodules SpeedyTransforms, or RingGrids. If something does not seem to be defined, although you can see it in the documentation or directly in the code, you probably need to specify its module too! Alternatively, note that you can also always do import SpeedWeather: ModelSetup to bring a given variable into global scope which removes the necessity to write SpeedyWeather.ModelSetup.","category":"page"},{"location":"extending/#Custom-forcing:-define","page":"Extending SpeedyWeather","title":"Custom forcing: define","text":"","category":"section"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"The following example is a bit more concrete than the previous conceptual example, but we try to add a few more details that are important, or you at least should be aware of it. In this example we want to add a StochasticStirring forcing as defined in Vallis et al., 2004","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"beginaligned\nfracpartial zetapartial t + nabla cdot (mathbfu(zeta + f)) =\nS - rzeta - nunabla^4zeta \nS_lm^i = A(1-exp(-2tfracDelta ttau))Q^i_lm + exp(-tfracdttau)S_lm^i-1 \nendaligned","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"So there is a term S that is supposed to force the vorticity equation in the [Barotropic vorticity model]. However, this term is also stochastically evolving in time, meaning we have to store the previous time steps, i-1, in spectral space, because that's where the forcing is defined: for degree l and order m of the spherical harmonics. A is a real amplitude. Delta t the time step of the model, tau the decorrelation time scale of the stochastic process. Q is for every spherical harmonic a complex random uniform number in -11 in both its real and imaginary components. So we actually define our StochasticStirring forcing as follows and will explain the details in second","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"using SpeedyWeather, Dates\n\nBase.@kwdef struct StochasticStirring{NF} <: SpeedyWeather.AbstractForcing{NF}\n \n # DIMENSIONS from SpectralGrid\n \"Spectral resolution as max degree of spherical harmonics\"\n trunc::Int\n \n \"Number of latitude rings, used for latitudinal mask\"\n nlat::Int\n\n \n # OPTIONS\n \"Decorrelation time scale τ [days]\"\n decorrelation_time::Float64 = 2\n\n \"Stirring strength A [1/s²]\"\n strength::Float64 = 1e-11\n\n \"Stirring latitude [˚N]\"\n latitude::Float64 = 45\n\n \"Stirring width [˚]\"\n width::Float64 = 24\n\n \n # TO BE INITIALISED\n \"Stochastic stirring term S\"\n S::LowerTriangularMatrix{Complex{NF}} = zeros(LowerTriangularMatrix{Complex{NF}},trunc+2,trunc+1)\n \n \"a = A*sqrt(1 - exp(-2dt/τ)), the noise factor times the stirring strength [1/s²]\"\n a::Base.RefValue{NF} = Ref(zero(NF))\n \n \"b = exp(-dt/τ), the auto-regressive factor [1]\"\n b::Base.RefValue{NF} = Ref(zero(NF))\n \n \"Latitudinal mask, confined to mid-latitude storm track by default [1]\"\n lat_mask::Vector{NF} = zeros(NF,nlat)\nend","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"So, first the scalar parameters, are added as fields of type Float64 with some default values as suggested in the Vallis et al., 2004 paper. In order to be able to define the default values, we add the Base.@kwdef macro before the struct definition. Then we need the term S as coefficients of the spherical harmonics, which is a LowerTriangularMatrix, however we want its elements to be of number format NF, which is also the parametric type of StochasticStirring{NF}, this is done because it will allow us to use multiple dispatch not just based on StochasticStirring but also based on the number format. Neat. In order to allocate S with some default though we need to know the size of the matrix, which is given by the spectral resolution trunc. So in order to automatically allocate S based on the right size we add trunc as another field, which does not have a default but will be initialised with the help of a SpectralGrid, as explained later. So once we call StochasticStirring{NF}(trunc=31) then S will automatically have the right size.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Then we also see in the definition of S that there are prefactors A(1-exp(-2tfracDelta ttau)) which depend on the forcing's parameters but also on the time step, which, at the time of the creation of StochasticStirring we might not know about! And definitely do not want to hardcode in. So to illustrate what you can do in this case we define two additional parameters a,b that are just initialized as zero, but that will be precalculated in the initialize! function. However, we decided to define our struct as immutable (meaning you cannot change it after creation unless its elements have mutable fields, like the elements in vectors). In order to make it mutable, we could write mutable struct instead, or as outlined here use RefValues. Another option would be to just recalculate a,b in forcing! on every time step. Depending on exactly what you would like to do, you can choose your way. Anyway, we decide to include a,b as RefValues so that we can always access the scalar underneath with a[] and b[] and also change it with a[] = 1 etc.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Lastly, the Vallis et al., 2004 paper also describes how the forcing is not supposed to be applied everywhere on the globe but only over a range of latitudes, meaning we want to scale down certain latitudes with a factor approaching zero. For this we want to define a latitudinal mask lat_mask that is a vector of length nlat, the number of latitude rings. Similar to S, we want to allocate it with zeros (or any other value for that matter), but then precompute this mask in the initialize! step. For this we need to know nlat at creation time meaning we add this field similar as to how we added trunc. This mask requires the parameters latitude and a width which are therefore also added to the definition of StochasticStirring.","category":"page"},{"location":"extending/#Custom-forcing:-generator-function","page":"Extending SpeedyWeather","title":"Custom forcing: generator function","text":"","category":"section"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Cool. Now you could create our new StochasticStirring forcing with StochasticStirring{NF}(trunc=31,nlat=48), and the default values would be chosen as well as the correct size of the arrays S and lat_mask we need. Furthermore, note that because StochasticStirring{NF} is parametric on the number format NF, these arrays are also allocated with the correct number format that will be used throughout model integration.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"But in SpeedyWeather we typically use the SpectralGrid object to pass on the information of the resolution (and number format) so we want a generator function like","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"function StochasticStirring(SG::SpectralGrid;kwargs...)\n (;trunc,Grid,nlat_half) = SG\n nlat = RingGrids.get_nlat(Grid,nlat_half)\n return StochasticStirring{SG.NF}(;trunc,nlat,kwargs...)\nend\nnothing # hide","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Which allows us to do","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"julia> spectral_grid = SpectralGrid(trunc=42,nlev=1)\njulia> stochastic_stirring = StochasticStirring(spectral_grid,latitude=30,decorrelation_time=5)","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"So the respective resolution parameters and the number format are just pulled from the SpectralGrid as a first argument and the remaining parameters are just keyword arguments that one can change at creation. This evolved as a SpeedyWeather convention: The first argument of the generating function to a model component is a SpectralGrid and other keyword arguments specific to that component follow.","category":"page"},{"location":"extending/#Custom-forcing:-initialize","page":"Extending SpeedyWeather","title":"Custom forcing: initialize","text":"","category":"section"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Now let us have a closer look at the details of the initialize! function, in our example we would actually do","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"function SpeedyWeather.initialize!( forcing::StochasticStirring,\n model::SpeedyWeather.ModelSetup)\n \n # precompute forcing strength, scale with radius^2 as is the vorticity equation\n (;radius) = model.spectral_grid\n A = radius^2 * forcing.strength\n \n # precompute noise and auto-regressive factor, packed in RefValue for mutability\n dt = model.time_stepping.Δt_sec\n τ = forcing.decorrelation_time*24*3600 # convert to seconds\n forcing.a[] = A*sqrt(1 - exp(-2dt/τ))\n forcing.b[] = exp(-dt/τ)\n \n # precompute the latitudinal mask\n (;Grid,nlat_half) = model.spectral_grid\n latd = RingGrids.get_latd(Grid,nlat_half)\n \n for j in eachindex(forcing.lat_mask)\n # Gaussian centred at forcing.latitude of width forcing.width\n forcing.lat_mask[j] = exp(-(forcing.latitude-latd[j])^2/forcing.width^2*2)\n end\n\n return nothing\nend","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"As we want to add a method for the StochasticStirring to the initialize! function from within SpeedyWeather we add the SpeedyWeather. to add this method in the right Scope of variables. The initialize! function must have that function signature, instance of your new type StochasticStirring first, then the second argument a model of type ModelSetup or, if your forcing (and in general component) only makes sense in a specific model, you could also write model::Barotropic for example, to be more restrictive. Inside the initialize! method we are defining we can use parameters from other components. For example, the definition of the S term includes the time step Delta t, which should be pulled from the model.time_stepping. We also pull the Grid and its resolution parameter nlat_half (see Grids) to get the latitudes with get_latd from the RingGrids module. Alternatively, we could have used model.geometry.latd which is contains a bunch of similar arrays describing the geometry of the grid we use and at its given resolution.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Note that initialize! is expected to be read and write on the forcing argument (hence using Julia's !-notation) but read-only on the model, except for model.forcing which points to the same object. You technically can initialize or generally alter several model components in one, but that not advised and can easily lead to unexpected behaviour because of multiple dispatch.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"As a last note on initialize!, you can see that we scale the amplitude/strength A with the radius squared, this is because the Barotropic vorticity equation are scaled that way, so we have to scale S too.","category":"page"},{"location":"extending/#Custom-forcing:-forcing!-function","page":"Extending SpeedyWeather","title":"Custom forcing: forcing! function","text":"","category":"section"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Now that we have defined how to create and initialize our new StochasticStirring forcing, we need to define what it actually is supposed to do. For this SpeedyWeather will call the forcing! function within a time step. However, this function is not yet defined for our new StochasticStirring forcing. But if you define it as follows then this will be called automatically with multiple dispatch.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"function SpeedyWeather.forcing!(diagn::SpeedyWeather.DiagnosticVariablesLayer,\n forcing::StochasticStirring,\n time::DateTime,\n model::SpeedyWeather.ModelSetup)\n # function barrier only\n forcing!(diagn,forcing,model.spectral_transform)\nend","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"The function has to be as outlined above. The first argument has to be of type DiagnosticVariablesLayer as it acts on a layer of the diagnostic variables, where the current model state in grid-point space and the tendencies (in spectral space) are defined. The second argument has to be of the type of our new forcing, the third argument is time which you may use or skip, the last element is a ModelSetup, but as before you can be more restrictive to define a forcing only for the BarotropicModel for example, use modelBarotropic in that case.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"As you can see, for now not much is actually happening inside this function, this is what is often called a function barrier, the only thing we do in here is to unpack the model to the specific model components we actually need. You can omit this function barrier and jump straight to the definition below, but often this is done for performance reasons: model has several abstract fields which the compiler cannot optimize for, but unpacking them makes that possible. So we define the actual forcing! function that's then called as follows","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"function forcing!( diagn::SpeedyWeather.DiagnosticVariablesLayer,\n forcing::StochasticStirring{NF},\n spectral_transform::SpectralTransform) where NF\n \n # noise and auto-regressive factors\n a = forcing.a[] # = sqrt(1 - exp(-2dt/τ))\n b = forcing.b[] # = exp(-dt/τ)\n \n (;S) = forcing\n for lm in eachindex(S)\n # Barnes and Hartmann, 2011 Eq. 2\n Qi = 2rand(Complex{NF}) - (1 + im) # ~ [-1,1] in complex\n S[lm] = a*Qi + b*S[lm]\n end\n\n # to grid-point space\n S_grid = diagn.dynamics_variables.a_grid\n SpeedyTransforms.gridded!(S_grid,S,spectral_transform)\n \n # mask everything but mid-latitudes\n RingGrids._scale_lat!(S_grid,forcing.lat_mask)\n \n # back to spectral space\n (;vor_tend) = diagn.tendencies\n SpeedyTransforms.spectral!(vor_tend,S_grid,spectral_transform)\n SpeedyTransforms.spectral_truncation!(vor_tend) # set lmax+1 to zero\n \n return nothing\nend\nnothing # hide","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"The function signature can then just match to whatever we need. In our case we have a forcing defined in spectral space which, however, is masked in grid-point space. So we will need the model.spectral_transform. You could recompute the spectral_transform object inside the function but that is inefficient.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Now this is actually where we implement the equation we started from in Custom forcing: define simply by looping over the spherical harmonics in S and updating its entries. Then we transform S into grid-point space using the a_grid work array that is in dynamics_variables, b_grid is another one you can use, so are a,b in spectral space. However, these are really work arrays, meaning you should expect them to be overwritten momentarily once the function concludes and no information will remain. Equivalently, these arrays may have an undefined state prior to the forcing! call. We then use the _scale_lat! function from RingGrids which takes every element in the latitude mask lat_mask and multiplies it with every grid-point on the respective latitude ring.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Now for the last lines we have to know the order in which different terms are written into the tendencies for vorticity, diagn.tendencies.vor_tend. In SpeedyWeather, the forcing! comes first, then the drag! (see Custom drag) then the curl of the vorticity flux (the vorticity advection). This means we can transform S_grid directly back into vor_tend without overwriting other terms which, in fact, will be added to this array afterwards. In general, you can also force the momentum equations in grid-point space by writing into u_tend_grid and v_tend_grid.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Then one last detail is the call to spectral_truncation!. Despite the triangular spectral truncation in SpeedyWeather, the lower triangular matrices for the spherical harmonics are actually of size N+1 times N, because this additional degree (=row) is needed for vector quantities (see One more degree for spectral fields). Here particularly, this means that the last spectral transform will compute the spherical harmonics of this last degree which, however, scalar quantities like vorticity should not make use of. We therefore set this degree to zero with the call to spectral_truncation!, which does exactly that.","category":"page"},{"location":"extending/#Custom-forcing:-model-construction","page":"Extending SpeedyWeather","title":"Custom forcing: model construction","text":"","category":"section"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Now that we have defined a new forcing, how to initialize it and what it is supposed to execute on every time step, we explain how to construct a model with this new forcing. We generally follow other [Model setups], start with the SpectralGrid, use that to get a StochasticStirring forcing instance. This calls the generator function from Custom forcing: generator function. Here we want to stir vorticity not at the default latitude of 45N, but on the southern hemisphere to illustrate how to pass on non-default parameters. We explicitly set the initial_conditions to rest and pass them as well as forcing=stochastic_stirring on to the BarotropicModel constructor. That's it! This is really the beauty of our modular interface that you can create instances of individual model components and just put them together as you like, and as long as you follow some rules.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"spectral_grid = SpectralGrid(trunc=42,nlev=1)\nstochastic_stirring = StochasticStirring(spectral_grid,latitude=-45)\ninitial_conditions = StartFromRest()\nmodel = BarotropicModel(;spectral_grid,initial_conditions,forcing=stochastic_stirring)\nsimulation = initialize!(model)\nrun!(simulation)","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Yay! As you can see the vorticity does something funky on the southern hemisphere but not on the northern, as we do not force there. Awesome! Adding new components other than forcing works surprisingly similar. We briefly discuss how to add a custom drag to illustrate the differences but there are not really many.","category":"page"},{"location":"extending/#Custom-drag","page":"Extending SpeedyWeather","title":"Custom drag","text":"","category":"section"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"From the barotropic vorticity equation in Custom forcing: define we omitted the drag term -rzeta which however can be defined in a strikingly similar way. This section is just to outline some differences.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"SpeedyWeather defines AbstractForcing and AbstractDrag, both are only conceptual supertypes, and in fact you could define a forcing as a subtype of AbstractDrag and vice versa. So for a drag, most straight-forwardly you would do","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"struct MyDrag <: AbstractDrag\n # parameters and arrays\nend","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"then define the initialize! function as before, but extend the method drag! instead of forcing!. The only detail that is important to know is that forcing! is called first, then drag! and then the other tendencies. So if you write into vor_tend like so vor_tend[1] = 1, you will overwrite the previous tendency. For the forcing that does not matter as it is the first one to be called, but for the drag you will want to do vor_tend[1] += 1 instead to accumulate the tendency. Otherwise you would undo the forcing term! Note that this conflict would be avoided if the forcing writes into vor_tend but the drag writes into u_tend_grid.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"In general, these are the fields you can write into for new terms","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"vor_tend in spectral space\ndiv_tend in spectral space (shallow water only)\npres_tend in spectral space (shallow water only)\nu_tend_grid in grid-point space\nv_tend_grid in grid-point space","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"These space restrictions exist because of the way how SpeedyWeather transforms between spaces to obtain tendencies.","category":"page"},{"location":"spectral_transform/#Spherical-Harmonic-Transform","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The following sections outline the implementation of the spherical harmonic transform (in short spectral transform) between the coefficients of the spherical harmonics (the spectral space) and the grid space which can be any of the Implemented grids as defined by RingGrids. This includes the classical full Gaussian grid, a regular longitude-latitude grid called the full Clenshaw grid (FullClenshawGrid), ECMWF's octahedral Gaussian grid[Malardel2016], and HEALPix grids[Gorski2004]. SpeedyWeather.jl's spectral transform module SpeedyTransforms is grid-flexible and can be used with any of these, see Grids.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"info: SpeedyTransforms is a module too!\nSpeedyTransform is the underlying module that SpeedyWeather imports to transform between spectral and grid-point space, which also implements Derivatives in spherical coordinates. You can use this module independently of SpeedyWeather for spectral transforms, see SpeedyTransforms.","category":"page"},{"location":"spectral_transform/#Inspiration","page":"Spherical Harmonic Transform","title":"Inspiration","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The spectral transform implemented by SpeedyWeather.jl follows largely Justin Willmert's CMB.jl and SphericalHarmonicTransforms.jl package and makes use of AssociatedLegendrePolynomials.jl and FFTW.jl for the Fourier transform. Justin described his work in a Blog series [Willmert2020].","category":"page"},{"location":"spectral_transform/#Spherical-harmonics","page":"Spherical Harmonic Transform","title":"Spherical harmonics","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The spherical harmonics Y_lm of degree l and order m over the longitude phi = (02pi) and colatitudes theta = (-pi2pi2), are","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Y_lm(phi theta) = lambda_l^m(sintheta) e^imphi","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"with lambda_l^m being the pre-normalized associated Legendre polynomials, and e^imphi are the complex exponentials (the Fourier modes). Together they form a set of orthogonal basis functions on the sphere. For an interactive visualisation of the spherical harmonics, see here.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"info: Latitudes versus colatitudes\nThe implementation of the spectral transforms in SpeedyWeather.jl uses colatitudes theta = (0pi) (0 at the north pole) but the dynamical core uses latitudes theta = (-pi2pi2) (pi2 at the north pole). Note: We may also use latitudes in the spherical harmonic transform in the future for consistency. ","category":"page"},{"location":"spectral_transform/#synthesis","page":"Spherical Harmonic Transform","title":"Synthesis (spectral to grid)","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The synthesis (or inverse transform) takes the spectral coefficients a_lm and transforms them to grid-point values f(phitheta) (for the sake of simplicity first regarded as continuous). The synthesis is a linear combination of the spherical harmonics Y_lm with non-zero coefficients.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"f(phitheta) = sum_l=0^infty sum_m=-l^l a_lm Y_lm(phitheta)","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"We obtain an approximation with a finite set of a_lm by truncating the series in both degree l and order m somehow. Most commonly, a triangular truncation is applied, such that all degrees after l = l_max are discarded. Triangular because the retained array of the coefficients a_lm looks like a triangle. Other truncations like rhomboidal have been studied[Daley78] but are rarely used since. Choosing l_max also constrains m_max and determines the (horizontal) spectral resolution. In SpeedyWeather.jl this resolution as chosen as trunc when creating the SpectralGrid.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"For f being a real-valued there is a symmetry","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"a_l-m = (-1)^m a^*_l+m","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"meaning that the coefficients at -m and m are the same, but the sign of the real and imaginary component can be flipped, as denoted with the (-1)^m and the complex conjugate a_lm^*. As we are only dealing with real-valued fields anyway, we therefore never have to store the negative orders -m and end up with a lower triangular matrix of size (l_max+1) times (m_max+1) or technically (T+1)^2 where T is the truncation trunc. One is added here because the degree l and order m use 0-based indexing but sizes (and so is Julia's indexing) are 1-based.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"For correctness we want to mention here that vector quantities require one more degree l due to the recurrence relation in the Meridional derivative. Hence for practical reasons all spectral fields are represented as a lower triangular matrix of size (m_max + 2) times (m_max +1). And the scalar quantities would just not make use of that last degree, and its entries would be simply zero. We will, however, for the following sections ignore this and only discuss it again in Meridional derivative.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Another consequence of the symmetry mentioned above is that the zonal harmonics, meaning a_lm=0 have no imaginary component. Because these harmonics are zonally constant, a non-zero imaginary component would rotate them around the Earth's axis, which, well, doesn't actually change a real-valued field. ","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Following the notation of [Willmert2020] we can therefore write the truncated synthesis as","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"f(phitheta) = sum_l=0^l_max sum_m=0^l (2-delta_m0) a_lm Y_lm(phitheta)","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The (2-delta_m0) factor using the Kronecker delta is used here because of the symmetry we have to count both the m-m order pairs (hence the 2) except for the zonal harmonics which do not have a pair.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Another symmetry arises from the fact that the spherical harmonics are either symmetric or anti-symmetric around the Equator. There is an even/odd combination of degrees and orders so that the sign flips like a checkerboard","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Y_lm(phipi-theta) = (-1)^l+mY_lm(phiphi)","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"This means that one only has to compute the Legendre polynomials for one hemisphere and the other one follows with this equality.","category":"page"},{"location":"spectral_transform/#analysis","page":"Spherical Harmonic Transform","title":"Analysis (grid to spectral)","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Starting in grid-point space we can transform a field f(lambdatheta) into the spectral space of the spherical harmonics by","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"a_lm = int_0^2pi int_0^pi f(phitheta) Y_lm(phitheta) sin theta dtheta dphi","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Note that this notation again uses colatitudes theta, for latitudes the sintheta becomes a costheta and the bounds have to be changed accordingly to (-fracpi2fracpi2). A discretization with N grid points at location (phi_itheta_i), indexed by i can be written as [Willmert2020]","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"hata_lm = sum_i f(phi_itheta_i) Y_lm(phi_itheta_i) sin theta_i Deltatheta Deltaphi","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The hat on a just means that it is an approximation, or an estimate of the true a_lm approx hata_lm. We can essentially make use of the same symmetries as already discussed in Synthesis. Splitting into the Fourier modes e^imphi and the Legendre polynomials lambda_l^m(costheta) (which are defined over -11 so the costheta argument maps them to colatitudes) we have","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"hata_lm = sum_j left sum_i f(phi_itheta_j) e^-imphi_i right lambda_lm(theta_j) sin theta_j Deltatheta Deltaphi","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"So the term in brackets can be separated out as long as the latitude theta_j is constant, which motivates us to restrict the spectral transform to grids with iso-latitude rings, see Grids. Furthermore, this term can be written as a fast Fourier transform, if the phi_i are equally spaced on the latitude ring j. Note that the in-ring index i can depend on the ring index j, so that one can have reduced grids, which have fewer grid points towards the poles, for example. Also the Legendre polynomials only have to be computed for the colatitudes theta_j (and in fact only one hemisphere, due to the north-south symmetry discussed in the Synthesis). It is therefore practical and efficient to design a spectral transform implementation for ring grids, but there is no need to hardcode a specific grid.","category":"page"},{"location":"spectral_transform/#Spectral-packing","page":"Spherical Harmonic Transform","title":"Spectral packing","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Spectral packing is the way how the coefficients a_lm of the spherical harmonics of a given spectral field are stored in an array. SpeedyWeather.jl uses the conventional spectral packing of degree l and order m as illustrated in the following image (Cyp, CC BY-SA 3.0, via Wikimedia Commons)","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Every row represents an order l geq 0, starting from l=0 at the top. Every column represents an order m geq 0, starting from m=0 on the left. The coefficients of these spherical harmonics are directly mapped into a matrix a_lm as ","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":" m \nl a_00 \n a_10 a_11 \n a_20 a_12 a_22 \n a_30 a_13 a_23 a_33","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"which is consistently extended for higher degrees and orders. Consequently, all spectral fields are lower-triangular matrices with complex entries. The upper triangle excluding the diagonal are zero. Note that internally vector fields include an additional degree, such that l_max = m_max + 1 (see Derivatives in spherical coordinates for more information). The harmonics with a_l0 (the first column) are also called zonal harmonics as they are constant with longitude phi. The harmonics with a_ll (the main diagonal) are also called sectoral harmonics as they essentially split the sphere into 2l sectors in longitude phi without a zero-crossing in latitude.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"For correctness it is mentioned here that SpeedyWeather.jl uses a LowerTriangularMatrix type to store the spherical harmonic coefficients. By doing so, the upper triangle is actually not explicitly stored and the data technically unravelled into a vector, but this is hidden as much as possible from the user. For more details see LowerTriangularMatrices.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"info: Array indices\nFor a spectral field a note that due to Julia's 1-based indexing the coefficient a_lm is obtained via a[l+1,m+1]. Alternatively, we may index over 1-based l,m but a comment is usually added for clarification.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Fortran SPEEDY does not use the same spectral packing as SpeedyWeather.jl. The alternative packing lm therein uses l=m and m=l-m as summarized in the following table.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"degree l order m l=m m=l-m\n0 0 0 0\n1 0 0 1\n1 1 1 0\n2 0 0 2\n2 1 1 1\n2 2 2 0\n3 0 0 3\n... ... ... ...","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"This alternative packing uses the top-left triangle of a coefficient matrix, and the degrees and orders from above are stored at the following indices","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":" m \nl a_00 a_10 a_20 a_30\n a_11 a_21 a_31 \n a_22 a_32 \n a_33 ","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"This spectral packing is not used in SpeedyWeather.jl but illustrated here for completeness and comparison with Fortran SPEEDY.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"SpeedyWeather.jl uses triangular truncation such that only spherical harmonics with l leq l_max and m leq m_max are explicitly represented. This is usually described as Tm_max, with l_max = m_max (although in vector quantities require one more degree l in the recursion relation of meridional gradients). For example, T31 is the spectral resolution with l_max = m_max = 31. Note that the degree l and order m are mathematically 0-based, such that the corresponding coefficient matrix is of size 32x32.","category":"page"},{"location":"spectral_transform/#Available-horizontal-resolutions","page":"Spherical Harmonic Transform","title":"Available horizontal resolutions","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Technically, SpeedyWeather.jl supports arbitrarily chosen resolution parameter trunc when creating the SpectralGrid that refers to the highest non-zero degree l_max that is resolved in spectral space. SpeedyWeather.jl will always try to choose an easily-Fourier transformable[FFT] size of the grid, but as we use FFTW.jl there is quite some flexibility without performance sacrifice. However, this has traditionally lead to typical resolutions that we also use for testing we therefore recommend to use. They are as follows with more details below","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"trunc nlon nlat Delta x\n31 (default) 96 48 400 km\n42 128 64 312 km\n63 192 96 216 km\n85 256 128 165 km\n127 384 192 112 km\n170 512 256 85 km\n255 768 384 58 km\n341 1024 512 43 km\n511 1536 768 29 km\n682 2048 1024 22 km\n1024 3072 1536 14 km\n1365 4092 2048 11 km","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Some remarks on this table","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"This assumes the default quadratic truncation, you can always adapt the grid resolution via the dealiasing option, see Matching spectral and grid resolution\nnlat refers to the total number of latitude rings, see Grids. With non-Gaussian grids, nlat will be one one less, e.g. 47 instead of 48 rings.\nnlon is the number of longitude points on the Full Gaussian Grid, for other grids there will be at most these number of points around the Equator.\nDelta x is the horizontal resolution. For a spectral model there are many ways of estimating this[Randall2021]. We use here the square root of the average area a grid cell covers, see Effective grid resolution","category":"page"},{"location":"spectral_transform/#Effective-grid-resolution","page":"Spherical Harmonic Transform","title":"Effective grid resolution","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"There are many ways to estimate the effective grid resolution of spectral models[Randall2021]. Some of them are based on the wavelength a given spectral resolution allows to represent, others on the total number of real variables per area. However, as many atmospheric models do represent a considerable amount of physics on the grid (see Parameterizations) there is also a good argument to include the actual grid resolution into this estimate and not just the spectral resolution. We therefore use the average grid cell area to estimate the resolution","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Delta x = sqrtfrac4pi R^2N","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"with N number of grid points over a sphere with radius R. However, we have to acknowledge that this usually gives higher resolution compared to other methods of estimating the effective resolution, see [Randall2021] for a discussion. You may therefore need to be careful to make claims that, e.g. trunc=85 can resolve the atmospheric dynamics at a scale of 165km.","category":"page"},{"location":"spectral_transform/#Derivatives-in-spherical-coordinates","page":"Spherical Harmonic Transform","title":"Derivatives in spherical coordinates","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Horizontal gradients in spherical coordinates are defined for a scalar field A and the latitudes theta and longitudes lambda as","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"nabla A = left(frac1Rcosthetafracpartial Apartial lambda frac1Rfracpartial Apartial theta right)","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"However, the divergence of a vector field mathbfu = (uv) includes additional cos(theta) scalings","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"nabla cdot mathbfu = frac1Rcosthetafracpartial upartial lambda +\nfrac1Rcosthetafracpartial (v costheta)partial theta","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"and similar for the curl","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"nabla times mathbfu = frac1Rcosthetafracpartial vpartial lambda -\nfrac1Rcosthetafracpartial (u costheta)partial theta","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The radius of the sphere (i.e. Earth) is R. The zonal gradient scales with 1cos(theta) as the longitudes converge towards the poles (note that theta describes latitudes here, definitions using colatitudes replace the cos with a sin.)","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Starting with a spectral field of vorticity zeta and divergence mathcalD one can obtain stream function Psi and velocity potential Phi by inverting the Laplace operator nabla^2:","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Psi = nabla^-2zeta quad Phi = nabla^-2mathcalD","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The velocities uv are then obtained from (uv) = nabla^botPsi + nablaPhi following the definition from above and nabla^bot = (-R^-1partial_theta (Rcostheta)^-1partial_lambda)","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"beginaligned\nu = -frac1Rpartial_thetaPsi + frac1Rcosthetapartial_lambdaPhi \nv = +frac1Rpartial_thetaPhi + frac1Rcosthetapartial_lambdaPsi\nendaligned","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"How the operators nabla nabla times nabla cdot can be implemented with spherical harmonics is presented in the following sections. However, note that the actually implemented operators differ slightly in their scaling with respect to the radius R and the cosine of latitude cos(theta). For further details see Gradient operators which describes those as implemented in the SpeedyTransforms module. Also note that the equations in SpeedyWeather.jl are scaled with the radius R^2 (see Radius scaling) which turns most operators into non-dimensional operators on the unit sphere anyway.","category":"page"},{"location":"spectral_transform/#Zonal-derivative","page":"Spherical Harmonic Transform","title":"Zonal derivative","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The zonal derivative of a scalar field Psi in spectral space is the zonal derivative of all its respective spherical harmonics Psi_lm(phitheta) (now we use phi for longitudes to avoid confusion with the Legendre polynomials lambda_lm)","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"v_lm = frac1R cos(theta) fracpartialpartial phi left( lambda_l^m(costheta) e^imphi right) =\nfracimR cos(theta) lambda_l^m(costheta) e^imphi = fracimR cos(theta) Psi_lm","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"So for every spectral harmonic, cos(theta)v_lm is obtained from Psi_lm via a multiplication with imR. Unscaling the cos(theta)-factor is done after transforming the spectral coefficients v_lm into grid-point space. As discussed in Radius scaling, SpeedyWeather.jl scales the stream function as tildePsi = R^-1Psi such that the division by radius R in the gradients can be omitted. The zonal derivative becomes therefore effectively for each spherical harmonic a scaling with its (imaginary) order im. The spherical harmonics are essentially just a Fourier transform in zonal direction and the derivative a multiplication with the respective wave number m times imaginary i.","category":"page"},{"location":"spectral_transform/#Meridional-derivative","page":"Spherical Harmonic Transform","title":"Meridional derivative","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The meridional derivative of the spherical harmonics is a derivative of the Legendre polynomials for which the following recursion relation applies[Randall2021],[Durran2010],[GFDL],[Orszag70]","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"costheta fracdP_lmdtheta = -lepsilon_l+1mP_l+1m + (l+1)epsilon_lmP_l-1m","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"with recursion factors","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"epsilon_lm = sqrtfracl^2-m^24l^2-1","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"In the following we use the example of obtaining the zonal velocity u from the stream function Psi, which is through the negative meridional gradient. For the meridional derivative itself the leading minus sign has to be omitted. Starting with the spectral expansion","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Psi(lambdatheta) = sum_lmPsi_lmP_lm(sintheta)e^imlambda","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"we multiply with -R^-1costhetapartial_theta to obtain","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"costhetaleft(-frac1Rpartial_thetaPsi right) = -frac1Rsum_lmPsi_lme^imlambdacosthetapartial_theta P_lm","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"at which point the recursion from above can be applied. Collecting terms proportional to P_lm then yields","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"(cos(theta)u)_lm = -frac1R(-(l-1)epsilon_lmPsi_l-1m + (l+2)epsilon_l+1mPsi_l+1m)","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"To obtain the coefficient of each spherical harmonic lm of the meridional gradient of a spectral field, two coefficients at l-1m and l+1m have to be combined. This means that the coefficient of a gradient ((costheta) u)_lm is a linear combination of the coefficients of one higher and one lower degree Psi_l+1mPsi_l-1m. As the coefficient Psi_lm with ml are zero, the sectoral harmonics (l=m) of the gradients are obtained from the first off-diagonal only. However, the l=l_max harmonics of the gradients require the l_max-1 as well as the l_max+1 harmonics. As a consequence vector quantities like velocity components uv require one more degree l than scalar quantities like vorticity[Bourke72]. However, for easier compatibility all spectral fields in SpeedyWeather.jl use one more degree l, but scalar quantities should not make use of it. Equivalently, the last degree l is set to zero before the time integration, which only advances scalar quantities.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"In SpeedyWeather.jl, vector quantities like uv use therefore one more meridional mode than scalar quantities such as vorticity zeta or stream function Psi. The meridional derivative in SpeedyWeather.jl also omits the 1R-scaling as explained for the Zonal derivative and in Radius scaling.","category":"page"},{"location":"spectral_transform/#Divergence-and-curl-in-spherical-harmonics","page":"Spherical Harmonic Transform","title":"Divergence and curl in spherical harmonics","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The meridional gradient as described above can be applied to scalars, such as Psi and Phi in the conversion to velocities (uv) = nabla^botPsi + nablaPhi, however, the operators curl nabla times and divergence nabla cdot in spherical coordinates involve a costheta scaling before the meridional gradient is applied. How to translate this to spectral coefficients has to be derived separately[Randall2021],[Durran2010].","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The spectral transform of vorticity zeta is","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"zeta_lm = frac12piint_-tfracpi2^tfracpi2int_0^2pi zeta(lambdatheta)\nP_lm(sintheta) e^imlambda dlambda costheta dtheta","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Given that Rzeta = cos^-1partial_lambda v - cos^-1partial_theta (u costheta), we therefore have to evaluate a meridional integral of the form","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"int P_lm frac1cos theta partial_theta(u costheta) cos theta dtheta","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"which can be solved through integration by parts. As ucostheta = 0 at theta = pm tfracpi2 only the integral","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"= -int partial_theta P_lm (u costheta) dtheta = -int costheta partial_theta P_lm\n(fracucostheta) costheta dtheta","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"remains. Inserting the recurrence relation from the Meridional derivative turns this into","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"= -int left(-l epsilon_l+1mP_l+1m + (l+1)epsilon_lm P_l-1m right) (fracucostheta)\ncos theta dtheta","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Now we expand (tfracucostheta) but only the lm harmonic will project ontoP_lm. Let u^* = ucos^-1theta v^* = vcos^-1theta we then have in total","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"beginaligned\nRzeta_lm = imv^*_lm + (l+1)epsilon_lmu^*_l-1m - lepsilon_l+1mu^*_l+1m \nRD_lm = imu^*_lm - (l+1)epsilon_lmv^*_l-1m + lepsilon_l+1mv^*_l+1m \nendaligned","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"And the divergence D is similar, but (uv) to (-vu). We have moved the scaling with the radius R directly into zetaD as further described in Radius scaling.","category":"page"},{"location":"spectral_transform/#Laplacian","page":"Spherical Harmonic Transform","title":"Laplacian","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The spectral Laplacian is easily applied to the coefficients Psi_lm of a spectral field as the spherical harmonics are eigenfunctions of the Laplace operator nabla^2 in spherical coordinates with eigenvalues -l(l+1) divided by the radius squared R^2, i.e. nabla^2 Psi becomes tfrac-l(l+1)R^2Psi_lm in spectral space. For example, vorticity zeta and stream function Psi are related by zeta = nabla^2Psi in the barotropic vorticity model. Hence, in spectral space this is equivalent for every spectral mode of degree l and order m to","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"zeta_lm = frac-l(l+1)R^2Psi_lm","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"This can be easily inverted to obtain the stream function Psi from vorticity zeta instead. In order to avoid division by zero, we set Psi_00 here, given that the stream function is only defined up to a constant anyway.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"beginaligned\nPsi_lm = fracR^2-l(l+1)zeta_lm quad foralllm 0\nPsi_00 = 0\nendaligned","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"See also Horizontal diffusion and Normalization of diffusion.","category":"page"},{"location":"spectral_transform/#U,V-from-vorticity-and-divergence","page":"Spherical Harmonic Transform","title":"U,V from vorticity and divergence","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"After having discussed the zonal and meridional derivatives with spherical harmonics as well as the Laplace operator, we can derive the conversion from vorticity zeta and divergence D (which are prognostic variables) to U=ucostheta V=vcostheta. Both are linear operations that act either solely on a given harmonic (the zonal gradient and the Laplace operator) or are linear combinations between one lower and one higher degree l (the meridional gradient). It is therefore computationally more efficient to compute UV directly from zetaD instead of calculating stream function and velocity potential first. In total we have","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"beginaligned\nU_lm = -fraciml(l+1)(RD)_lm + fracepsilon_l+1ml+1(Rzeta)_l+1m -\nfracepsilon_lml(Rzeta)_l-1m \nV_lm = -fraciml(l+1)(Rzeta)_lm - fracepsilon_l+1ml+1(RD)_l+1m +\nfracepsilon_lml(RD)_l-1m \nendaligned","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"We have moved the scaling with the radius R directly into zetaD as further described in Radius scaling.","category":"page"},{"location":"spectral_transform/#References","page":"Spherical Harmonic Transform","title":"References","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"[Malardel2016]: Malardel S, Wedi N, Deconinck W, Diamantakis M, Kühnlein C, Mozdzynski G, Hamrud M, Smolarkiewicz P. A new grid for the IFS. ECMWF newsletter. 2016;146(23-28):321. doi: 10.21957/zwdu9u5i","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"[Gorski2004]: Górski, Hivon, Banday, Wandelt, Hansen, Reinecke, Bartelmann, 2004. HEALPix: A FRAMEWORK FOR HIGH-RESOLUTION DISCRETIZATION AND FAST ANALYSIS OF DATA DISTRIBUTED ON THE SPHERE, The Astrophysical Journal. doi:10.1086/427976","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"[Willmert2020]: Justin Willmert, 2020. justinwillmert.comIntroduction to Associated Legendre Polynomials (Legendre.jl Series, Part I)\nCalculating Legendre Polynomials (Legendre.jl Series, Part II)\nPre-normalizing Legendre Polynomials (Legendre.jl Series, Part III)\nMaintaining numerical accuracy in the Legendre recurrences (Legendre.jl Series, Part IV)\nIntroducing Legendre.jl (Legendre.jl Series, Part V)\nNumerical Accuracy of the Spherical Harmonic Recurrence Coefficient (Legendre.jl Series Addendum)\nNotes on Calculating the Spherical Harmonics\nMore Notes on Calculating the Spherical Harmonics: Analysis of maps to harmonic coefficients","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"[Daley78]: Roger Daley & Yvon Bourassa (1978) Rhomboidal versus triangular spherical harmonic truncation: Some verification statistics, Atmosphere-Ocean, 16:2, 187-196, DOI: 10.1080/07055900.1978.9649026","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"[Randall2021]: David Randall, 2021. An Introduction to Numerical Modeling of the Atmosphere, Chapter 22.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"[Durran2010]: Dale Durran, 2010. Numerical Methods for Fluid Dynamics, Springer. In particular section 6.2, 6.4.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"[GFDL]: Geophysical Fluid Dynamics Laboratory, The barotropic vorticity equation.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"[FFT]: Depending on the implementation of the Fast Fourier Transform (Cooley-Tukey algorithm, or or the Bluestein algorithm) easily Fourier-transformable can mean different things: Vectors of the length n that is a power of two, i.e. n = 2^i is certainly easily Fourier-transformable, but for most FFT implementations so are n = 2^i3^j5^k with ijk some positive integers. In fact, FFTW uses O(n log n) algorithms even for prime sizes.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"[Bourke72]: Bourke, W. An Efficient, One-Level, Primitive-Equation Spectral Model. Mon. Wea. Rev. 100, 683–689 (1972). doi:10.1175/1520-0493(1972)100<0683:AEOPSM>2.3.CO;2","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"[Orszag70]: Orszag, S. A., 1970: Transform Method for the Calculation of Vector-Coupled Sums: Application to the Spectral Form of the Vorticity Equation. J. Atmos. Sci., 27, 890–895, 10.1175/1520-0469(1970)027<0890:TMFTCO>2.0.CO;2. ","category":"page"},{"location":"ringgrids/#RingGrids","page":"Submodule: RingGrids","title":"RingGrids","text":"","category":"section"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"RingGrids defines and exports the following grids:","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"full grids: FullClenshawGrid, FullGaussianGrid, FullHEALPix, and FullOctaHEALPix\nreduced grids: OctahedralGaussianGrid, OctahedralClenshawGrid, OctaHEALPixGrid and HEALPixGrid","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"note: What is a ring?\nWe 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.","category":"page"},{"location":"ringgrids/#Creating-data-on-a-RingGrid","page":"Submodule: RingGrids","title":"Creating data on a RingGrid","text":"","category":"section"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"Data in a Matrix which follows this ring order can be put on a FullGaussianGrid like so","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"using SpeedyWeather.RingGrids\nmap = randn(Float32,8,4)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"grid = FullGaussianGrid(map)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"grid.data","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"Which can be reshaped to reobtain map from above. Alternatively you can Matrix(grid) to do this in one step","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"map == Matrix(FullGaussianGrid(map))","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"nlat_half = 4\ngrid = randn(OctahedralGaussianGrid{Float16},nlat_half)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"and any element type T can be used for OctahedralGaussianGrid{T} and similar for other grid types.","category":"page"},{"location":"ringgrids/#Visualising-RingGrid-data","page":"Submodule: RingGrids","title":"Visualising RingGrid data","text":"","category":"section"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"nlat_half = 24\ngrid = randn(OctahedralGaussianGrid,nlat_half)\nplot(grid)","category":"page"},{"location":"ringgrids/#Indexing-RingGrids","page":"Submodule: RingGrids","title":"Indexing RingGrids","text":"","category":"section"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"grid = randn(OctahedralClenshawGrid,5)\nlatd = get_latd(grid)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"Now we could calculate Coriolis and add it on the grid as follows","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"rotation = 7.29e-5 # angular frequency of Earth's rotation [rad/s]\ncoriolis = 2rotation*sind.(latd) # vector of coriolis parameters per latitude ring\n\nrings = eachring(grid)\nfor (j,ring) in enumerate(rings)\n f = coriolis[j]\n for ij in ring\n grid[ij] += f\n end\nend","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"for ij in eachgridpoint(grid)\n grid[ij]\nend","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"or use eachindex instead.","category":"page"},{"location":"ringgrids/#Interpolation-on-RingGrids","page":"Submodule: RingGrids","title":"Interpolation on RingGrids","text":"","category":"section"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"grid = randn(OctahedralGaussianGrid{Float32},4)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"interpolate(FullGaussianGrid,grid)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"interpolate(FullGaussianGrid,6,grid)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"One can also interpolate onto a given coordinate ˚N, ˚E like so","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"interpolate(30.0,10.0,grid)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"interpolate([30.0,40.0,50.0],[10.0,10.0,10.0],grid)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/#Performance-for-RingGrid-interpolation","page":"Submodule: RingGrids","title":"Performance for RingGrid interpolation","text":"","category":"section"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"output vector\ninput grid\ninterpolator","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"grid_in = rand(HEALPixGrid,4)\ngrid_out = zeros(FullClenshawGrid,6)\ninterp = RingGrids.interpolator(grid_out,grid_in)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"interpolate!(grid_out,grid_in,interp)\ngrid_out","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"grid_out = zeros(FullClenshawGrid{Float16},6);\ninterpolate!(grid_out,grid_in,interp)\ngrid_out","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"grid_in = randn(OctahedralGaussianGrid{Float16},24)\ngrid_out = zeros(FullClenshawGrid{Float16},24)\ninterp = RingGrids.interpolator(Float32,grid_out,grid_in)\ninterpolate!(grid_out,grid_in,interp)\ngrid_out","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"npoints = 10 # number of coordinates to interpolate onto\ninterp = AnvilInterpolator(Float32,HEALPixGrid,24,npoints)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"latds = collect(0.0:5.0:45.0)\nlonds = collect(-10.0:2.0:8.0)\nnothing # hide","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"now we can update the locator inside our interpolator as follows","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"RingGrids.update_locator!(interp,latds,londs)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"With data matching the input from above, a nlat_half=24 HEALPixGrid, and allocate 10-element output vector","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"output_vec = zeros(10)\ngrid_input = rand(HEALPixGrid,24)\nnothing # hide","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"we can use the interpolator as follows","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"interpolate!(output_vec,grid_input,interp)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"which is the approximately the same as doing it directly without creating an interpolator first and updating its locator","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"interpolate(latds,londs,grid_input)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/#Anvil-interpolator","page":"Submodule: RingGrids","title":"Anvil interpolator","text":"","category":"section"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":" 0..............1 # fraction of distance Δab between a,b\n |< Δab >|\n\n0^ a -------- o - b # anvil-shaped average of a,b,c,d at location x\n.Δy |\n. |\n.v x \n. |\n1 c ------ o ---- d\n\n |< Δcd >|\n 0...............1 # fraction of distance Δcd between c,d\n\n^ fraction of distance Δy between a-b and c-d.","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/#Function-index","page":"Submodule: RingGrids","title":"Function index","text":"","category":"section"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"Modules = [SpeedyWeather.RingGrids]","category":"page"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractFullGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.AbstractFullGrid","text":"abstract type AbstractFullGrid{T} <: AbstractGrid{T} end\n\nAn 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.AbstractGrid","text":"abstract type AbstractGrid{T} <: AbstractVector{T} end\n\nThe abstract supertype for all spatial grids on the sphere supported by SpeedyWeather.jl. Every new grid has to be of the form\n\nabstract type AbstractGridClass{T} <: AbstractGrid{T} end\nstruct MyNewGrid{T} <: AbstractGridClass{T}\n data::Vector{T} # all grid points unravelled into a vector\n nlat_half::Int # resolution: latitude rings on one hemisphere (Equator incl)\nend\n\nMyNewGrid 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)\n\nFundamental 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\n\nIndexing 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\n\nCoordinates getcolat # vector of colatitudes (radians) getcolatlon # vectors of colatitudes, longitudes (both radians)\n\nSpectral 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\n\nQuadrature 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\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractHEALPixGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.AbstractHEALPixGrid","text":"abstract type AbstractHEALPixGrid{T} <: AbstractGrid{T} end\n\nAn AbstractHEALPixGrid is a horizontal grid similar to the standard HEALPixGrid, but different latitudes can be used, the default HEALPix latitudes or others.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractInterpolator","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.AbstractInterpolator","text":"abstract type AbstractInterpolator{NF,G} end\n\nSupertype for Interpolators. Every Interpolator <: AbstractInterpolator is expected to have two fields,\n\ngeometry, which describes the grid G to interpolate from\nlocator, which locates the indices on G and their weights to interpolate onto a new grid.\n\nNF 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractLocator","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.AbstractLocator","text":"AbstractLocator{NF}\n\nSupertype 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractOctaHEALPixGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.AbstractOctaHEALPixGrid","text":"abstract type AbstractOctaHEALPixGrid{T} <: AbstractGrid{T} end\n\nAn 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractOctahedralGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.AbstractOctahedralGrid","text":"abstract type AbstractOctahedralGrid{T} <: AbstractGrid{T} end\n\nAn 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AnvilLocator","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.AnvilLocator","text":"AnvilLocator{NF<:AbstractFloat} <: AbtractLocator\n\nContains 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AnvilLocator-Union{Tuple{Integer}, Tuple{NF}} where NF<:AbstractFloat","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.AnvilLocator","text":"L = AnvilLocator( ::Type{NF}, # number format used for the interpolation\n npoints::Integer # number of points to interpolate onto\n ) where {NF<:AbstractFloat}\n\nZero 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.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.FullClenshawGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.FullClenshawGrid","text":"G = FullClenshawGrid{T}\n\nA 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.FullGaussianGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.FullGaussianGrid","text":"G = FullGaussianGrid{T}\n\nA 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.FullHEALPixGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.FullHEALPixGrid","text":"G = FullHEALPixGrid{T}\n\nA 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.FullOctaHEALPixGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.FullOctaHEALPixGrid","text":"G = FullOctaHEALPixGrid{T}\n\nA 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.GridGeometry","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.GridGeometry","text":"GridGeometry{G<:AbstractGrid}\n\ncontains general precomputed arrays describing the grid of G.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.GridGeometry-Tuple{Type{<:SpeedyWeather.RingGrids.AbstractGrid}, Integer}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.GridGeometry","text":"G = GridGeometry( Grid::Type{<:AbstractGrid},\n nlat_half::Integer)\n\nPrecomputed 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.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.HEALPixGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.HEALPixGrid","text":"H = HEALPixGrid{T}\n\nA 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.OctaHEALPixGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.OctaHEALPixGrid","text":"H = OctaHEALPixGrid{T}\n\nA 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.OctahedralClenshawGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.OctahedralClenshawGrid","text":"G = OctahedralClenshawGrid{T}\n\nAn 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.OctahedralGaussianGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.OctahedralGaussianGrid","text":"G = OctahedralGaussianGrid{T}\n\nAn 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.Matrix!-Tuple{AbstractMatrix, OctaHEALPixGrid}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.Matrix!","text":"Matrix!(M::AbstractMatrix,\n G::OctaHEALPixGrid;\n quadrant_rotation=(0,1,2,3),\n matrix_quadrant=((2,2),(1,2),(1,1),(2,1)),\n )\n\nSorts 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.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.Matrix!-Tuple{AbstractMatrix, OctahedralClenshawGrid}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.Matrix!","text":"Matrix!(M::AbstractMatrix,\n G::OctahedralClenshawGrid;\n quadrant_rotation=(0,1,2,3),\n matrix_quadrant=((2,2),(1,2),(1,1),(2,1)),\n )\n\nSorts 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.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.Matrix!-Union{Tuple{Vararg{Tuple{AbstractMatrix{T}, OctaHEALPixGrid}}}, Tuple{T}} where T","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.Matrix!","text":"Matrix!(MGs::Tuple{AbstractMatrix{T},OctaHEALPixGrid}...;kwargs...)\n\nLike 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.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.Matrix!-Union{Tuple{Vararg{Tuple{AbstractMatrix{T}, OctahedralClenshawGrid}}}, Tuple{T}} where T","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.Matrix!","text":"Matrix!(MGs::Tuple{AbstractMatrix{T},OctahedralClenshawGrid}...;kwargs...)\n\nLike 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.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids._scale_lat!-Union{Tuple{NF}, Tuple{SpeedyWeather.RingGrids.AbstractGrid{NF}, AbstractVector}} where NF","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids._scale_lat!","text":"_scale_lat!(\n A::SpeedyWeather.RingGrids.AbstractGrid{NF},\n v::AbstractVector\n) -> SpeedyWeather.RingGrids.AbstractGrid\n\n\nGeneric latitude scaling applied to A in-place with latitude-like vector v.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.anvil_average-NTuple{7, Any}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.anvil_average","text":"anvil_average(a, b, c, d, Δab, Δcd, Δy) -> Any\n\n\nThe 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:\n\n 0..............1 # fraction of distance Δab between a,b\n |< Δab >|\n\n 0^ a -------- o - b # anvil-shaped average of a,b,c,d at location x\n .Δy |\n . |\n .v x \n . |\n 1 c ------ o ---- d\n\n |< Δcd >|\n 0...............1 # fraction of distance Δcd between c,d\n\n^ fraction of distance Δy between a-b and c-d.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.average_on_poles-Union{Tuple{NF}, Tuple{SpeedyWeather.RingGrids.AbstractGrid{NF}, Vector{<:UnitRange{<:Integer}}}} where NF<:AbstractFloat","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.average_on_poles","text":"average_on_poles(\n A::SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n rings::Vector{<:UnitRange{<:Integer}}\n) -> Tuple{Any, Any}\n\n\nComputes 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.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.average_on_poles-Union{Tuple{NF}, Tuple{SpeedyWeather.RingGrids.AbstractGrid{NF}, Vector{<:UnitRange{<:Integer}}}} where NF<:Integer","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.average_on_poles","text":"average_on_poles(\n A::SpeedyWeather.RingGrids.AbstractGrid{NF<:Integer},\n rings::Vector{<:UnitRange{<:Integer}}\n) -> Tuple{Any, Any}\n\n\nMethod for A::Abstract{T<:Integer} which rounds the averaged values to return the same number format NF.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.each_index_in_ring-Union{Tuple{Grid}, Tuple{Grid, Integer}} where Grid<:SpeedyWeather.RingGrids.AbstractGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.each_index_in_ring","text":"i = each_index_in_ring(grid,j)\n\nUnitRange i to access data on grid grid on ring j.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.eachgridpoint-Tuple{SpeedyWeather.RingGrids.AbstractGrid}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.eachgridpoint","text":"ijs = eachgridpoint(grid)\n\nUnitRange ijs to access each grid point on grid grid.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.eachring-Tuple{SpeedyWeather.RingGrids.AbstractGrid}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.eachring","text":"eachring(grid::SpeedyWeather.RingGrids.AbstractGrid) -> Any\n\n\nVector{UnitRange} rings to loop over every ring of grid grid and then each grid point per ring. To be used like\n\nrings = eachring(grid)\nfor ring in rings\n for ij in ring\n grid[ij]\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.eachring-Union{Tuple{Grid}, Tuple{Grid, Vararg{Grid}}} where Grid<:SpeedyWeather.RingGrids.AbstractGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.eachring","text":"eachring(\n grid1::SpeedyWeather.RingGrids.AbstractGrid,\n grids::Grid<:SpeedyWeather.RingGrids.AbstractGrid...\n) -> Any\n\n\nSame as eachring(grid) but performs a bounds check to assess that all grids in grids are of same size.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.extrema_in-Tuple{Vector, Real, Real}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.extrema_in","text":"true/false = extrema_in(v::Vector,a::Real,b::Real)\n\nFor every element vᵢ in v does a<=vi<=b hold?\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_nlons-Tuple{Type{<:SpeedyWeather.RingGrids.AbstractGrid}, Integer}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.get_nlons","text":"get_nlons(\n Grid::Type{<:SpeedyWeather.RingGrids.AbstractGrid},\n nlat_half::Integer;\n both_hemispheres\n) -> Any\n\n\nReturns 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.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.grid_cell_average!-Tuple{SpeedyWeather.RingGrids.AbstractGrid, SpeedyWeather.RingGrids.AbstractFullGrid}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.grid_cell_average!","text":"grid_cell_average!(\n output::SpeedyWeather.RingGrids.AbstractGrid,\n input::SpeedyWeather.RingGrids.AbstractFullGrid\n) -> SpeedyWeather.RingGrids.AbstractGrid\n\n\nAverages 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.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.grid_cell_average-Tuple{Type{<:SpeedyWeather.RingGrids.AbstractGrid}, Integer, SpeedyWeather.RingGrids.AbstractFullGrid}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.grid_cell_average","text":"grid_cell_average(\n Grid::Type{<:SpeedyWeather.RingGrids.AbstractGrid},\n nlat_half::Integer,\n input::SpeedyWeather.RingGrids.AbstractFullGrid\n) -> Any\n\n\nAverages 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.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.isdecreasing-Tuple{Vector}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.isdecreasing","text":"true/false = isdecreasing(v::Vector)\n\nCheck whether elements of a vector v are strictly decreasing.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.isincreasing-Tuple{Vector}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.isincreasing","text":"true/false = isincreasing(v::Vector)\n\nCheck whether elements of a vector v are strictly increasing.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.rotate_matrix_indices_180-Tuple{Integer, Integer, Integer}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.rotate_matrix_indices_180","text":"i_new,j_new = rotate_matrix_indices_180(i,j,s)\n\nRotate indices i,j of a square matrix of size s x s by 180˚.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.rotate_matrix_indices_270-Tuple{Integer, Integer, Integer}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.rotate_matrix_indices_270","text":"i_new,j_new = rotate_matrix_indices_270(i,j,s)\n\nRotate indices i,j of a square matrix of size s x s anti-clockwise by 270˚.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.rotate_matrix_indices_90-Tuple{Integer, Integer, Integer}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.rotate_matrix_indices_90","text":"i_new,j_new = rotate_matrix_indices_90(i,j,s)\n\nRotate indices i,j of a square matrix of size s x s anti-clockwise by 90˚.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.whichring-Tuple{Integer, Vector{UnitRange{Int64}}}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.whichring","text":"whichring(\n ij::Integer,\n rings::Vector{UnitRange{Int64}}\n) -> Int64\n\n\nObtain ring index j from gridpoint ij and Vector{UnitRange} describing rind indices as obtained from eachring(::Grid)\n\n\n\n\n\n","category":"method"},{"location":"#SpeedyWeather.jl-documentation","page":"Home","title":"SpeedyWeather.jl documentation","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Welcome to the documentation for SpeedyWeather.jl a global atmospheric circulation model with simple parametrizations to represent physical processes such as clouds, precipitation and radiation. SpeedyWeather in general is more a library than just a model as it exposes most of its internal functions to the user such that simulations and analysis can be interactively combined. Its user interface is built in a very modular way such that new components can be easily defined and integrated into SpeedyWeather.","category":"page"},{"location":"#Overview","page":"Home","title":"Overview","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"SpeedyWeather.jl is uses a spherical harmonic transform to simulate the general circulation of the atmosphere using a vorticity-divergence formulation, a semi-implicit time integration and simple parameterizations to represent various climate processes: Radiation, clouds, precipitation, surface fluxes, among others.","category":"page"},{"location":"","page":"Home","title":"Home","text":"SpeedyWeather.jl defines ","category":"page"},{"location":"","page":"Home","title":"Home","text":"BarotropicModel for the 2D barotropic vorticity equation\nShallowWaterModel for the 2D shallow water equations\nPrimitiveDryModel for the 3D primitive equations without humidity\nPrimitiveWetModel for the 3D primitive equations with humidity","category":"page"},{"location":"","page":"Home","title":"Home","text":"and solves these equations in spherical coordinates as described in this documentation.","category":"page"},{"location":"#Manual-outline","page":"Home","title":"Manual outline","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"See the following pages of the documentation for more details","category":"page"},{"location":"","page":"Home","title":"Home","text":"Installation\nHow to run SpeedyWeather.jl\nModel setups\nSpherical harmonic transform\nGrids\nBarotropic model\nShallow water model\nPrimitive equation model\nParameterizations\nExtending SpeedyWeather\nNetCDF output","category":"page"},{"location":"","page":"Home","title":"Home","text":"and the submodules","category":"page"},{"location":"","page":"Home","title":"Home","text":"RingGrids\nLowerTriangularMatrices \nSpeedyTransforms","category":"page"},{"location":"","page":"Home","title":"Home","text":"and the original documentation by Molteni and Kucharski.","category":"page"},{"location":"#Developers","page":"Home","title":"Developers","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"The development of SpeedyWeather.jl is lead by Milan Klöwer and current and past contributors include","category":"page"},{"location":"","page":"Home","title":"Home","text":"Tom Kimpson\nAlistair White\nMaximilian Gelbrecht\nDavid Meyer\nDaisuke Hotta\nNavid Constantinou\nSimone Silvestri","category":"page"},{"location":"","page":"Home","title":"Home","text":"Any contributions are always welcome!","category":"page"},{"location":"#Funding","page":"Home","title":"Funding","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"MK received funding by the European Research Council under Horizon 2020 within the ITHACA project, grant agreement number 741112 from 2021-2022. Since 2023 this project is also funded by the National Science Foundation NSF.","category":"page"}] +[{"location":"parameterizations/#parameterizations","page":"Parameterizations","title":"Parameterizations","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"This page describes the mathematical formulation of the parameterizations used in SpeedyWeather.jl to represent physical processes in the atmosphere. Every section is followed by a brief description of implementation details.","category":"page"},{"location":"parameterizations/#Convection","page":"Parameterizations","title":"Convection","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"more to come ...","category":"page"},{"location":"parameterizations/#Large-scale-condensation","page":"Parameterizations","title":"Large-scale condensation","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"more to come ...","category":"page"},{"location":"parameterizations/#Clouds","page":"Parameterizations","title":"Clouds","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"more to come ...","category":"page"},{"location":"parameterizations/#Short-wave-radiation","page":"Parameterizations","title":"Short-wave radiation","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"more to come ...","category":"page"},{"location":"parameterizations/#Long-wave-radiation","page":"Parameterizations","title":"Long-wave radiation","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"more to come ...","category":"page"},{"location":"parameterizations/#Surface-fluxes-of-momentum-and-energy","page":"Parameterizations","title":"Surface fluxes of momentum and energy","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"more to come ...","category":"page"},{"location":"parameterizations/#Vertical-diffusion","page":"Parameterizations","title":"Vertical diffusion","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"more to come ...","category":"page"},{"location":"barotropic/#Barotropic-vorticity-model","page":"Barotropic model","title":"Barotropic vorticity model","text":"","category":"section"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"The barotropic vorticity model describes the evolution of a 2D non-divergent flow with velocity components mathbfu = (uv) through self-advection, forces and dissipation. Due to the non-divergent nature of the flow, it can be described by (the vertical component) of the relative vorticity zeta = nabla times mathbfu.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"The dynamical core presented here to solve the barotropic vorticity equations largely follows the idealized models with spectral dynamics developed at the Geophysical Fluid Dynamics Laboratory[1]: A barotropic vorticity model[2].","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"Many concepts of the Shallow water model and the Primitive equation model are similar, such that for example horizontal diffusion and the Time integration are only explained here.","category":"page"},{"location":"barotropic/#Barotropic-vorticity-equation","page":"Barotropic model","title":"Barotropic vorticity equation","text":"","category":"section"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"The barotropic vorticity equation is the prognostic equation that describes the time evolution of relative vorticity zeta with advection, Coriolis force, forcing and diffusion in a single global layer on the sphere.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"fracpartial zetapartial t + nabla cdot (mathbfu(zeta + f)) =\nF_zeta + nabla times mathbfF_mathbfu + (-1)^n+1nunabla^2nzeta","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"We denote timet, velocity vector mathbfu = (u v), Coriolis parameter f, and hyperdiffusion (-1)^n+1 nu nabla^2n zeta (n is the hyperdiffusion order, see Horizontal diffusion). We also include possible forcing terms F_zeta mathbfF_mathbfu = (F_uF_v) which act on the vorticity and/or on the zonal velocity u and the meridional velocity v and hence the curl nabla times mathbfF_mathbfu is a tendency for relative vorticity zeta. See Extending SpeedyWeather how to define these.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"Starting with some relative vorticity zeta, the Laplacian is inverted to obtain the stream function Psi","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"Psi = nabla^-2zeta","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"The zonal velocity u and meridional velocity v are then the (negative) meridional gradient and zonal gradient of Psi","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"beginaligned\nu = -frac1R fracpartial Psipartial theta \nv = frac1Rcos(theta) fracpartial Psipartial phi \nendaligned","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"which is described in Derivatives in spherical coordinates. Using u and v we can then advect the absolute vorticity zeta + f. In order to avoid to calculate both the curl and the divergence of a flux we rewrite the barotropic vorticity equation as","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"fracpartial zetapartial t = F_zeta +\nnabla times (mathbfF + mathbfu_perp(zeta + f)) + (-1)^n+1nunabla^2nzeta","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"with mathbfu_perp = (v-u) the rotated velocity vector, because -nablacdotmathbfu = nabla times mathbfu_perp. This is the form that is solved in the BarotropicModel, as outlined in the following section.","category":"page"},{"location":"barotropic/#Algorithm","page":"Barotropic model","title":"Algorithm","text":"","category":"section"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"We briefly outline the algorithm that SpeedyWeather.jl uses in order to integrate the barotropic vorticity equation. As an initial step","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"0. Start with initial conditions of zeta_lm in spectral space and transform this model state to grid-point space:","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"Invert the Laplacian of vorticity zeta_lm to obtain the stream function Psi_lm in spectral space\nobtain zonal velocity (cos(theta)u)_lm through a Meridional derivative\nobtain meridional velocity (cos(theta)v)_lm through a Zonal derivative\nTransform zonal and meridional velocity (cos(theta)u)_lm, (cos(theta)v)_lm to grid-point space\nUnscale the cos(theta) factor to obtain uv\nTransform zeta_lm to zeta in grid-point space","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"Now loop over","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"Compute the forcing (or drag) terms F_zeta mathbfF_mathbfu\nMultiply uv with zeta+f in grid-point space\nAdd A = F_u + v(zeta + f) and B = F_v - u(zeta + f)\nTransform these vector components to spectral space A_lm, B_lm\nCompute the curl of (AB)_lm in spectral space, add to F_zeta to accumulate the tendency of zeta_lm\nCompute the horizontal diffusion based on that tendency\nCompute a leapfrog time step as described in Time integration with a Robert-Asselin and Williams filter\nTransform the new spectral state of zeta_lm to grid-point uvzeta as described in 0.\nPossibly do some output\nRepeat from 1.","category":"page"},{"location":"barotropic/#diffusion","page":"Barotropic model","title":"Horizontal diffusion","text":"","category":"section"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"In SpeedyWeather.jl we use hyperdiffusion through an n-th power Laplacian (-1)^n+1nabla^2n (hyper when n1) which can be implemented as a multiplication of the spectral coefficients Psi_lm with (-l(l+1))^nR^-2n (see spectral Laplacian) It is therefore computationally not more expensive to apply hyperdiffusion over diffusion as the (-l(l+1))^nR^-2n can be precomputed. Note the sign change (-1)^n+1 here is such that the dissipative nature of the diffusion operator is retained for n odd and even.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"In SpeedyWeather.jl the diffusion is applied implicitly. For that, consider a leapfrog scheme with time step Delta t of variable zeta to obtain from time steps i-1 and i, the next time step i+1","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"zeta_i+1 = zeta_i-1 + 2Delta t dzeta","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"with dzeta being some tendency evaluated from zeta_i. Now we want to add a diffusion term (-1)^n+1nu nabla^2nzeta with coefficient nu, which however, is implicitly calculated from zeta_i+1, then","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"zeta_i+1 = zeta_i-1 + 2Delta t (dzeta + (-1)^n+1 nunabla^2nzeta_i+1)","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"As the application of (-1)^n+1nunabla^2n is, for every spectral mode, equivalent to a multiplication of a constant, we can rewrite this to","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"zeta_i+1 = fraczeta_i-1 + 2Delta t dzeta1 - 2Delta (-1)^n+1nunabla^2n","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"and expand the numerator to","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"zeta_i+1 = zeta_i-1 + 2Delta t fracdzeta + (-1)^n+1 nunabla^2nzeta_i-11 - 2Delta t (-1)^n+1nu nabla^2n","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"Hence the diffusion can be applied implicitly by updating the tendency dzeta as","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"dzeta to fracdzeta + (-1)^n+1nunabla^2nzeta_i-11 - 2Delta t nu nabla^2n","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"which only depends on zeta_i-1. Now let D_textexplicit = (-1)^n+1nunabla^2n be the explicit part and D_textimplicit = 1 - (-1)^n+1 2Delta t nunabla^2n the implicit part. Both parts can be precomputed and are D_textimplicit = 1 - 2Delta t nunabla^2n the implicit part. Both parts can be precomputed and are only an element-wise multiplication in spectral space. For every spectral harmonic lm we do","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"dzeta to D_textimplicit^-1(dzeta + D_textexplicitzeta_i-1)","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"Hence 2 multiplications and 1 subtraction with precomputed constants. However, we will normalize the (hyper-)Laplacians as described in the following. This also will take care of the alternating sign such that the diffusion operation is dissipative regardless the power n.","category":"page"},{"location":"barotropic/#Normalization-of-diffusion","page":"Barotropic model","title":"Normalization of diffusion","text":"","category":"section"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"In physics, the Laplace operator nabla^2 is often used to represent diffusion due to viscosity in a fluid or diffusion that needs to be added to retain numerical stability. In both cases, the coefficient is nu of units textm^2texts^-1 and the full operator reads as nu nabla^2 with units (textm^2texts^-1)(textm^-2) = texts^-1. This motivates us to normalize the Laplace operator by a constant of units textm^-2 and the coefficient by its inverse such that it becomes a damping timescale of unit texts^-1. Given the application in spectral space we decide to normalize by the largest eigenvalue -l_textmax(l_textmax+1) such that all entries in the discrete spectral Laplace operator are in 01. This also has the effect that the alternating sign drops out, such that higher wavenumbers are always dampened and not amplified. The normalized coefficient nu^* = l_textmax(l_textmax+1)nu (always positive) is therefore reinterpreted as the (inverse) time scale at which the highest wavenumber is dampened to zero due to diffusion. Together we have ","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"D^textexplicit_lm = -nu^* fracl(l+1)l_textmax(l_textmax+1)","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"and the hyper-Laplacian of power n follows as","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"D^textexplicitn_lm = -nu^* left(fracl(l+1)l_textmax(l_textmax+1)right)^n","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"and the implicit part is accordingly D^textimplicitn_lm = 1 - 2Delta t D^textexplicitn_lm. Note that the diffusion time scale nu^* is then also scaled by the radius, see next section.","category":"page"},{"location":"barotropic/#scaling","page":"Barotropic model","title":"Radius scaling","text":"","category":"section"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"Similar to a non-dimensionalization of the equations, SpeedyWeather.jl scales the barotropic vorticity equation with R^2 to obtain normalized gradient operators as follows. A scaling for vorticity zeta and stream function Psi is used that is","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"tildezeta = zeta R tildePsi = Psi R^-1","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"This is also convenient as vorticity is often 10^-5text s^-1 in the atmosphere, but the stream function more like 10^5text m^2text s^-1 and so this scaling brings both closer to 1 with a typical radius of the Earth of 6371km. The inversion of the Laplacians in order to obtain Psi from zeta therefore becomes","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"tildezeta = tildenabla^2 tildePsi","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"where the dimensionless gradients simply omit the scaling with 1R, tildenabla = Rnabla. The Barotropic vorticity equation scaled with R^2 is","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"partial_tildettildezeta + tildenabla cdot (mathbfu(tildezeta + tildef)) =\nnabla times tildemathbfF + (-1)^n+1tildenutildenabla^2ntildezeta","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"with","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"tildet = tR^-1, the scaled time t\nmathbfu = (uv), the velocity vector (no scaling applied)\ntildef = fR, the scaled Coriolis parameter f\ntildemathbfF = RmathbfF, the scaled forcing vector mathbfF\ntildenu = nu^* R, the scaled diffusion coefficient nu^*, which itself is normalized to a damping time scale, see Normalization of diffusion.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"So scaling with the radius squared means we can use dimensionless operators, however, this comes at the cost of needing to deal with both a time step in seconds as well as a scaled time step in seconds per meter, which can be confusing. Furthermore, some constants like Coriolis or the diffusion coefficient need to be scaled too during initialization, which may be confusing too because values are not what users expect them to be. SpeedyWeather.jl follows the logic that the scaling to the prognostic variables is only applied just before the time integration and variables are unscaled for output and after the time integration finished. That way, the scaling is hidden as much as possible from the user. In hopefully many other cases it is clearly denoted that a variable or constant is scaled.","category":"page"},{"location":"barotropic/#leapfrog","page":"Barotropic model","title":"Time integration","text":"","category":"section"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"SpeedyWeather.jl is based on the Leapfrog time integration, which, for relative vorticity zeta, is in its simplest form","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"fraczeta_i+1 - zeta_i-12Delta t = RHS(zeta_i)","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"meaning we step from the previous time step i-1, leapfrogging over the current time stepi to the next time step i+1 by evaluating the tendencies on the right-hand side RHS at the current time step i. The time stepping is done in spectral space. Once the right-hand side RHS is evaluated, leapfrogging is a linear operation, meaning that its simply applied to every spectral coefficient zeta_lm as one would evaluate it on every grid point in grid-point models.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"For the Leapfrog time integration two time steps of the prognostic variables have to be stored, i-1 and i. Time step i is used to evaluate the tendencies which are then added to i-1 in a step that also swaps the indices for the next time step i to i-1 and i+1 to i, so that no additional memory than two time steps have to be stored at the same time.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"The Leapfrog time integration has to be initialized with an Euler forward step in order to have a second time step i+1 available when starting from i to actually leapfrog over. SpeedyWeather.jl therefore does two initial time steps that are different from the leapfrog time steps that follow and that have been described above.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"an Euler forward step with Delta t2, then\none leapfrog time step with Delta t, then\nleapfrog with 2 Delta t till the end","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"This is particularly done in a way that after 2. we have t=0 at i-1 and t=Delta t at i available so that 3. can start the leapfrogging without any offset from the intuitive spacing 0Delta t 2Delta t 3Delta t. The following schematic can be useful","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":" time at step i-1 time at step i time step at i+1\nInitial conditions t = 0 \n1: Euler (T) quad t = 0 t=Delta t2 \n2: Leapfrog with Delta t t = 0 (T) quad t = Delta t2 t = Delta t\n3 to n: Leapfrog with 2Delta t t-Delta t (T) qquad quad quad t t+Delta t","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"The time step that is used to evaluate the tendencies is denoted with (T). It is always the time step furthest in time that is available.","category":"page"},{"location":"barotropic/#Robert-Asselin-and-Williams-filter","page":"Barotropic model","title":"Robert-Asselin and Williams filter","text":"","category":"section"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"The standard leapfrog time integration is often combined with a Robert-Asselin filter[Robert66][Asselin72] to dampen a computational mode. The idea is to start with a standard leapfrog step to obtain the next time step i+1 but then to correct the current time step i by applying a filter which dampens the computational mode. The filter looks like a discrete Laplacian in time with a (1 -2 1) stencil, and so, maybe unsurprisingly, is efficient to filter out a \"grid-scale oscillation\" in time, aka the computational mode. Let v be the unfiltered variable and u be the filtered variable, F the right-hand side tendency, then the standard leapfrog step is","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"v_i+1 = u_i-1 + 2Delta tF(v_i)","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"Meaning we start with a filtered variable u at the previous time step i-1, evaluate the tendency F(v_i) based on the current time step i to obtain an unfiltered next time step v_i+1. We then filter the current time step i (which will become i-1 on the next iteration)","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"u_i = v_i + fracnu2(v_i+1 - 2v_i + u_i-1)","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"by adding a discrete Laplacian with coefficient tfracnu2 to it, evaluated from the available filtered and unfiltered time steps centred around i: v_i-1 is not available anymore because it was overwritten by the filtering at the previous iteration, u_i u_i+1 are not filtered yet when applying the Laplacian. The filter parameter nu is typically chosen between 0.01-0.2, with stronger filtering for higher values.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"Williams[Williams2009] then proposed an additional filter step to regain accuracy that is otherwise lost with a strong Robert-Asselin filter[Amezcua2011][Williams2011]. Now let w be unfiltered, v be once filtered, and u twice filtered, then","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"beginaligned\nw_i+1 = u_i-1 + 2Delta tF(v_i) \nu_i = v_i + fracnualpha2(w_i+1 - 2v_i + u_i-1) \nv_i+1 = w_i+1 - fracnu(1-alpha)2(w_i+1 - 2v_i + u_i-1)\nendaligned","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"with the Williams filter parameter alpha in 051. For alpha=1 we're back with the Robert-Asselin filter (the first two lines).","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"The Laplacian in the parentheses is often called a displacement, meaning that the filtered value is displaced (or corrected) in the direction of the two surrounding time steps. The Williams filter now also applies the same displacement, but in the opposite direction to the next time step i+1 as a correction step (line 3 above) for a once-filtered value v_i+1 which will then be twice-filtered by the Robert-Asselin filter on the next iteration. For more details see the referenced publications.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"The initial Euler step (see Time integration, Table) is not filtered. Both the the Robert-Asselin and Williams filter are then switched on for all following leapfrog time steps.","category":"page"},{"location":"barotropic/#References","page":"Barotropic model","title":"References","text":"","category":"section"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"[1]: Geophysical Fluid Dynamics Laboratory, Idealized models with spectral dynamics","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"[2]: Geophysical Fluid Dynamics Laboratory, The barotropic vorticity equation.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"[Robert66]: Robert, André. “The Integration of a Low Order Spectral Form of the Primitive Meteorological Equations.” Journal of the Meteorological Society of Japan 44 (1966): 237-245.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"[Asselin72]: ASSELIN, R., 1972: Frequency Filter for Time Integrations. Mon. Wea. Rev., 100, 487–490, doi:10.1175/1520-0493(1972)100<0487:FFFTI>2.3.CO;2","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"[Williams2009]: Williams, P. D., 2009: A Proposed Modification to the Robert–Asselin Time Filter. Mon. Wea. Rev., 137, 2538–2546, 10.1175/2009MWR2724.1.","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"[Amezcua2011]: Amezcua, J., E. Kalnay, and P. D. Williams, 2011: The Effects of the RAW Filter on the Climatology and Forecast Skill of the SPEEDY Model. Mon. Wea. Rev., 139, 608–619, doi:10.1175/2010MWR3530.1. ","category":"page"},{"location":"barotropic/","page":"Barotropic model","title":"Barotropic model","text":"[Williams2011]: Williams, P. D., 2011: The RAW Filter: An Improvement to the Robert–Asselin Filter in Semi-Implicit Integrations. Mon. Wea. Rev., 139, 1996–2007, doi:10.1175/2010MWR3601.1. ","category":"page"},{"location":"installation/#Installation","page":"Installation","title":"Installation","text":"","category":"section"},{"location":"installation/","page":"Installation","title":"Installation","text":"SpeedyWeather.jl is registered in the Julia Registry. In most cases just open the Julia REPL and type","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"julia> using Pkg\njulia> Pkg.add(\"SpeedyWeather\")","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"or, equivalently, (] opens the package manager)","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"julia>] add SpeedyWeather","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"which will automatically install the latest release and all necessary dependencies. If you run into any troubles please raise an issue.","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"However, you may want to make use of the latest features, then install directly from the main branch with","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"julia> Pkg.add(url=\"https://github.com/SpeedyWeather/SpeedyWeather.jl\",rev=\"main\")","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"other branches than main can be similarly installed. You can also type, equivalently,","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"julia>] add https://github.com/SpeedyWeather/SpeedyWeather.jl#main","category":"page"},{"location":"installation/#Compatibility-with-Julia-versions","page":"Installation","title":"Compatibility with Julia versions","text":"","category":"section"},{"location":"installation/","page":"Installation","title":"Installation","text":"SpeedyWeather.jl usually lives on the latest minor release and/or its predecessor. At the moment (June 2023) this means ","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"Julia v1.8\nJulia v1.9","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"are supported, but we dropped the support of earlier versions.","category":"page"},{"location":"output/#NetCDF-output","page":"NetCDF output","title":"NetCDF output","text":"","category":"section"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"SpeedyWeather.jl uses NetCDF to output the data of a simulation. The following describes the details of this and how to change the way in which the NetCDF output is written. There are many options to this available.","category":"page"},{"location":"output/#Accessing-the-NetCDF-output-writer","page":"NetCDF output","title":"Accessing the NetCDF output writer","text":"","category":"section"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"The output writer is a component of every Model, i.e. BarotropicModel, ShallowWaterModel, PrimitiveDryModel and PrimitiveWetModel, hence a non-default output writer can be passed on as a keyword argument to the model constructor","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"julia> using SpeedyWeather\njulia> spectral_grid = SpectralGrid()\njulia> my_output_writer = OutputWriter(spectral_grid, PrimitiveDry)\njulia> model = PrimitiveDryModel(;spectral_grid, output=my_output_writer)","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"So after we have defined the grid through the SpectralGrid object we can use and change the implemented OutputWriter by passing on the following arguments","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"julia> my_output_writer = OutputWriter(spectral_grid, PrimitiveDry, kwargs...)","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"the spectral_grid has to be the first argument then the model type (Barotropic, ShallowWater, PrimitiveDry, PrimitiveWet) which helps the output writer to make default choices on which variables to output. However, we can also pass on further keyword arguments. So let's start with an example.","category":"page"},{"location":"output/#Example-1:-NetCDF-output-every-hour","page":"NetCDF output","title":"Example 1: NetCDF output every hour","text":"","category":"section"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"If we want to increase the frequency of the output we can choose output_dt (default =6 in hours) like so","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"julia> my_output_writer = OutputWriter(spectral_grid, PrimitiveDry, output_dt=1)\njulia> model = PrimitiveDryModel(;spectral_grid, output=my_output_writer)","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"which will now output every hour. It is important to pass on the new output writer my_output_writer to the model constructor, otherwise it will not be part of your model and the default is used instead. Note that output_dt has to be understood as the minimum frequency or maximum output time step. Example, we run the model at a resolution of T85 and the time step is going to be 670s","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"julia> spectral_grid = SpectralGrid(trunc=85)\njulia> time_stepper = Leapfrog(spectral_grid)\nLeapfrog{Float32}:\n...\n Δt_sec::Int64 = 670\n...","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"This means that after 32 time steps 5h 57min and 20s will have passed where output will happen as the next time step would be >6h. The time axis of the NetCDF output will look like","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"julia> using NCDatasets\njulia> ds = NCDataset(\"run_0001/output.nc\");\njulia> ds[\"time\"][:]\n5-element Vector{Dates.DateTime}:\n 2000-01-01T00:00:00\n 2000-01-01T05:57:20\n 2000-01-01T11:54:40\n 2000-01-01T17:52:00\n 2000-01-01T23:49:20\n\njulia> diff(ds[\"time\"][:])\n4-element Vector{Dates.Millisecond}:\n 21440000 milliseconds\n 21440000 milliseconds\n 21440000 milliseconds\n 21440000 milliseconds","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"This is so that we don't interpolate in time during output to hit exactly every 6 hours, but at the same time have a constant spacing in time between output time steps.","category":"page"},{"location":"output/#Example-2:-Output-onto-a-higher/lower-resolution-grid","page":"NetCDF output","title":"Example 2: Output onto a higher/lower resolution grid","text":"","category":"section"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"Say we want to run the model at a given horizontal resolution but want to output on another resolution, the OutputWriter takes as argument output_Grid<:AbstractFullGrid and nlat_half::Int. So for example output_Grid=FullClenshawGrid and nlat_half=48 will always interpolate onto a regular 192x95 longitude-latitude grid of 1.875˚ resolution, regardless the grid and resolution used for the model integration.","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"julia> my_output_writer = OutputWriter(spectral_grid, PrimitiveDry, output_Grid=FullClenshawGrid, nlat_half=48)","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"Note that by default the output is on the corresponding full of the grid used in the dynamical core so that interpolation only happens at most in the zonal direction as they share the location of the latitude rings. You can check this by","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"julia> RingGrids.full_grid(OctahedralGaussianGrid)\nFullGaussianGrid","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"So the corresponding full grid of an OctahedralGaussianGrid is the FullGaussiangrid and the same resolution nlat_half is chosen by default in the output writer (which you can change though as shown above). Overview of the corresponding full grids","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"Grid Corresponding full grid\nFullGaussianGrid FullGaussianGrid\nFullClenshawGrid FullClenshawGrid\nOctahadralGaussianGrid FullGaussianGrid\nOctahedralClensawhGrid FullClenshawGrid\nHEALPixGrid FullHEALPixGrid\nOctaHEALPixGrid FullOctaHEALPixGrid","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"The grids FullHEALPixGrid, FullOctaHEALPixGrid share the same latitude rings as their reduced grids, but have always as many longitude points as they are at most around the equator. These grids are not tested in the dynamical core (but you may use them experimentally) and mostly designed for output purposes.","category":"page"},{"location":"output/#Example-3:-Changing-the-output-path-or-identification","page":"NetCDF output","title":"Example 3: Changing the output path or identification","text":"","category":"section"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"That's easy by passing on path=\"/my/favourite/path/\" and the folder run_* with * the identification of the run (that's the id keyword, which can be manually set but is also automatically determined as a number counting up depending on which folders already exist) will be created within.","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"julia> path = pwd()\n\"/Users/milan\"\njulia> my_output_writer = OutputWriter(spectral_grid, PrimitiveDry, path=path)","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"This folder must already exist. If you want to give your run a name/identification you can pass on id","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"julia> my_output_writer = OutputWriter(spectral_grid,PrimitiveDry,id=\"diffusion_test\");","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"which will be used instead of a 4 digit number like 0001, 0002 which is automatically determined if id is not provided. You will see the id of the run in the progress bar","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"Weather is speedy: run diffusion_test 100%|███████████████████████| Time: 0:00:12 (19.20 years/day)","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"and the run folder, here run_diffusion_test, is also named accordingly","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"shell> ls\n...\nrun_diffusion_test\n...","category":"page"},{"location":"output/#Further-options","page":"NetCDF output","title":"Further options","text":"","category":"section"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"Further options are described in the OutputWriter docstring, (also accessible via julia>?OutputWriter for example). Note that some fields are actual options, but others are derived from the options you provided or are arrays/objects the output writer needs, but shouldn't be passed on by the user. The actual options are declared as [OPTION] in the following","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"help?> OutputWriter\nsearch: OutputWriter\n\n NetCDF output writer. Contains all output options and auxiliary fields for\n output interpolation. To be initialised with\n OutputWriter(::SpectralGrid,::Type{<:ModelSetup},kwargs...) to pass on the\n resolution information and the model type which chooses which variables to\n output. Options include\n\n • spectral_grid::SpectralGrid\n\n • output::Bool\n\n • path::String: [OPTION] path to output folder, run_???? will be\n created within\n\n • id::String: [OPTION] run identification number/string\n\n • run_path::String\n\n • filename::String: [OPTION] name of the output netcdf file\n\n • write_restart::Bool: [OPTION] also write restart file if\n output==true?\n\n • pkg_version::VersionNumber\n\n • startdate::Dates.DateTime\n\n • output_dt::Float64: [OPTION] output frequency, time step [hrs]\n\n • output_dt_sec::Int64: actual output time step [sec]\n\n • output_vars::Vector{Symbol}: [OPTION] which variables to output,\n u, v, vor, div, pres, temp, humid","category":"page"},{"location":"functions/#Function-and-type-index","page":"Function and type index","title":"Function and type index","text":"","category":"section"},{"location":"functions/","page":"Function and type index","title":"Function and type index","text":"Modules = [SpeedyWeather]","category":"page"},{"location":"functions/#SpeedyWeather.AbstractDevice","page":"Function and type index","title":"SpeedyWeather.AbstractDevice","text":"abstract type AbstractDevice\n\nSupertype of all devices SpeedyWeather.jl can ran on\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.BarotropicModel","page":"Function and type index","title":"SpeedyWeather.BarotropicModel","text":"The BarotropicModel struct holds all other structs that contain precalculated constants, whether scalars or arrays that do not change throughout model integration.\n\nspectral_grid::SpectralGrid: dictates resolution for many other components\nplanet::SpeedyWeather.AbstractPlanet: contains physical and orbital characteristics\natmosphere::SpeedyWeather.AbstractAtmosphere\nforcing::SpeedyWeather.AbstractForcing{NF} where NF<:AbstractFloat\ndrag::SpeedyWeather.AbstractDrag{NF} where NF<:AbstractFloat\ninitial_conditions::SpeedyWeather.InitialConditions\ntime_stepping::SpeedyWeather.TimeStepper{NF} where NF<:AbstractFloat\nspectral_transform::SpectralTransform\nhorizontal_diffusion::SpeedyWeather.HorizontalDiffusion{NF} where NF<:AbstractFloat\nimplicit::SpeedyWeather.AbstractImplicit{NF} where NF<:AbstractFloat\ngeometry::Geometry\nconstants::DynamicsConstants\ndevice_setup::SpeedyWeather.DeviceSetup\noutput::SpeedyWeather.AbstractOutputWriter\nfeedback::SpeedyWeather.AbstractFeedback\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.CPUDevice","page":"Function and type index","title":"SpeedyWeather.CPUDevice","text":"CPUDevice <: AbstractDevice\n\nIndicates that SpeedyWeather.jl runs on a single CPU \n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Clock","page":"Function and type index","title":"SpeedyWeather.Clock","text":"Clock struct keeps track of the model time, how many days to integrate for and how many time steps this takes\n\ntime::Dates.DateTime: current model time\nn_days::Float64: number of days to integrate for, set in run!(::Simulation)\nn_timesteps::Int64: number of time steps to integrate for, set in initialize!(::Clock,::TimeStepper)\n\n.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Clock-Tuple{SpeedyWeather.TimeStepper}","page":"Function and type index","title":"SpeedyWeather.Clock","text":"Clock(\n time_stepping::SpeedyWeather.TimeStepper;\n kwargs...\n) -> SpeedyWeather.Clock\n\n\nCreate and initialize a clock from time_stepping\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.ColumnVariables","page":"Function and type index","title":"SpeedyWeather.ColumnVariables","text":"Mutable struct that contains all prognostic (copies thereof) and diagnostic variables in a single column needed to evaluate the physical parametrizations. For now the struct is mutable as we will reuse the struct to iterate over horizontal grid points. Every column vector has nlev entries, from [1] at the top to [end] at the lowermost model level at the planetary boundary layer.\n\nnlev::Int64\nnband::Int64\nn_stratosphere_levels::Int64\njring::Int64\nlond::AbstractFloat\nlatd::AbstractFloat\nland_fraction::AbstractFloat\nu::Vector{NF} where NF<:AbstractFloat\nv::Vector{NF} where NF<:AbstractFloat\ntemp::Vector{NF} where NF<:AbstractFloat\nhumid::Vector{NF} where NF<:AbstractFloat\nln_pres::Vector{NF} where NF<:AbstractFloat\npres::Vector{NF} where NF<:AbstractFloat\nu_tend::Vector{NF} where NF<:AbstractFloat\nv_tend::Vector{NF} where NF<:AbstractFloat\ntemp_tend::Vector{NF} where NF<:AbstractFloat\nhumid_tend::Vector{NF} where NF<:AbstractFloat\ngeopot::Vector{NF} where NF<:AbstractFloat\nflux_u_upward::Vector{NF} where NF<:AbstractFloat\nflux_u_downward::Vector{NF} where NF<:AbstractFloat\nflux_v_upward::Vector{NF} where NF<:AbstractFloat\nflux_v_downward::Vector{NF} where NF<:AbstractFloat\nflux_temp_upward::Vector{NF} where NF<:AbstractFloat\nflux_temp_downward::Vector{NF} where NF<:AbstractFloat\nflux_humid_upward::Vector{NF} where NF<:AbstractFloat\nflux_humid_downward::Vector{NF} where NF<:AbstractFloat\nsat_humid::Vector{NF} where NF<:AbstractFloat\nsat_vap_pres::Vector{NF} where NF<:AbstractFloat\ndry_static_energy::Vector{NF} where NF<:AbstractFloat\nmoist_static_energy::Vector{NF} where NF<:AbstractFloat\nhumid_half::Vector{NF} where NF<:AbstractFloat\nsat_humid_half::Vector{NF} where NF<:AbstractFloat\nsat_moist_static_energy::Vector{NF} where NF<:AbstractFloat\ndry_static_energy_half::Vector{NF} where NF<:AbstractFloat\nsat_moist_static_energy_half::Vector{NF} where NF<:AbstractFloat\nconditional_instability::Bool\nactivate_convection::Bool\ncloud_top::Int64\nexcess_humidity::AbstractFloat\ncloud_base_mass_flux::AbstractFloat\nprecip_convection::AbstractFloat\nnet_flux_humid::Vector{NF} where NF<:AbstractFloat\nnet_flux_dry_static_energy::Vector{NF} where NF<:AbstractFloat\nentrainment_profile::Vector{NF} where NF<:AbstractFloat\nprecip_large_scale::AbstractFloat\nwvi::Matrix{NF} where NF<:AbstractFloat\ntau2::Matrix{NF} where NF<:AbstractFloat\ndfabs::Vector{NF} where NF<:AbstractFloat\nfsfcd::AbstractFloat\nst4a::Matrix{NF} where NF<:AbstractFloat\nflux::Vector{NF} where NF<:AbstractFloat\nfsfcu::AbstractFloat\nts::AbstractFloat\nfsfc::AbstractFloat\nftop::AbstractFloat\nstratc::Vector{NF} where NF<:AbstractFloat\ntyear::AbstractFloat\ncsol::AbstractFloat\ntopsr::AbstractFloat\nfsol::AbstractFloat\nozupp::AbstractFloat\nozone::AbstractFloat\nzenit::AbstractFloat\nstratz::AbstractFloat\nalbsfc::AbstractFloat\nssrd::AbstractFloat\nssr::AbstractFloat\ntsr::AbstractFloat\ntend_t_rsw::Vector{NF} where NF<:AbstractFloat\nnorm_pres::AbstractFloat\nicltop::Int64\ncloudc::AbstractFloat\nclstr::AbstractFloat\nqcloud::AbstractFloat\nfmask::AbstractFloat\nrel_hum::Vector{NF} where NF<:AbstractFloat\ngrad_dry_static_energy::AbstractFloat\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.DeviceSetup","page":"Function and type index","title":"SpeedyWeather.DeviceSetup","text":"DeviceSetup{S<:AbstractDevice}\n\nHolds information about the device the model is running on and workgroup size. \n\ndevice::AbstractDevice: Device the model is running on \ndevice_KA::KernelAbstractions.Device: Device for use with KernelAbstractions\nn: workgroup size \n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.DiagnosticVariables","page":"Function and type index","title":"SpeedyWeather.DiagnosticVariables","text":"DiagnosticVariables{Grid<:AbstractGrid,NF<:AbstractFloat}\n\nStruct holding the diagnostic variables.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.DynamicsConstants","page":"Function and type index","title":"SpeedyWeather.DynamicsConstants","text":"Struct holding constants needed at runtime for the dynamical core in number format NF.\n\nradius::AbstractFloat: Radius of Planet [m]\nrotation::AbstractFloat: Angular frequency of Planet's rotation [s^-1]\ngravity::AbstractFloat: Gravitational acceleration [m/s^2]\nlayer_thickness::AbstractFloat: shallow water layer thickness [m]\nR_dry::AbstractFloat: specific gas constant for dry air [J/kg/K]\nR_vapour::AbstractFloat: specific gas constant for water vapour [J/kg/K]\nμ_virt_temp::AbstractFloat: used in Tv = T(1+μq) for virt temp Tv(T,q) calculation\ncₚ::AbstractFloat: specific heat at constant pressure [J/K/kg]\nκ::AbstractFloat: = R_dry/cₚ, gas const for air over heat capacity\nwater_density::AbstractFloat: water density [kg/m³]\nf_coriolis::Vector{NF} where NF<:AbstractFloat: coriolis frequency [s^-1], scaled by radius as is vorticity = 2Ωsin(lat)radius\nσ_lnp_A::Vector{NF} where NF<:AbstractFloat: σ-related factor A needed for adiabatic conversion term\nσ_lnp_B::Vector{NF} where NF<:AbstractFloat: σ-related factor B needed for adiabatic conversion term\nΔp_geopot_half::Vector{NF} where NF<:AbstractFloat: = R*(ln(pk+1) - ln(pk+1/2)), for half level geopotential\nΔp_geopot_full::Vector{NF} where NF<:AbstractFloat: = R*(ln(pk+1/2) - ln(pk)), for full level geopotential\ntemp_ref_profile::Vector{NF} where NF<:AbstractFloat: reference temperature profile\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.DynamicsConstants-Tuple{SpectralGrid, SpeedyWeather.AbstractPlanet, SpeedyWeather.AbstractAtmosphere, Geometry}","page":"Function and type index","title":"SpeedyWeather.DynamicsConstants","text":"DynamicsConstants(\n spectral_grid::SpectralGrid,\n planet::SpeedyWeather.AbstractPlanet,\n atmosphere::SpeedyWeather.AbstractAtmosphere,\n geometry::Geometry\n) -> Any\n\n\nGenerator function for a DynamicsConstants struct.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.DynamicsVariables","page":"Function and type index","title":"SpeedyWeather.DynamicsVariables","text":"DynamicsVariables{Grid<:AbstractGrid,NF<:AbstractFloat}\n\nStruct holding intermediate quantities for the dynamics of a given layer.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Earth","page":"Function and type index","title":"SpeedyWeather.Earth","text":"Create a struct Earth<:AbstractPlanet, with the following physical/orbital characteristics. Note that radius is not part of it as this should be chosen in SpectralGrid. Keyword arguments are\n\nrotation::Float64: angular frequency of Earth's rotation [rad/s]\ngravity::Float64: gravitational acceleration [m/s^2]\ndaily_cycle::Bool: switch on/off daily cycle\nlength_of_day::Float64: [hrs] in a day\nseasonal_cycle::Bool: switch on/off seasonal cycle\nlength_of_year::Float64: [days] in a year\nequinox::Dates.DateTime: time of spring equinox (year irrelevant)\naxial_tilt::Float64: angle [˚] rotation axis tilt wrt to orbit\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.EarthAtmosphere","page":"Function and type index","title":"SpeedyWeather.EarthAtmosphere","text":"Create a struct EarthAtmosphere<:AbstractPlanet, with the following physical/chemical characteristics. Note that radius is not part of it as this should be chosen in SpectralGrid. Keyword arguments are\n\nmol_mass_dry_air::Float64: molar mass of dry air [g/mol]\nmol_mass_vapour::Float64: molar mass of water vapour [g/mol]\ncₚ::Float64: specific heat at constant pressure [J/K/kg]\nR_gas::Float64: universal gas constant [J/K/mol]\nR_dry::Float64: specific gas constant for dry air [J/kg/K]\nR_vapour::Float64: specific gas constant for water vapour [J/kg/K]\nwater_density::Float64: water density [kg/m³]\nlatent_heat_condensation::Float64: latent heat of condensation [J/g] for consistency with specific humidity [g/Kg], also called alhc\nlatent_heat_sublimation::Float64: latent heat of sublimation [J/g], also called alhs\nstefan_boltzmann::Float64: stefan-Boltzmann constant [W/m²/K⁴]\nlapse_rate::Float64: moist adiabatic temperature lapse rate -dTdz [K/km]\ntemp_ref::Float64: absolute temperature at surface z=0 [K]\ntemp_top::Float64: absolute temperature in stratosphere [K]\nΔT_stratosphere::Float64: for stratospheric lapse rate [K] after Jablonowski\nσ_tropopause::Float64: start of the stratosphere in sigma coordinates\nσ_boundary_layer::Float64: top of the planetary boundary layer in sigma coordinates\nscale_height::Float64: scale height for pressure [km]\npres_ref::Float64: surface pressure [hPa]\nscale_height_humid::Float64: scale height for specific humidity [km]\nrelhumid_ref::Float64: relative humidity of near-surface air [1]\nwater_pres_ref::Float64: saturation water vapour pressure [Pa]\nlayer_thickness::Float64: layer thickness for the shallow water model [km]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.EarthOrography","page":"Function and type index","title":"SpeedyWeather.EarthOrography","text":"Earth's orography read from file, with smoothing.\n\npath::String: path to the folder containing the orography file, pkg path default\nfile::String: filename of orography\nfile_Grid::Type{<:SpeedyWeather.RingGrids.AbstractGrid}: Grid the orography file comes on\nscale::Float64: scale orography by a factor\nsmoothing::Bool: smooth the orography field?\nsmoothing_power::Float64: power of Laplacian for smoothing\nsmoothing_strength::Float64: highest degree l is multiplied by\nsmoothing_truncation::Int64: resolution of orography in spectral trunc\norography::SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat: height [m] on grid-point space.\ngeopot_surf::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: surface geopotential, height*gravity [m²/s²]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.EarthOrography-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.EarthOrography","text":"EarthOrography(\n spectral_grid::SpectralGrid;\n kwargs...\n) -> Any\n\n\nGenerator function pulling the resolution information from spectral_grid.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.Feedback","page":"Function and type index","title":"SpeedyWeather.Feedback","text":"Feedback() -> Feedback\nFeedback(verbose::Bool) -> Feedback\nFeedback(verbose::Bool, debug::Bool) -> Feedback\n\n\nGenerator function for a Feedback struct.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Feedback-2","page":"Function and type index","title":"SpeedyWeather.Feedback","text":"Feedback struct that contains options and object for command-line feedback like the progress meter.\n\nverbose::Bool: print feedback to REPL?\ndebug::Bool: check for NaRs in the prognostic variables\noutput::Bool: write a progress.txt file? State synced with OutputWriter.output\nid::Union{Int64, String}: identification of run, taken from ::OutputWriter\nrun_path::String: path to run folder, taken from ::OutputWriter\nprogress_meter::ProgressMeter.Progress: struct containing everything progress related\nprogress_txt::Union{Nothing, IOStream}: txt is a Nothing in case of no output\nnars_detected::Bool: did Infs/NaNs occur in the simulation?\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.GPUDevice","page":"Function and type index","title":"SpeedyWeather.GPUDevice","text":"GPUDevice <: AbstractDevice\n\nIndicates that SpeedyWeather.jl runs on a single GPU\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.GenLogisticCoefs","page":"Function and type index","title":"SpeedyWeather.GenLogisticCoefs","text":"Coefficients of the generalised logistic function to describe the vertical coordinate. Default coefficients A,K,C,Q,B,M,ν are fitted to the old L31 configuration at ECMWF.\n\nFollowing the notation of https://en.wikipedia.org/wiki/Generalisedlogisticfunction (Dec 15 2021).\n\nChange default parameters for more/fewer levels in the stratosphere vs troposphere vs boundary layer.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Geometry","page":"Function and type index","title":"SpeedyWeather.Geometry","text":"Construct Geometry struct containing parameters and arrays describing an iso-latitude grid <:AbstractGrid and the vertical levels. Pass on SpectralGrid to calculate the following fields\n\nspectral_grid::SpectralGrid: SpectralGrid that defines spectral and grid resolution\nGrid::Type{<:SpeedyWeather.RingGrids.AbstractGrid}: grid of the dynamical core\nnlat_half::Int64: resolution parameter nlat_half of Grid, # of latitudes on one hemisphere (incl Equator)\nnlon_max::Int64: maximum number of longitudes (at/around Equator)\nnlon::Int64: =nlon_max, same (used for compatibility), TODO: still needed?\nnlat::Int64: number of latitude rings\nnlev::Int64: number of vertical levels\nnpoints::Int64: total number of grid points\nradius::AbstractFloat: Planet's radius [m]\ncolat::Vector{Float64}: array of colatitudes in radians (0...π)\nlatd::Vector{Float64}: array of latitudes in degrees (90˚...-90˚)\nlond::Vector{Float64}: array of longitudes in degrees (0...360˚), empty for non-full grids\nlonds::Vector{NF} where NF<:AbstractFloat: longitude (-180˚...180˚) for each grid point in ring order\nlatds::Vector{NF} where NF<:AbstractFloat: latitude (-90˚...˚90) for each grid point in ring order\nsinlat::Vector{NF} where NF<:AbstractFloat: sin of latitudes\ncoslat::Vector{NF} where NF<:AbstractFloat: cos of latitudes\ncoslat⁻¹::Vector{NF} where NF<:AbstractFloat: = 1/cos(lat)\ncoslat²::Vector{NF} where NF<:AbstractFloat: = cos²(lat)\ncoslat⁻²::Vector{NF} where NF<:AbstractFloat: # = 1/cos²(lat)\nσ_levels_half::Vector{NF} where NF<:AbstractFloat: σ at half levels, σ_k+1/2\nσ_levels_full::Vector{NF} where NF<:AbstractFloat: σ at full levels, σₖ\nσ_levels_thick::Vector{NF} where NF<:AbstractFloat: σ level thicknesses, σₖ₊₁ - σₖ\nln_σ_levels_full::Vector{NF} where NF<:AbstractFloat: log of σ at full levels, include surface (σ=1) as last element\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Geometry-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.Geometry","text":"Geometry(spectral_grid::SpectralGrid) -> Any\n\n\nGenerator function for Geometry struct based on spectral_grid.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.GridVariables","page":"Function and type index","title":"SpeedyWeather.GridVariables","text":"GridVariables{NF<:AbstractFloat}\n\nStruct holding the prognostic spectral variables of a given layer in grid point space.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.HeldSuarez","page":"Function and type index","title":"SpeedyWeather.HeldSuarez","text":"Struct that defines the temperature relaxation from Held and Suarez, 1996 BAMS\n\nnlat::Int64: number of latitude rings\nnlev::Int64: number of vertical levels\nσb::Float64: sigma coordinate below which faster surface relaxation is applied\nrelax_time_slow::Float64: time scale [hrs] for slow global relaxation\nrelax_time_fast::Float64: time scale [hrs] for faster tropical surface relaxation\nTmin::Float64: minimum equilibrium temperature [K]\nTmax::Float64: maximum equilibrium temperature [K]\nΔTy::Float64: meridional temperature gradient [K]\nΔθz::Float64: vertical temperature gradient [K]\nκ::Base.RefValue{NF} where NF<:AbstractFloat\np₀::Base.RefValue{NF} where NF<:AbstractFloat\ntemp_relax_freq::Matrix{NF} where NF<:AbstractFloat\ntemp_equil_a::Vector{NF} where NF<:AbstractFloat\ntemp_equil_b::Vector{NF} where NF<:AbstractFloat\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.HeldSuarez-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.HeldSuarez","text":"HeldSuarez(SG::SpectralGrid; kwargs...) -> Any\n\n\ncreate a HeldSuarez temperature relaxation with arrays allocated given spectral_grid\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.HyperDiffusion","page":"Function and type index","title":"SpeedyWeather.HyperDiffusion","text":"Struct for horizontal hyper diffusion of vor, div, temp; implicitly in spectral space with a power of the Laplacian (default=4) and the strength controlled by time_scale. Options exist to scale the diffusion by resolution, and adaptive depending on the current vorticity maximum to increase diffusion in active layers. Furthermore the power can be decreased above the tapering_σ to power_stratosphere (default 2). For Barotropic, ShallowWater, the default non-adaptive constant-time scale hyper diffusion is used. Options are\n\ntrunc::Int64: spectral resolution\nnlev::Int64: number of vertical levels\npower::Float64: power of Laplacian\ntime_scale::Float64: diffusion time scales [hrs]\nresolution_scaling::Float64: stronger diffusion with resolution? 0: constant with trunc, 1: (inverse) linear with trunc, etc\npower_stratosphere::Float64: different power for tropopause/stratosphere\ntapering_σ::Float64: linearly scale towards power_stratosphere above this σ\nadaptive::Bool: adaptive = higher diffusion for layers with higher vorticity levels.\nvor_max::Float64: above this (absolute) vorticity level [1/s], diffusion is increased\nadaptive_strength::Float64: increase strength above vor_max by this factor times max(abs(vor))/vor_max\n∇²ⁿ_2D::Vector\n∇²ⁿ_2D_implicit::Vector\n∇²ⁿ::Array{Vector{NF}, 1} where NF\n∇²ⁿ_implicit::Array{Vector{NF}, 1} where NF\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.HyperDiffusion-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.HyperDiffusion","text":"HyperDiffusion(\n spectral_grid::SpectralGrid;\n kwargs...\n) -> Any\n\n\nGenerator function based on the resolutin in spectral_grid. Passes on keyword arguments.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.ImplicitPrimitiveEq","page":"Function and type index","title":"SpeedyWeather.ImplicitPrimitiveEq","text":"Struct that holds various precomputed arrays for the semi-implicit correction to prevent gravity waves from amplifying in the primitive equation model.\n\ntrunc::Int64: spectral resolution\nnlev::Int64: number of vertical levels\nα::Float64: time-step coefficient: 0=explicit, 0.5=centred implicit, 1=backward implicit\ntemp_profile::Vector{NF} where NF<:AbstractFloat: vertical temperature profile, obtained from diagn\nξ::Base.RefValue{NF} where NF<:AbstractFloat: time step 2α*Δt packed in RefValue for mutability\nR::Matrix{NF} where NF<:AbstractFloat: divergence: operator for the geopotential calculation\nU::Vector{NF} where NF<:AbstractFloat: divergence: the -RdTₖ∇² term excl the eigenvalues from ∇² for divergence\nL::Matrix{NF} where NF<:AbstractFloat: temperature: operator for the TₖD + κTₖDlnps/Dt term\nW::Vector{NF} where NF<:AbstractFloat: pressure: vertical averaging of the -D̄ term in the log surface pres equation\nL0::Vector{NF} where NF<:AbstractFloat: components to construct L, 1/ 2Δσ\nL1::Matrix{NF} where NF<:AbstractFloat: vert advection term in the temperature equation (below+above)\nL2::Vector{NF} where NF<:AbstractFloat: factor in front of the divsumabove term\nL3::Matrix{NF} where NF<:AbstractFloat: sumabove operator itself\nL4::Vector{NF} where NF<:AbstractFloat: factor in front of div term in Dlnps/Dt\nS::Matrix{NF} where NF<:AbstractFloat: for every l the matrix to be inverted\nS⁻¹::Array{NF, 3} where NF<:AbstractFloat: combined inverted operator: S = 1 - ξ²(RL + UW)\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ImplicitPrimitiveEq-Tuple{SpectralGrid, Vararg{Any}}","page":"Function and type index","title":"SpeedyWeather.ImplicitPrimitiveEq","text":"ImplicitPrimitiveEq(\n spectral_grid::SpectralGrid,\n kwargs...\n) -> Any\n\n\nGenerator using the resolution from SpectralGrid.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.ImplicitShallowWater","page":"Function and type index","title":"SpeedyWeather.ImplicitShallowWater","text":"Struct that holds various precomputed arrays for the semi-implicit correction to prevent gravity waves from amplifying in the shallow water model.\n\ntrunc::Int64\nα::Float64: coefficient for semi-implicit computations to filter gravity waves\nH::Base.RefValue{NF} where NF<:AbstractFloat\nξH::Base.RefValue{NF} where NF<:AbstractFloat\ng∇²::Vector{NF} where NF<:AbstractFloat\nξg∇²::Vector{NF} where NF<:AbstractFloat\nS⁻¹::Vector{NF} where NF<:AbstractFloat\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ImplicitShallowWater-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.ImplicitShallowWater","text":"ImplicitShallowWater(\n spectral_grid::SpectralGrid;\n kwargs...\n) -> Any\n\n\nGenerator using the resolution from spectral_grid.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.JablonowskiRelaxation","page":"Function and type index","title":"SpeedyWeather.JablonowskiRelaxation","text":"HeldSuarez-like temperature relaxation, but towards the Jablonowski temperature profile with increasing temperatures in the stratosphere.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.JablonowskiRelaxation-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.JablonowskiRelaxation","text":"JablonowskiRelaxation(SG::SpectralGrid; kwargs...) -> Any\n\n\ncreate a JablonowskiRelaxation temperature relaxation with arrays allocated given spectral_grid\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.JetStreamForcing","page":"Function and type index","title":"SpeedyWeather.JetStreamForcing","text":"Forcing term for the Barotropic or ShallowWaterModel with an idealised jet stream similar to the initial conditions from Galewsky, 2004, but mirrored for both hemispheres.\n\nnlat::Int64: Number of latitude rings\nlatitude::Float64: jet latitude [˚N]\nwidth::Float64: jet width [˚], default ≈ 19.29˚\nspeed::Float64: jet speed scale [m/s]\ntime_scale::Float64: time scale [days]\namplitude::Vector: precomputed amplitude vector [m/s²]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Keepbits","page":"Function and type index","title":"SpeedyWeather.Keepbits","text":"Number of mantissa bits to keep for each prognostic variable when compressed for netCDF and .jld2 data output.\n\nu::Int64\nv::Int64\nvor::Int64\ndiv::Int64\ntemp::Int64\npres::Int64\nhumid::Int64\nprecip_cond::Int64\nprecip_conv::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.LandSeaMask","page":"Function and type index","title":"SpeedyWeather.LandSeaMask","text":"Land-sea mask, fractional, read from file.\n\npath::String: path to the folder containing the land-sea mask file, pkg path default\nfile::String: filename of land sea mask\nfile_Grid::Type{<:SpeedyWeather.RingGrids.AbstractGrid}: Grid the land-sea mask file comes on\nland_sea_mask::SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat: Land-sea mask [1] on grid-point space. Land=1, sea=0, land-area fraction in between.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.LandSeaMask-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.LandSeaMask","text":"LandSeaMask(spectral_grid::SpectralGrid; kwargs...) -> Any\n\n\nGenerator function pulling the resolution information from spectral_grid.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.Leapfrog","page":"Function and type index","title":"SpeedyWeather.Leapfrog","text":"Leapfrog time stepping defined by the following fields\n\ntrunc::Int64: spectral resolution (max degree of spherical harmonics)\nΔt_at_T31::Float64: time step in minutes for T31, scale linearly to trunc\nradius::Any: radius of sphere [m], used for scaling\nrobert_filter::Any: Robert (1966) time filter coefficeint to suppress comput. mode\nwilliams_filter::Any: Williams time filter (Amezcua 2011) coefficient for 3rd order acc\nΔt_sec::Int64: time step Δt [s] at specified resolution\nΔt::Any: time step Δt [s/m] at specified resolution, scaled by 1/radius\nΔt_hrs::Float64: convert time step Δt from minutes to hours\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Leapfrog-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.Leapfrog","text":"Leapfrog(spectral_grid::SpectralGrid; kwargs...) -> Any\n\n\nGenerator function for a Leapfrog struct using spectral_grid for the resolution information.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.LinearDrag","page":"Function and type index","title":"SpeedyWeather.LinearDrag","text":"Linear boundary layer drag Following Held and Suarez, 1996 BAMS\n\nσb::Float64\ntime_scale::Float64\nnlev::Int64\ndrag_coefs::Vector{NF} where NF<:AbstractFloat\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.LinearDrag-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.LinearDrag","text":"LinearDrag(SG::SpectralGrid; kwargs...) -> Any\n\n\nGenerator function using nlev from SG::SpectralGrid\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.MagnusCoefs","page":"Function and type index","title":"SpeedyWeather.MagnusCoefs","text":"Parameters for computing saturation vapour pressure using the August-Roche-Magnus formula,\n\neᵢ(T) = e₀ * exp(Cᵢ * (T - T₀) / (T - Tᵢ)),\n\nwhere T is in Kelvin and i = 1,2 for saturation with respect to water and ice, respectively.\n\ne₀::AbstractFloat: Saturation vapour pressure at 0°C [Pa]\nT₀::AbstractFloat: 0°C in Kelvin\nT₁::AbstractFloat\nT₂::AbstractFloat\nC₁::AbstractFloat\nC₂::AbstractFloat\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.NoBoundaryLayerDrag","page":"Function and type index","title":"SpeedyWeather.NoBoundaryLayerDrag","text":"Concrete type that disables the boundary layer drag scheme.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.NoOrography","page":"Function and type index","title":"SpeedyWeather.NoOrography","text":"Orography with zero height in orography and zero surface geopotential geopot_surf.\n\norography::SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat: height [m] on grid-point space.\ngeopot_surf::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: surface geopotential, height*gravity [m²/s²]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.NoOrography-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.NoOrography","text":"NoOrography(spectral_grid::SpectralGrid) -> NoOrography\n\n\nGenerator function pulling the resolution information from spectral_grid.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.OutputWriter","page":"Function and type index","title":"SpeedyWeather.OutputWriter","text":"NetCDF output writer. Contains all output options and auxiliary fields for output interpolation. To be initialised with OutputWriter(::SpectralGrid,::Type{<:ModelSetup},kwargs...) to pass on the resolution information and the model type which chooses which variables to output. Options include\n\nspectral_grid::SpectralGrid\noutput::Bool\npath::String: [OPTION] path to output folder, run_???? will be created within\nid::String: [OPTION] run identification number/string\nrun_path::String\nfilename::String: [OPTION] name of the output netcdf file\nwrite_restart::Bool: [OPTION] also write restart file if output==true?\npkg_version::VersionNumber\nstartdate::Dates.DateTime\noutput_dt::Float64: [OPTION] output frequency, time step [hrs]\noutput_dt_sec::Int64: actual output time step [sec]\noutput_vars::Vector{Symbol}: [OPTION] which variables to output, u, v, vor, div, pres, temp, humid\nmissing_value::Union{Float32, Float64}: [OPTION] missing value to be used in netcdf output\ncompression_level::Int64: [OPTION] lossless compression level; 1=low but fast, 9=high but slow\nshuffle::Bool: [OPTION] shuffle/bittranspose filter for compression\nkeepbits::SpeedyWeather.Keepbits: [OPTION] mantissa bits to keep for every variable\noutput_every_n_steps::Int64\ntimestep_counter::Int64\noutput_counter::Int64\nnetcdf_file::Union{Nothing, NCDatasets.NCDataset}\ninput_Grid::Type{<:SpeedyWeather.RingGrids.AbstractGrid}\nas_matrix::Bool: [OPTION] sort grid points into a matrix (interpolation-free), for OctahedralClenshawGrid, OctaHEALPixGrid only\nquadrant_rotation::NTuple{4, Int64}\nmatrix_quadrant::NTuple{4, Tuple{Int64, Int64}}\noutput_Grid::Type{<:SpeedyWeather.RingGrids.AbstractFullGrid}: [OPTION] the grid used for output, full grids only\nnlat_half::Int64: [OPTION] the resolution of the output grid, default: same nlat_half as in the dynamical core\nnlon::Int64\nnlat::Int64\nnpoints::Int64\nnlev::Int64\ninterpolator::SpeedyWeather.RingGrids.AbstractInterpolator\nu::Matrix{NF} where NF<:Union{Float32, Float64}\nv::Matrix{NF} where NF<:Union{Float32, Float64}\nvor::Matrix{NF} where NF<:Union{Float32, Float64}\ndiv::Matrix{NF} where NF<:Union{Float32, Float64}\ntemp::Matrix{NF} where NF<:Union{Float32, Float64}\npres::Matrix{NF} where NF<:Union{Float32, Float64}\nhumid::Matrix{NF} where NF<:Union{Float32, Float64}\nprecip_cond::Matrix{NF} where NF<:Union{Float32, Float64}\nprecip_conv::Matrix{NF} where NF<:Union{Float32, Float64}\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.PrimitiveDryModel","page":"Function and type index","title":"SpeedyWeather.PrimitiveDryModel","text":"The PrimitiveDryModel struct holds all other structs that contain precalculated constants, whether scalars or arrays that do not change throughout model integration.\n\nspectral_grid::SpectralGrid: dictates resolution for many other components\nplanet::SpeedyWeather.AbstractPlanet: contains physical and orbital characteristics\natmosphere::SpeedyWeather.AbstractAtmosphere\ninitial_conditions::SpeedyWeather.InitialConditions\norography::SpeedyWeather.AbstractOrography{NF} where NF<:AbstractFloat\nland_sea_mask::SpeedyWeather.AbstractLandSeaMask{NF} where NF<:AbstractFloat\nphysics::Bool\nboundary_layer_drag::SpeedyWeather.BoundaryLayerDrag{NF} where NF<:AbstractFloat\ntemperature_relaxation::SpeedyWeather.TemperatureRelaxation{NF} where NF<:AbstractFloat\nstatic_energy_diffusion::SpeedyWeather.VerticalDiffusion{NF} where NF<:AbstractFloat\nvertical_advection::SpeedyWeather.VerticalAdvection{NF} where NF<:AbstractFloat\ntime_stepping::SpeedyWeather.TimeStepper{NF} where NF<:AbstractFloat\nspectral_transform::SpectralTransform\nhorizontal_diffusion::SpeedyWeather.HorizontalDiffusion{NF} where NF<:AbstractFloat\nimplicit::SpeedyWeather.AbstractImplicit{NF} where NF<:AbstractFloat\ngeometry::Geometry\nconstants::DynamicsConstants\ndevice_setup::SpeedyWeather.DeviceSetup\noutput::SpeedyWeather.AbstractOutputWriter\nfeedback::SpeedyWeather.AbstractFeedback\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.PrimitiveWetModel","page":"Function and type index","title":"SpeedyWeather.PrimitiveWetModel","text":"The PrimitiveDryModel struct holds all other structs that contain precalculated constants, whether scalars or arrays that do not change throughout model integration.\n\nspectral_grid::SpectralGrid: dictates resolution for many other components\nplanet::SpeedyWeather.AbstractPlanet: contains physical and orbital characteristics\natmosphere::SpeedyWeather.AbstractAtmosphere\ninitial_conditions::SpeedyWeather.InitialConditions\norography::SpeedyWeather.AbstractOrography{NF} where NF<:AbstractFloat\nland_sea_mask::SpeedyWeather.AbstractLandSeaMask{NF} where NF<:AbstractFloat\nphysics::Bool\nthermodynamics::SpeedyWeather.Thermodynamics{NF} where NF<:AbstractFloat\nboundary_layer_drag::SpeedyWeather.BoundaryLayerDrag{NF} where NF<:AbstractFloat\ntemperature_relaxation::SpeedyWeather.TemperatureRelaxation{NF} where NF<:AbstractFloat\nstatic_energy_diffusion::SpeedyWeather.VerticalDiffusion{NF} where NF<:AbstractFloat\nlarge_scale_condensation::SpeedyWeather.AbstractCondensation{NF} where NF<:AbstractFloat\nvertical_advection::SpeedyWeather.VerticalAdvection{NF} where NF<:AbstractFloat\ntime_stepping::SpeedyWeather.TimeStepper{NF} where NF<:AbstractFloat\nspectral_transform::SpectralTransform\nhorizontal_diffusion::SpeedyWeather.HorizontalDiffusion{NF} where NF<:AbstractFloat\nimplicit::SpeedyWeather.AbstractImplicit{NF} where NF<:AbstractFloat\ngeometry::Geometry\nconstants::DynamicsConstants\ndevice_setup::SpeedyWeather.DeviceSetup\noutput::SpeedyWeather.AbstractOutputWriter\nfeedback::SpeedyWeather.AbstractFeedback\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.PrognosticLayerTimesteps","page":"Function and type index","title":"SpeedyWeather.PrognosticLayerTimesteps","text":"Collect the n time steps of PrognosticVariablesLayer of an n-step time integration (leapfrog=2) into a single struct.\n\ntimesteps::Array{SpeedyWeather.PrognosticVariablesLayer{NF}, 1} where NF<:AbstractFloat\n\n.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.PrognosticSurfaceTimesteps","page":"Function and type index","title":"SpeedyWeather.PrognosticSurfaceTimesteps","text":"Collect the n time steps of PrognosticVariablesSurface of an n-step time integration (leapfrog=2) into a single struct.\n\ntimesteps::Array{SpeedyWeather.PrognosticVariablesSurface{NF}, 1} where NF<:AbstractFloat\n\n.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.PrognosticVariablesLayer","page":"Function and type index","title":"SpeedyWeather.PrognosticVariablesLayer","text":"A layer of the prognostic variables in spectral space.\n\ntrunc::Int64: Spectral resolution as max degree of spherical harmonics\nvor::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: Vorticity of horizontal wind field [1/s]\ndiv::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: Divergence of horizontal wind field [1/s]\ntemp::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: Absolute temperature [K]\nhumid::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: Specific humidity [kg/kg]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.PrognosticVariablesSurface","page":"Function and type index","title":"SpeedyWeather.PrognosticVariablesSurface","text":"The spectral and gridded prognostic variables at the surface.\n\ntrunc::Int64: Spectral resolution as max degree of spherical harmonics\npres::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: log of surface pressure [log(Pa)] for PrimitiveEquation, interface displacement [m] for ShallowWaterModel\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.RandomWaves","page":"Function and type index","title":"SpeedyWeather.RandomWaves","text":"Parameters for random initial conditions for the interface displacement η in the shallow water equations.\n\nA::Float64\nlmin::Int64\nlmax::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ShallowWaterModel","page":"Function and type index","title":"SpeedyWeather.ShallowWaterModel","text":"The ShallowWaterModel struct holds all other structs that contain precalculated constants, whether scalars or arrays that do not change throughout model integration.\n\nspectral_grid::SpectralGrid: dictates resolution for many other components\nplanet::SpeedyWeather.AbstractPlanet: contains physical and orbital characteristics\natmosphere::SpeedyWeather.AbstractAtmosphere\nforcing::SpeedyWeather.AbstractForcing{NF} where NF<:AbstractFloat\ndrag::SpeedyWeather.AbstractDrag{NF} where NF<:AbstractFloat\ninitial_conditions::SpeedyWeather.InitialConditions\norography::SpeedyWeather.AbstractOrography{NF} where NF<:AbstractFloat\ntime_stepping::SpeedyWeather.TimeStepper{NF} where NF<:AbstractFloat\nspectral_transform::SpectralTransform\nhorizontal_diffusion::SpeedyWeather.HorizontalDiffusion{NF} where NF<:AbstractFloat\nimplicit::SpeedyWeather.AbstractImplicit{NF} where NF<:AbstractFloat\ngeometry::Geometry\nconstants::DynamicsConstants\ndevice_setup::SpeedyWeather.DeviceSetup\noutput::SpeedyWeather.AbstractOutputWriter\nfeedback::SpeedyWeather.AbstractFeedback\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Simulation","page":"Function and type index","title":"SpeedyWeather.Simulation","text":"Simulation is a container struct to be used with run!(::Simulation). It contains\n\nprognostic_variables::PrognosticVariables: define the current state of the model\ndiagnostic_variables::DiagnosticVariables: contain the tendencies and auxiliary arrays to compute them\nmodel::SpeedyWeather.ModelSetup: all parameters, constant at runtime\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SpectralGrid","page":"Function and type index","title":"SpeedyWeather.SpectralGrid","text":"Defines the horizontal spectral resolution and corresponding grid and the vertical coordinate for SpeedyWeather.jl. Options are\n\nNF::Type{<:AbstractFloat}: number format used throughout the model\ntrunc::Int64: horizontal resolution as the maximum degree of spherical harmonics\nGrid::Type{<:SpeedyWeather.RingGrids.AbstractGrid}: horizontal grid used for calculations in grid-point space\ndealiasing::Float64: how to match spectral with grid resolution: dealiasing factor, 1=linear, 2=quadratic, 3=cubic grid\nradius::Float64: radius of the sphere [m]\nnlat_half::Int64: number of latitude rings on one hemisphere (Equator incl)\nnpoints::Int64: total number of grid points in the horizontal\nnlev::Int64: number of vertical levels\nvertical_coordinates::SpeedyWeather.VerticalCoordinates: coordinates used to discretize the vertical\n\nnlat_half and npoints should not be chosen but are derived from trunc, Grid and dealiasing.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SpeedyCondensation","page":"Function and type index","title":"SpeedyWeather.SpeedyCondensation","text":"Large scale condensation as in Fortran SPEEDY with default values from therein.\n\nnlev::Int64: number of vertical levels\nthreshold_boundary_layer::Float64: Relative humidity threshold for boundary layer\nthreshold_range::Float64: Vertical range of relative humidity threshold\nthreshold_max::Float64: Maximum relative humidity threshold [1]\ntime_scale::Float64: Relaxation time for humidity [hrs]\nn_stratosphere_levels::Base.RefValue{Int64}\nhumid_tend_max::Vector{NF} where NF<:AbstractFloat\nrelative_threshold::Vector{NF} where NF<:AbstractFloat\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SpeedyTransforms.SpectralTransform-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.SpeedyTransforms.SpectralTransform","text":"SpectralTransform(\n spectral_grid::SpectralGrid;\n recompute_legendre,\n one_more_degree,\n kwargs...\n) -> SpectralTransform\n\n\nGenerator function for a SpectralTransform struct pulling in parameters from a SpectralGrid struct.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.StartFromFile","page":"Function and type index","title":"SpeedyWeather.StartFromFile","text":"Restart from a previous SpeedyWeather.jl simulation via the restart file restart.jld2 Applies interpolation in the horizontal but not in the vertical. restart.jld2 is identified by\n\npath::String: path for restart file\nid::Union{Int64, String}: run_id of restart file in run_????/restart.jld2\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.StartWithRandomVorticity","page":"Function and type index","title":"SpeedyWeather.StartWithRandomVorticity","text":"Start with random vorticity as initial conditions\n\npower::Float64: Power of the spectral distribution k^power\namplitude::Float64: (approximate) amplitude in [1/s], used as standard deviation of spherical harmonic coefficients\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.StaticEnergyDiffusion","page":"Function and type index","title":"SpeedyWeather.StaticEnergyDiffusion","text":"Diffusion of dry static energy: A relaxation towards a reference gradient of static energy wrt to geopotential, see Fortran SPEEDY documentation.\n\ntime_scale::Float64: time scale [hrs] for strength\nstatic_energy_lapse_rate::Float64: [1] ∂SE/∂Φ, vertical gradient of static energy SE with geopotential Φ\nFstar::Base.RefValue{NF} where NF<:AbstractFloat\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Tendencies","page":"Function and type index","title":"SpeedyWeather.Tendencies","text":"Tendencies{Grid<:AbstractGrid,NF<:AbstractFloat}\n\nStruct holding the tendencies of the prognostic spectral variables for a given layer.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ZonalJet","page":"Function and type index","title":"SpeedyWeather.ZonalJet","text":"Create a struct that contains all parameters for the Galewsky et al, 2004 zonal jet intitial conditions for the shallow water model. Default values as in Galewsky.\n\nlatitude::Float64: jet latitude [˚N]\nwidth::Float64: jet width [˚], default ≈ 19.29˚\numax::Float64: jet maximum velocity [m/s]\nperturb_lat::Float64: perturbation latitude [˚N], position in jet by default\nperturb_lon::Float64: perturbation longitude [˚E]\nperturb_xwidth::Float64: perturbation zonal extent [˚], default ≈ 19.1˚\nperturb_ywidth::Float64: perturbation meridinoal extent [˚], default ≈ 3.8˚\nperturb_height::Float64: perturbation amplitude [m]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ZonalRidge","page":"Function and type index","title":"SpeedyWeather.ZonalRidge","text":"Zonal ridge orography after Jablonowski and Williamson, 2006.\n\nη₀::Float64: conversion from σ to Jablonowski's ηᵥ-coordinates\nu₀::Float64: max amplitude of zonal wind [m/s] that scales orography height\norography::SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat: height [m] on grid-point space.\ngeopot_surf::LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat: surface geopotential, height*gravity [m²/s²]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ZonalRidge-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.ZonalRidge","text":"ZonalRidge(spectral_grid::SpectralGrid; kwargs...) -> Any\n\n\nGenerator function pulling the resolution information from spectral_grid.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.ZonalWind","page":"Function and type index","title":"SpeedyWeather.ZonalWind","text":"Create a struct that contains all parameters for the Jablonowski and Williamson, 2006 intitial conditions for the primitive equation model. Default values as in Jablonowski.\n\nη₀::Float64: conversion from σ to Jablonowski's ηᵥ-coordinates\nu₀::Float64: max amplitude of zonal wind [m/s]\nperturb_lat::Float64: perturbation centred at [˚N]\nperturb_lon::Float64: perturbation centred at [˚E]\nperturb_uₚ::Float64: perturbation strength [m/s]\nperturb_radius::Float64: radius of Gaussian perturbation in units of Earth's radius [1]\nΔT::Float64: temperature difference used for stratospheric lapse rate [K], Jablonowski uses ΔT = 4.8e5 [K]\nTmin::Float64: minimum temperature [K] of profile\npressure_on_orography::Bool: initialize pressure given the atmosphere.lapse_rate on orography?\n\n\n\n\n\n","category":"type"},{"location":"functions/#Base.copy!-Tuple{PrognosticVariables, PrognosticVariables}","page":"Function and type index","title":"Base.copy!","text":"copy!(progn_new::PrognosticVariables, progn_old::PrognosticVariables)\n\nCopies entries of progn_old into progn_new. Only copies those variables that are present in the model of both progn_new and progn_old.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.Device-Tuple{}","page":"Function and type index","title":"SpeedyWeather.Device","text":"Device()\n\nReturn default used device for internal purposes, either CPUDevice or GPUDevice if a GPU is available.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.DeviceArray-Tuple{SpeedyWeather.GPUDevice, Any}","page":"Function and type index","title":"SpeedyWeather.DeviceArray","text":"DeviceArray(device::AbstractDevice, x)\n\nAdapts x to a CuArray when device<:GPUDevice is used, otherwise a regular Array. Uses adapt, thus also can return SubArrays etc.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.DeviceArrayNotAdapt-Tuple{SpeedyWeather.GPUDevice, Any}","page":"Function and type index","title":"SpeedyWeather.DeviceArrayNotAdapt","text":"DeviceArrayNotAdapt(device::AbstractDevice, x)\n\nReturns a CuArray when device<:GPUDevice is used, otherwise a regular Array. Doesn't uses adapt, therefore always returns CuArray/Array\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.Device_KernelAbstractions-Tuple{SpeedyWeather.CPUDevice}","page":"Function and type index","title":"SpeedyWeather.Device_KernelAbstractions","text":"Device_KernelAbstractions(::AbstractDevice)\n\nReturn used device for use with KernelAbstractions\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.Device_KernelAbstractions-Tuple{}","page":"Function and type index","title":"SpeedyWeather.Device_KernelAbstractions","text":"Device_KernelAbstractions()\n\nReturn default used device for KernelAbstractions, either CPU or CUDADevice if a GPU is available\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SpeedyTransforms.gridded!-Tuple{DiagnosticVariables, PrognosticVariables, Int64, SpeedyWeather.ModelSetup}","page":"Function and type index","title":"SpeedyWeather.SpeedyTransforms.gridded!","text":"gridded!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Int64,\n model::SpeedyWeather.ModelSetup\n)\n\n\nPropagate the spectral state of progn to diagn using time step/leapfrog index lf. Function barrier that calls gridded! for the respective model.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SpeedyTransforms.gridded!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, SpeedyWeather.PrognosticLayerTimesteps, Int64, Barotropic}","page":"Function and type index","title":"SpeedyWeather.SpeedyTransforms.gridded!","text":"gridded!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n progn::SpeedyWeather.PrognosticLayerTimesteps,\n lf::Int64,\n model::Barotropic\n)\n\n\nPropagate the spectral state of the prognostic variables progn to the diagnostic variables in diagn for the barotropic vorticity model. Updates grid vorticity, spectral stream function and spectral and grid velocities u,v.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SpeedyTransforms.gridded!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, SpeedyWeather.PrognosticLayerTimesteps, Int64, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.SpeedyTransforms.gridded!","text":"gridded!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n progn::SpeedyWeather.PrognosticLayerTimesteps,\n lf::Int64,\n model::PrimitiveEquation\n)\n\n\nPropagate the spectral state of the prognostic variables progn to the diagnostic variables in diagn for primitive equation models. Updates grid vorticity, grid divergence, grid temperature, pressure (pres_grid) and the velocities u,v.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SpeedyTransforms.gridded!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, SpeedyWeather.PrognosticLayerTimesteps, Int64, ShallowWater}","page":"Function and type index","title":"SpeedyWeather.SpeedyTransforms.gridded!","text":"gridded!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n progn::SpeedyWeather.PrognosticLayerTimesteps,\n lf::Int64,\n model::ShallowWater\n)\n\n\nPropagate the spectral state of the prognostic variables progn to the diagnostic variables in diagn for the shallow water model. Updates grid vorticity, grid divergence, grid interface displacement (pres_grid) and the velocities u,v.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.bernoulli_potential!-Union{Tuple{NF}, Tuple{SpeedyWeather.DiagnosticVariablesLayer{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, SpectralTransform}} where NF","page":"Function and type index","title":"SpeedyWeather.bernoulli_potential!","text":"bernoulli_potential!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF},\n S::SpectralTransform\n) -> LowerTriangularMatrix{Complex{NF}} where NF\n\n\nComputes the Laplace operator ∇² of the Bernoulli potential B in spectral space.\n\ncomputes the kinetic energy KE = ½(u²+v²) on the grid\ntransforms KE to spectral space\nadds geopotential for the Bernoulli potential in spectral space\ntakes the Laplace operator.\n\nThis version is used for both ShallowWater and PrimitiveEquation, only the geopotential calculation in geopotential! differs.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.boundary_layer_drag!-Tuple{ColumnVariables, LinearDrag}","page":"Function and type index","title":"SpeedyWeather.boundary_layer_drag!","text":"boundary_layer_drag!(\n column::ColumnVariables,\n scheme::LinearDrag\n)\n\n\nCompute tendency for boundary layer drag of a column and add to its tendencies fields\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.boundary_layer_drag!-Tuple{ColumnVariables, SpeedyWeather.NoBoundaryLayerDrag, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.boundary_layer_drag!","text":"NoBoundaryLayer scheme just passes.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.clip_negatives!-Union{Tuple{AbstractArray{T}}, Tuple{T}} where T","page":"Function and type index","title":"SpeedyWeather.clip_negatives!","text":"clip_negatives!(A::AbstractArray)\n\nSet all negative entries a in A to zero.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.coriolis-Tuple{Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid","page":"Function and type index","title":"SpeedyWeather.coriolis","text":"coriolis(\n grid::SpeedyWeather.RingGrids.AbstractGrid;\n rotation\n) -> Any\n\n\nReturn the Coriolis parameter f on a grid like grid on a planet of ratation [1/s]. Default rotation of Earth.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.coriolis-Union{Tuple{Grid}, Tuple{Type{Grid}, Integer}} where Grid<:SpeedyWeather.RingGrids.AbstractGrid","page":"Function and type index","title":"SpeedyWeather.coriolis","text":"coriolis(\n grid::SpeedyWeather.RingGrids.AbstractGrid;\n rotation\n) -> Any\n\n\nReturn the Coriolis parameter f on the grid Grid of resolution nlat_half on a planet of ratation [1/s]. Default rotation of Earth.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.create_output_folder-Tuple{String, Union{Int64, String}}","page":"Function and type index","title":"SpeedyWeather.create_output_folder","text":"create_output_folder(\n path::String,\n id::Union{Int64, String}\n) -> String\n\n\nCreates a new folder run_* with the identification id. Also returns the full path run_path of that folder.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.default_sigma_coordinates-Tuple{Integer}","page":"Function and type index","title":"SpeedyWeather.default_sigma_coordinates","text":"default_sigma_coordinates(nlev::Integer) -> Any\n\n\nVertical sigma coordinates defined by their nlev+1 half levels σ_levels_half. Sigma coordinates are fraction of surface pressure (p/p0) and are sorted from top (stratosphere) to bottom (surface). The first half level is at 0 the last at 1. Evaluate a generalised logistic function with coefficients in P for the distribution of values in between. Default coefficients follow the L31 configuration historically used at ECMWF.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.drag!-Union{Tuple{NF}, Tuple{SpeedyWeather.DiagnosticVariablesLayer, QuadraticDrag{NF}, DynamicsConstants}} where NF","page":"Function and type index","title":"SpeedyWeather.drag!","text":"drag!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n drag::QuadraticDrag{NF},\n C::DynamicsConstants\n)\n\n\nQuadratic drag for the momentum equations.\n\nF = -c_D/H*|(u,v)|*(u,v)\n\nwith c_D the non-dimensional drag coefficient as defined in drag::QuadraticDrag. Layer thickness H is taken from the Atmosphere via DynamicsConstants, as defined for the ShallowWaterModel.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.dry_static_energy!-Tuple{ColumnVariables, DynamicsConstants}","page":"Function and type index","title":"SpeedyWeather.dry_static_energy!","text":"dry_static_energy!(\n column::ColumnVariables,\n constants::DynamicsConstants\n)\n\n\nCompute the dry static energy SE = cₚT + Φ (latent heat times temperature plus geopotential) for the column.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.dynamics_tendencies!","page":"Function and type index","title":"SpeedyWeather.dynamics_tendencies!","text":"dynamics_tendencies!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n model::PrimitiveEquation\n) -> Any\ndynamics_tendencies!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n model::PrimitiveEquation,\n lf::Int64\n) -> Any\n\n\nCalculate all tendencies for the PrimitiveEquation model (wet or dry).\n\n\n\n\n\n","category":"function"},{"location":"functions/#SpeedyWeather.dynamics_tendencies!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, Dates.DateTime, Barotropic}","page":"Function and type index","title":"SpeedyWeather.dynamics_tendencies!","text":"dynamics_tendencies!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n time::Dates.DateTime,\n model::Barotropic\n)\n\n\nCalculate all tendencies for the BarotropicModel.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.dynamics_tendencies!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, SpeedyWeather.SurfaceVariables, LowerTriangularMatrix, Dates.DateTime, ShallowWater}","page":"Function and type index","title":"SpeedyWeather.dynamics_tendencies!","text":"dynamics_tendencies!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n surface::SpeedyWeather.SurfaceVariables,\n pres::LowerTriangularMatrix,\n time::Dates.DateTime,\n model::ShallowWater\n)\n\n\nCalculate all tendencies for the ShallowWaterModel.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.first_timesteps!-Tuple{PrognosticVariables, DiagnosticVariables, SpeedyWeather.ModelSetup, SpeedyWeather.AbstractOutputWriter}","page":"Function and type index","title":"SpeedyWeather.first_timesteps!","text":"first_timesteps!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::SpeedyWeather.ModelSetup,\n output::SpeedyWeather.AbstractOutputWriter\n) -> typeof(time)\n\n\nPerforms the first two initial time steps (Euler forward, unfiltered leapfrog) to populate the prognostic variables with two time steps (t=0,Δt) that can then be used in the normal leap frogging.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.flipsign!-Tuple{AbstractArray}","page":"Function and type index","title":"SpeedyWeather.flipsign!","text":"flipgsign!(A::AbstractArray)\n\nLike -A but in-place.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.flux_divergence!-Union{Tuple{NF}, Tuple{LowerTriangularMatrix{Complex{NF}}, SpeedyWeather.RingGrids.AbstractGrid{NF}, SpeedyWeather.DiagnosticVariablesLayer{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Geometry{NF}, SpectralTransform{NF}}} where NF","page":"Function and type index","title":"SpeedyWeather.flux_divergence!","text":"flux_divergence!(\n A_tend::LowerTriangularMatrix{Complex{NF}},\n A_grid::SpeedyWeather.RingGrids.AbstractGrid{NF},\n diagn::SpeedyWeather.DiagnosticVariablesLayer{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF},\n G::Geometry{NF},\n S::SpectralTransform{NF};\n add,\n flipsign\n)\n\n\nComputes ∇⋅((u,v)*A) with the option to add/overwrite A_tend and to flip_sign of the flux divergence by doing so.\n\nA_tend = ∇⋅((u,v)*A) for add=false, flip_sign=false\nA_tend = -∇⋅((u,v)*A) for add=false, flip_sign=true\nA_tend += ∇⋅((u,v)*A) for add=true, flip_sign=false\nA_tend -= ∇⋅((u,v)*A) for add=true, flip_sign=true\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.fluxes_to_tendencies!-Tuple{ColumnVariables, Geometry, DynamicsConstants}","page":"Function and type index","title":"SpeedyWeather.fluxes_to_tendencies!","text":"fluxes_to_tendencies!(\n column::ColumnVariables,\n geometry::Geometry,\n constants::DynamicsConstants\n)\n\n\nConvert the fluxes on half levels to tendencies on full levels.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.forcing!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, JetStreamForcing}","page":"Function and type index","title":"SpeedyWeather.forcing!","text":"forcing!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n forcing::JetStreamForcing\n)\n\n\nSet for every latitude ring the tendency to the precomputed forcing in the momentum equations following the JetStreamForcing. The forcing is precomputed in initialize!(::JetStreamForcing,::ModelSetup).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.generalised_logistic-Tuple{Any, SpeedyWeather.GenLogisticCoefs}","page":"Function and type index","title":"SpeedyWeather.generalised_logistic","text":"Generalised logistic function based on the coefficients in coefs.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.geopotential!-Tuple{DiagnosticVariables, SpeedyWeather.AbstractOrography, DynamicsConstants}","page":"Function and type index","title":"SpeedyWeather.geopotential!","text":"geopotential!(\n diagn::DiagnosticVariables,\n O::SpeedyWeather.AbstractOrography,\n C::DynamicsConstants\n)\n\n\nCompute spectral geopotential geopot from spectral temperature temp and spectral surface geopotential geopot_surf (orography*gravity).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.geopotential!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, LowerTriangularMatrix, DynamicsConstants}","page":"Function and type index","title":"SpeedyWeather.geopotential!","text":"geopotential!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n pres::LowerTriangularMatrix,\n C::DynamicsConstants\n) -> Any\n\n\ncalculates the geopotential in the ShallowWaterModel as g*η, i.e. gravity times the interface displacement (field pres)\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.geopotential!-Tuple{Vector, DynamicsConstants}","page":"Function and type index","title":"SpeedyWeather.geopotential!","text":"geopotential!(temp::Vector, C::DynamicsConstants) -> Vector\n\n\nCalculate the geopotential based on temp in a single column. This exclues the surface geopotential that would need to be added to the returned vector. Function not used in the dynamical core but for post-processing and analysis.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.get_column!-Tuple{ColumnVariables, DiagnosticVariables, Int64, Geometry, LandSeaMask}","page":"Function and type index","title":"SpeedyWeather.get_column!","text":"Recalculate ring index if not provided.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.get_column!-Tuple{ColumnVariables, DiagnosticVariables, Integer, Integer, Geometry, SpeedyWeather.AbstractLandSeaMask}","page":"Function and type index","title":"SpeedyWeather.get_column!","text":"get_column!(\n C::ColumnVariables,\n D::DiagnosticVariables,\n ij::Integer,\n jring::Integer,\n G::Geometry,\n L::SpeedyWeather.AbstractLandSeaMask\n)\n\n\nUpdate C::ColumnVariables by copying the prognostic variables from D::DiagnosticVariables at gridpoint index ij. Provide G::Geometry for coordinate information.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.get_full_output_file_path-Tuple{OutputWriter}","page":"Function and type index","title":"SpeedyWeather.get_full_output_file_path","text":"get_full_output_file_path(output::OutputWriter) -> String\n\n\nReturns the full path of the output file after it was created.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.get_run_id-Tuple{String, String}","page":"Function and type index","title":"SpeedyWeather.get_run_id","text":"get_run_id(path::String, id::String) -> String\n\n\nChecks existing run_???? folders in path to determine a 4-digit id number by counting up. E.g. if folder run_0001 exists it will return the string \"0002\". Does not create a folder for the returned run id.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.get_thermodynamics!-Tuple{ColumnVariables, PrimitiveDry}","page":"Function and type index","title":"SpeedyWeather.get_thermodynamics!","text":"get_thermodynamics!(\n column::ColumnVariables,\n model::PrimitiveDry\n)\n\n\nCalculate the dry static energy for the primitive dry model.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.get_thermodynamics!-Tuple{ColumnVariables, PrimitiveWet}","page":"Function and type index","title":"SpeedyWeather.get_thermodynamics!","text":"get_thermodynamics!(\n column::ColumnVariables,\n model::PrimitiveWet\n)\n\n\nCalculate thermodynamic quantities like saturation vapour pressure, saturation specific humidity, dry static energy, moist static energy and saturation moist static energy from the prognostic column variables.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.get_var-Tuple{PrognosticVariables, Symbol}","page":"Function and type index","title":"SpeedyWeather.get_var","text":"get_var(progn::PrognosticVariables, var_name::Symbol; lf::Integer=1)\n\nReturns the prognostic variable var_name at leapfrog index lf as a Vector{LowerTriangularMatrices}.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.has-Tuple{Type{<:SpeedyWeather.ModelSetup}, Symbol}","page":"Function and type index","title":"SpeedyWeather.has","text":"has(\n M::Type{<:SpeedyWeather.ModelSetup},\n var_name::Symbol\n) -> Bool\n\n\nReturns true if the model M has a prognostic variable var_name, false otherwise. The default fallback is that all variables are included. \n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.horizontal_diffusion!","page":"Function and type index","title":"SpeedyWeather.horizontal_diffusion!","text":"horizontal_diffusion!(\n progn::SpeedyWeather.PrognosticLayerTimesteps,\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n model::ShallowWater\n)\nhorizontal_diffusion!(\n progn::SpeedyWeather.PrognosticLayerTimesteps,\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n model::ShallowWater,\n lf::Int64\n)\n\n\nApply horizontal diffusion to vorticity and diffusion in the ShallowWater models.\n\n\n\n\n\n","category":"function"},{"location":"functions/#SpeedyWeather.horizontal_diffusion!-2","page":"Function and type index","title":"SpeedyWeather.horizontal_diffusion!","text":"horizontal_diffusion!(\n progn::SpeedyWeather.PrognosticLayerTimesteps,\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n model::PrimitiveEquation\n) -> Union{Nothing, Bool}\nhorizontal_diffusion!(\n progn::SpeedyWeather.PrognosticLayerTimesteps,\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n model::PrimitiveEquation,\n lf::Int64\n) -> Union{Nothing, Bool}\n\n\nApply horizontal diffusion applied to vorticity, diffusion and temperature in the PrimitiveEquation models. Uses the constant diffusion for temperature but possibly adaptive diffusion for vorticity and divergence.\n\n\n\n\n\n","category":"function"},{"location":"functions/#SpeedyWeather.horizontal_diffusion!-3","page":"Function and type index","title":"SpeedyWeather.horizontal_diffusion!","text":"horizontal_diffusion!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n progn::SpeedyWeather.PrognosticLayerTimesteps,\n model::Barotropic\n)\nhorizontal_diffusion!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n progn::SpeedyWeather.PrognosticLayerTimesteps,\n model::Barotropic,\n lf::Int64\n)\n\n\nApply horizontal diffusion to vorticity in the Barotropic models.\n\n\n\n\n\n","category":"function"},{"location":"functions/#SpeedyWeather.horizontal_diffusion!-Union{Tuple{NF}, Tuple{LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, AbstractVector{NF}, AbstractVector{NF}}} where NF<:AbstractFloat","page":"Function and type index","title":"SpeedyWeather.horizontal_diffusion!","text":"horizontal_diffusion!(\n tendency::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n A::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n ∇²ⁿ_expl::AbstractArray{NF<:AbstractFloat, 1},\n ∇²ⁿ_impl::AbstractArray{NF<:AbstractFloat, 1}\n)\n\n\nApply horizontal diffusion to a 2D field A in spectral space by updating its tendency tendency with an implicitly calculated diffusion term. The implicit diffusion of the next time step is split into an explicit part ∇²ⁿ_expl and an implicit part ∇²ⁿ_impl, such that both can be calculated in a single forward step by using A as well as its tendency tendency.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.implicit_correction!-Tuple{DiagnosticVariables, ImplicitPrimitiveEq, PrognosticVariables}","page":"Function and type index","title":"SpeedyWeather.implicit_correction!","text":"implicit_correction!(\n diagn::DiagnosticVariables,\n implicit::ImplicitPrimitiveEq,\n progn::PrognosticVariables\n) -> Any\n\n\nApply the implicit corrections to dampen gravity waves in the primitive equation models.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.implicit_correction!-Union{Tuple{NF}, Tuple{SpeedyWeather.DiagnosticVariablesLayer{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, SpeedyWeather.PrognosticLayerTimesteps{NF}, SpeedyWeather.SurfaceVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, SpeedyWeather.PrognosticSurfaceTimesteps{NF}, ImplicitShallowWater}} where NF","page":"Function and type index","title":"SpeedyWeather.implicit_correction!","text":"implicit_correction!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF},\n progn::SpeedyWeather.PrognosticLayerTimesteps{NF},\n diagn_surface::SpeedyWeather.SurfaceVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF},\n progn_surface::SpeedyWeather.PrognosticSurfaceTimesteps{NF},\n implicit::ImplicitShallowWater\n)\n\n\nApply correction to the tendencies in diagn to prevent the gravity waves from amplifying. The correction is implicitly evaluated using the parameter implicit.α to switch between forward, centered implicit or backward evaluation of the gravity wave terms.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n scheme::HyperDiffusion,\n k::Int64,\n G::Geometry,\n L::SpeedyWeather.TimeStepper\n)\ninitialize!(\n scheme::HyperDiffusion,\n k::Int64,\n G::Geometry,\n L::SpeedyWeather.TimeStepper,\n vor_max::Real\n)\n\n\nPrecomputes the hyper diffusion terms in scheme for layer k based on the model time step in L, the vertical level sigma level in G, and the current (absolute) vorticity maximum level vor_max\n\n\n\n\n\n","category":"function"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{Barotropic}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(model::Barotropic) -> SpeedyWeather.Simulation\n\n\nCalls all initialize! functions for components of model, except for model.output and model.feedback which are always called at in time_stepping!.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{EarthOrography, SpeedyWeather.AbstractPlanet, SpectralTransform, Geometry}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n orog::EarthOrography,\n P::SpeedyWeather.AbstractPlanet,\n S::SpectralTransform,\n G::Geometry\n) -> LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat\n\n\nInitialize the arrays orography,geopot_surf in orog by reading the orography field from file.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{Feedback, SpeedyWeather.Clock, SpeedyWeather.ModelSetup}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n feedback::Feedback,\n clock::SpeedyWeather.Clock,\n model::SpeedyWeather.ModelSetup\n) -> Union{Nothing, IOStream}\n\n\nInitializes the a Feedback struct.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{HeldSuarez, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(scheme::HeldSuarez, model::PrimitiveEquation)\n\n\ninitialize the HeldSuarez temperature relaxation by precomputing terms for the equilibrium temperature Teq.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{HyperDiffusion, SpeedyWeather.DiagnosticVariablesLayer, Geometry, SpeedyWeather.TimeStepper}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n scheme::HyperDiffusion,\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n G::Geometry,\n L::SpeedyWeather.TimeStepper\n)\n\n\nPre-function to other initialize!(::HyperDiffusion) initialisors that calculates the (absolute) vorticity maximum for the layer of diagn.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{HyperDiffusion, SpeedyWeather.ModelSetup}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n scheme::HyperDiffusion,\n model::SpeedyWeather.ModelSetup\n)\n\n\nPrecomputes the hyper diffusion terms in scheme based on the model time step, and possibly with a changing strength/power in the vertical.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{HyperDiffusion, SpeedyWeather.TimeStepper}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n scheme::HyperDiffusion,\n L::SpeedyWeather.TimeStepper\n)\n\n\nPrecomputes the 2D hyper diffusion terms in scheme based on the model time step.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{ImplicitPrimitiveEq, Real, DiagnosticVariables, Geometry, DynamicsConstants}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n implicit::ImplicitPrimitiveEq,\n dt::Real,\n diagn::DiagnosticVariables,\n geometry::Geometry,\n constants::DynamicsConstants\n)\n\n\nInitialize the implicit terms for the PrimitiveEquation models.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{ImplicitShallowWater, Real, DynamicsConstants}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n implicit::ImplicitShallowWater,\n dt::Real,\n constants::DynamicsConstants\n)\n\n\nUpdate the implicit terms in implicit for the shallow water model as they depend on the time step dt.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{JablonowskiRelaxation, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n scheme::JablonowskiRelaxation,\n model::PrimitiveEquation\n)\n\n\ninitialize the JablonowskiRelaxation temperature relaxation by precomputing terms for the equilibrium temperature Teq and the frequency (strength of relaxation).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{LinearDrag, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(scheme::LinearDrag, model::PrimitiveEquation)\n\n\nPrecomputes the drag coefficients for this BoundaryLayerDrag scheme.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{NoTemperatureRelaxation, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n scheme::NoTemperatureRelaxation,\n model::PrimitiveEquation\n)\n\n\njust passes, does not need any initialization.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{PrimitiveDry}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(model::PrimitiveDry) -> SpeedyWeather.Simulation\n\n\nCalls all initialize! functions for components of model, except for model.output and model.feedback which are always called at in time_stepping! and model.implicit which is done in first_timesteps!.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{PrimitiveWet}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(model::PrimitiveWet) -> SpeedyWeather.Simulation\n\n\nCalls all initialize! functions for components of model, except for model.output and model.feedback which are always called at in time_stepping! and model.implicit which is done in first_timesteps!.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{PrognosticVariables, StartFromFile, SpeedyWeather.ModelSetup}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn_new::PrognosticVariables,\n initial_conditions::StartFromFile,\n model::SpeedyWeather.ModelSetup\n) -> PrognosticVariables\n\n\nRestart from a previous SpeedyWeather.jl simulation via the restart file restart.jld2 Applies interpolation in the horizontal but not in the vertical.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{PrognosticVariables, ZonalJet, ShallowWater}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn::PrognosticVariables,\n initial_conditions::ZonalJet,\n model::ShallowWater\n) -> LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat\n\n\nInitial conditions from Galewsky, 2004, Tellus\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{ShallowWater}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(model::ShallowWater) -> SpeedyWeather.Simulation\n\n\nCalls all initialize! functions for components of model, except for model.output and model.feedback which are always called at in time_stepping! and model.implicit which is done in first_timesteps!.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{SpeedyCondensation, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n scheme::SpeedyCondensation,\n model::PrimitiveEquation\n)\n\n\nInitialize the SpeedyCondensation scheme.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{SpeedyWeather.Clock, SpeedyWeather.TimeStepper}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n clock::SpeedyWeather.Clock,\n time_stepping::SpeedyWeather.TimeStepper\n) -> SpeedyWeather.Clock\n\n\nInitialize the clock with the time step Δt in the time_stepping.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{SpeedyWeather.NoBoundaryLayerDrag, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"NoBoundaryLayer scheme does not need any initialisation.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{ZonalRidge, SpeedyWeather.AbstractPlanet, SpectralTransform, Geometry}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n orog::ZonalRidge,\n P::SpeedyWeather.AbstractPlanet,\n S::SpectralTransform,\n G::Geometry\n) -> LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat\n\n\nInitialize the arrays orography,geopot_surf in orog following Jablonowski and Williamson, 2006.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Union{Tuple{Model}, Tuple{output_NF}, Tuple{OutputWriter{output_NF, Model}, SpeedyWeather.AbstractFeedback, SpeedyWeather.TimeStepper, SpeedyWeather.Clock, DiagnosticVariables, Model}} where {output_NF, Model}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n output::OutputWriter{output_NF, Model},\n feedback::SpeedyWeather.AbstractFeedback,\n time_stepping::SpeedyWeather.TimeStepper,\n clock::SpeedyWeather.Clock,\n diagn::DiagnosticVariables,\n model\n)\n\n\nCreates a netcdf file on disk and the corresponding netcdf_file object preallocated with output variables and dimensions. write_output! then writes consecuitive time steps into this file.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Union{Tuple{NF}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, SpeedyWeather.RandomWaves, ShallowWater}} where NF","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn::PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF},\n initial_conditions::SpeedyWeather.RandomWaves,\n model::ShallowWater\n)\n\n\nRandom initial conditions for the interface displacement η in the shallow water equations. The flow (u,v) is zero initially. This kicks off gravity waves that will interact with orography.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Union{Tuple{NF}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, StartWithRandomVorticity, SpeedyWeather.ModelSetup}} where NF","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn::PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF},\n initial_conditions::StartWithRandomVorticity,\n model::SpeedyWeather.ModelSetup\n)\n\n\nStart with random vorticity as initial conditions\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Union{Tuple{NF}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, ZonalWind, PrimitiveEquation}} where NF","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn::PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF},\n initial_conditions::ZonalWind,\n model::PrimitiveEquation\n)\n\n\nInitial conditions from Jablonowski and Williamson, 2006, QJR Meteorol. Soc\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Union{Tuple{NF}, Tuple{SpeedyWeather.StaticEnergyDiffusion{NF}, PrimitiveEquation}} where NF","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n scheme::SpeedyWeather.StaticEnergyDiffusion{NF},\n model::PrimitiveEquation\n) -> Any\n\n\nInitialize dry static energy diffusion.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize_geopotential-Tuple{Vector, Vector, Real}","page":"Function and type index","title":"SpeedyWeather.initialize_geopotential","text":"initialize_geopotential(\n σ_levels_full::Vector,\n σ_levels_half::Vector,\n R_dry::Real\n) -> Tuple{Vector{Float64}, Vector{Float64}}\n\n\nPrecomputes constants for the vertical integration of the geopotential, defined as\n\nΦ_{k+1/2} = Φ_{k+1} + R*T_{k+1}*(ln(p_{k+1}) - ln(p_{k+1/2})) (half levels) Φ_k = Φ_{k+1/2} + R*T_k*(ln(p_{k+1/2}) - ln(p_k)) (full levels)\n\nSame formula but k → k-1/2.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.isdecreasing-Tuple{AbstractVector}","page":"Function and type index","title":"SpeedyWeather.isdecreasing","text":"isdecreasing(x::AbstractVector) -> Bool\n\n\nCheck whether elements of a vector v are strictly decreasing.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.isincreasing-Tuple{AbstractVector}","page":"Function and type index","title":"SpeedyWeather.isincreasing","text":"isincreasing(x::AbstractVector) -> Bool\n\n\nCheck whether elements of a vector v are strictly increasing.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.large_scale_condensation!-Tuple{ColumnVariables, PrimitiveDry}","page":"Function and type index","title":"SpeedyWeather.large_scale_condensation!","text":"large_scale_condensation!(\n column::ColumnVariables,\n model::PrimitiveDry\n)\n\n\nNo condensation in a PrimitiveDry model.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.large_scale_condensation!-Tuple{ColumnVariables, PrimitiveWet}","page":"Function and type index","title":"SpeedyWeather.large_scale_condensation!","text":"Function barrier only.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.large_scale_condensation!-Union{Tuple{NF}, Tuple{ColumnVariables{NF}, SpeedyCondensation, Geometry, DynamicsConstants, SpeedyWeather.AbstractAtmosphere, SpeedyWeather.TimeStepper}} where NF","page":"Function and type index","title":"SpeedyWeather.large_scale_condensation!","text":"large_scale_condensation!(\n column::ColumnVariables{NF},\n scheme::SpeedyCondensation,\n geometry::Geometry,\n constants::DynamicsConstants,\n atmosphere::SpeedyWeather.AbstractAtmosphere,\n time_stepping::SpeedyWeather.TimeStepper\n)\n\n\nLarge-scale condensation for a column by relaxation back to a reference relative humidity if larger than that. Calculates the tendencies for specific humidity and temperature and integrates the large-scale precipitation vertically for output.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.launch_kernel!-Tuple{SpeedyWeather.DeviceSetup, Any, Any, Vararg{Any}}","page":"Function and type index","title":"SpeedyWeather.launch_kernel!","text":"launch_kernel!(device_setup::DeviceSetup, kernel!, ndrange, kernel_args...)\n\nLaunches the kernel! on the device_setup with ndrange computations over the kernel and arguments kernel_args\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.leapfrog!-Union{Tuple{NF}, Tuple{LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, Real, Int64, Leapfrog{NF}}} where NF<:AbstractFloat","page":"Function and type index","title":"SpeedyWeather.leapfrog!","text":"leapfrog!(\n A_old::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n A_new::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n tendency::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n dt::Real,\n lf::Int64,\n L::Leapfrog{NF<:AbstractFloat}\n)\n\n\nPerforms one leapfrog time step with (lf=2) or without (lf=1) Robert+Williams filter (see Williams (2009), Montly Weather Review, Eq. 7-9).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.linear_pressure_gradient!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, SpeedyWeather.PrognosticSurfaceTimesteps, Int64, DynamicsConstants, ImplicitPrimitiveEq}","page":"Function and type index","title":"SpeedyWeather.linear_pressure_gradient!","text":"linear_pressure_gradient!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n surface::SpeedyWeather.PrognosticSurfaceTimesteps,\n lf::Int64,\n C::DynamicsConstants,\n I::ImplicitPrimitiveEq\n) -> LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat\n\n\nAdd the linear contribution of the pressure gradient to the geopotential. The pressure gradient in the divergence equation takes the form\n\n-∇⋅(Rd*Tᵥ*∇lnpₛ) = -∇⋅(Rd*Tᵥ'*∇lnpₛ) - ∇²(Rd*Tₖ*lnpₛ)\n\nSo that the second term inside the Laplace operator can be added to the geopotential. Rd is the gas constant, Tᵥ the virtual temperature and Tᵥ' its anomaly wrt to the average or reference temperature Tₖ, lnpₛ is the logarithm of surface pressure.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.linear_virtual_temperature!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, SpeedyWeather.PrognosticLayerTimesteps, DynamicsConstants, Int64}","page":"Function and type index","title":"SpeedyWeather.linear_virtual_temperature!","text":"linear_virtual_temperature!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n progn::SpeedyWeather.PrognosticLayerTimesteps,\n constants::DynamicsConstants,\n lf::Int64\n) -> Any\n\n\nCalculates a linearised virtual temperature Tᵥ as\n\nTᵥ = T + Tₖμq\n\nWith absolute temperature T, layer-average temperarture Tₖ (computed in temperature_average!), specific humidity q and\n\nμ = (1-ξ)/ξ, ξ = R_dry/R_vapour.\n\nin spectral space.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.linear_virtual_temperature!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, SpeedyWeather.PrognosticLayerTimesteps, PrimitiveDry, Integer}","page":"Function and type index","title":"SpeedyWeather.linear_virtual_temperature!","text":"linear_virtual_temperature!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n progn::SpeedyWeather.PrognosticLayerTimesteps,\n model::PrimitiveDry,\n lf::Integer\n) -> LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat\n\n\nLinear virtual temperature for model::PrimitiveDry: Just copy over arrays from temp to temp_virt at timestep lf in spectral space as humidity is zero in this model.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.load_trajectory-Tuple{Union{String, Symbol}, SpeedyWeather.ModelSetup}","page":"Function and type index","title":"SpeedyWeather.load_trajectory","text":"load_trajectory(\n var_name::Union{String, Symbol},\n model::SpeedyWeather.ModelSetup\n) -> Any\n\n\nLoads a var_name trajectory of the model M that has been saved in a netCDF file during the time stepping.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.moist_static_energy!-Tuple{ColumnVariables, SpeedyWeather.Thermodynamics}","page":"Function and type index","title":"SpeedyWeather.moist_static_energy!","text":"moist_static_energy!(\n column::ColumnVariables,\n thermodynamics::SpeedyWeather.Thermodynamics\n)\n\n\nCompute the moist static energy\n\nMSE = SE + Lc*Q = cₚT + Φ + Lc*Q\n\nwith the static energy SE, the latent heat of condensation Lc, the geopotential Φ. As well as the saturation moist static energy which replaces Q with Q_sat\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.nans-Tuple","page":"Function and type index","title":"SpeedyWeather.nans","text":"A = nans(dims...)\n\nAllocate A::Array{Float64} with NaNs.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.nans-Union{Tuple{T}, Tuple{Type{T}, Vararg{Any}}} where T","page":"Function and type index","title":"SpeedyWeather.nans","text":"A = nans(T,dims...)\n\nAllocate array A with NaNs of type T. Similar to zeros(T,dims...).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.nar_detection!-Tuple{Feedback, PrognosticVariables}","page":"Function and type index","title":"SpeedyWeather.nar_detection!","text":"nar_detection!(\n feedback::Feedback,\n progn::PrognosticVariables\n) -> Union{Nothing, Bool}\n\n\nDetect NaR (Not-a-Real) in the prognostic variables.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.parameterization_tendencies!-Tuple{DiagnosticVariables, Dates.DateTime, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.parameterization_tendencies!","text":"parameterization_tendencies!(\n diagn::DiagnosticVariables,\n time::Dates.DateTime,\n model::PrimitiveEquation\n) -> Any\n\n\nCompute tendencies for u,v,temp,humid from physical parametrizations. Extract for each vertical atmospheric column the prognostic variables (stored in diagn as they are grid-point transformed), loop over all grid-points, compute all parametrizations on a single-column basis, then write the tendencies back into a horizontal field of tendencies.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.pressure_on_orography!-Tuple{PrognosticVariables, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.pressure_on_orography!","text":"pressure_on_orography!(\n progn::PrognosticVariables,\n model::PrimitiveEquation\n)\n\n\nInitialize surface pressure on orography by integrating the hydrostatic equation with the reference temperature lapse rate.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.print_fields-Tuple{IO, Any, Any}","page":"Function and type index","title":"SpeedyWeather.print_fields","text":"print_fields(io::IO, A, keys; arrays)\n\n\nPrints to io all fields of a struct A identified by their keys.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.progress!-Tuple{Feedback}","page":"Function and type index","title":"SpeedyWeather.progress!","text":"progress!(feedback::Feedback)\n\n\nCalls the progress meter and writes every 5% progress increase to txt.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.progress_finish!-Tuple{Feedback}","page":"Function and type index","title":"SpeedyWeather.progress_finish!","text":"progress_finish!(F::Feedback)\n\n\nFinalises the progress meter and the progress txt file.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.readable_secs-Tuple{Real}","page":"Function and type index","title":"SpeedyWeather.readable_secs","text":"readable_secs(secs::Real) -> Dates.CompoundPeriod\n\n\nReturns Dates.CompoundPeriod rounding to either (days, hours), (hours, minutes), (minutes, seconds), or seconds with 1 decimal place accuracy for >10s and two for less. E.g.\n\njulia> readable_secs(12345)\n3 hours, 26 minutes\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.remaining_time-Tuple{ProgressMeter.Progress}","page":"Function and type index","title":"SpeedyWeather.remaining_time","text":"remaining_time(p::ProgressMeter.Progress) -> String\n\n\nEstimates the remaining time from a ProgresssMeter.Progress. Adapted from ProgressMeter.jl\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.reset_column!-Union{Tuple{ColumnVariables{NF}}, Tuple{NF}} where NF","page":"Function and type index","title":"SpeedyWeather.reset_column!","text":"reset_column!(column::ColumnVariables{NF})\n\n\nSet the accumulators (tendencies but also vertical sums and similar) back to zero for column to be reused at other grid points.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.run!-Tuple{SpeedyWeather.Simulation}","page":"Function and type index","title":"SpeedyWeather.run!","text":"run!(\n simulation::SpeedyWeather.Simulation;\n initialize,\n n_days,\n startdate,\n output\n) -> PrognosticVariables\n\n\nRun a SpeedyWeather.jl simulation. The simulation.model is assumed to be initialized, otherwise use initialize=true as keyword argument.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.saturation_humidity!-Tuple{ColumnVariables, SpeedyWeather.Thermodynamics}","page":"Function and type index","title":"SpeedyWeather.saturation_humidity!","text":"saturation_humidity!(\n column::ColumnVariables,\n thermodynamics::SpeedyWeather.Thermodynamics\n)\n\n\nCompute (1) the saturation vapour pressure as a function of temperature using the August-Roche-Magnus formula,\n\neᵢ(T) = e₀ * exp(Cᵢ * (T - T₀) / (T - Tᵢ)),\n\nwhere T is in Kelvin and i = 1,2 for saturation with respect to water and ice, respectively. And (2) the saturation specific humidity according to the formula,\n\n0.622 * e / (p - (1 - 0.622) * e),\n\nwhere e is the saturation vapour pressure, p is the pressure, and 0.622 is the ratio of the molecular weight of water to dry air.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.scale!-Tuple{PrognosticVariables, Real}","page":"Function and type index","title":"SpeedyWeather.scale!","text":"scale!(progn::PrognosticVariables, scale::Real) -> Real\n\n\nScales the prognostic variables vorticity and divergence with the Earth's radius which is used in the dynamical core.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.scale!-Union{Tuple{NF}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Symbol, Real}} where NF","page":"Function and type index","title":"SpeedyWeather.scale!","text":"scale!(\n progn::PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF},\n var::Symbol,\n scale::Real\n)\n\n\nScale the variable var inside progn with scalar scale.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_divergence!-Tuple{PrognosticVariables, Vararg{Any}}","page":"Function and type index","title":"SpeedyWeather.set_divergence!","text":"set_divergence!(progn::PrognosticVariables, varargs...; kwargs...)\n\nSee set_var!\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_humidity!-Tuple{PrognosticVariables, Vararg{Any}}","page":"Function and type index","title":"SpeedyWeather.set_humidity!","text":"set_humidity!(progn::PrognosticVariables, varargs...; kwargs...)\n\nSee set_var!\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_pressure!-Tuple{PrognosticVariables, AbstractMatrix}","page":"Function and type index","title":"SpeedyWeather.set_pressure!","text":"set_pressure!(progn::PrognosticVariables{NF}, \n pressure::AbstractMatrix, \n Grid::Type{<:AbstractGrid}, \n lf::Integer=1) where NF\n\nSets the prognostic variable with the surface pressure in grid space at leapfrog index lf.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_pressure!-Tuple{PrognosticVariables, LowerTriangularMatrix}","page":"Function and type index","title":"SpeedyWeather.set_pressure!","text":"set_pressure!(progn::PrognosticVariables{NF}, \n pressure::LowerTriangularMatrix;\n lf::Integer=1) where NF\n\nSets the prognostic variable with the surface pressure in spectral space at leapfrog index lf.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_pressure!-Tuple{PrognosticVariables, SpeedyWeather.RingGrids.AbstractGrid, SpeedyWeather.ModelSetup}","page":"Function and type index","title":"SpeedyWeather.set_pressure!","text":"set_pressure!(progn::PrognosticVariables{NF}, \n pressure::AbstractGrid, \n M::ModelSetup;\n lf::Integer=1) where NF\n\nSets the prognostic variable with the surface pressure in grid space at leapfrog index lf.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_pressure!-Tuple{PrognosticVariables, SpeedyWeather.RingGrids.AbstractGrid}","page":"Function and type index","title":"SpeedyWeather.set_pressure!","text":"set_pressure!(progn::PrognosticVariables{NF}, \n pressure::AbstractGrid, \n lf::Integer=1) where NF\n\nSets the prognostic variable with the surface pressure in grid space at leapfrog index lf.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_temperature!-Tuple{PrognosticVariables, Vararg{Any}}","page":"Function and type index","title":"SpeedyWeather.set_temperature!","text":"set_temperature!(progn::PrognosticVariables, varargs...; kwargs...)\n\nSee set_var!\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_var!-Union{Tuple{NF}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Symbol, Number}} where NF","page":"Function and type index","title":"SpeedyWeather.set_var!","text":"function set_var!(progn::PrognosticVariables{NF}, \n varname::Symbol, \n s::Number;\n lf::Integer=1) where NF\n\nSets all values of prognostic variable varname at leapfrog index lf to the scalar s.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_var!-Union{Tuple{NF}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Symbol, Vector{<:AbstractMatrix}}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Symbol, Vector{<:AbstractMatrix}, Type{<:SpeedyWeather.RingGrids.AbstractGrid}}} where NF","page":"Function and type index","title":"SpeedyWeather.set_var!","text":"set_var!(progn::PrognosticVariables{NF}, \n varname::Symbol, \n var::Vector{<:AbstractMatrix}, \n Grid::Type{<:AbstractGrid}=FullGaussianGrid;\n lf::Integer=1) where NF\n\nSets the prognostic variable with the name varname in all layers at leapfrog index lf with values given in var a vector with all information for all layers in grid space.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_var!-Union{Tuple{NF}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Symbol, Vector{<:LowerTriangularMatrix}}} where NF","page":"Function and type index","title":"SpeedyWeather.set_var!","text":"set_var!(progn::PrognosticVariables{NF}, \n varname::Symbol, \n var::Vector{<:LowerTriangularMatrix};\n lf::Integer=1) where NF\n\nSets the prognostic variable with the name varname in all layers at leapfrog index lf with values given in var a vector with all information for all layers in spectral space.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_var!-Union{Tuple{NF}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Symbol, Vector{<:SpeedyWeather.RingGrids.AbstractGrid}, SpeedyWeather.ModelSetup}} where NF","page":"Function and type index","title":"SpeedyWeather.set_var!","text":"set_var!(progn::PrognosticVariables{NF}, \n varname::Symbol, \n var::Vector{<:AbstractGrid}, \n M::ModelSetup;\n lf::Integer=1) where NF\n\nSets the prognostic variable with the name varname in all layers at leapfrog index lf with values given in var a vector with all information for all layers in grid space.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_var!-Union{Tuple{NF}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Symbol, Vector{<:SpeedyWeather.RingGrids.AbstractGrid}}} where NF","page":"Function and type index","title":"SpeedyWeather.set_var!","text":"set_var!(progn::PrognosticVariables{NF}, \n varname::Symbol, \n var::Vector{<:AbstractGrid};\n lf::Integer=1) where NF\n\nSets the prognostic variable with the name varname in all layers at leapfrog index lf with values given in var a vector with all information for all layers in grid space.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_vorticity!-Tuple{PrognosticVariables, Vararg{Any}}","page":"Function and type index","title":"SpeedyWeather.set_vorticity!","text":"set_vorticity!(progn::PrognosticVariables, varargs...; kwargs...)\n\nSee set_var!\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.sigma_okay-Tuple{Integer, AbstractVector}","page":"Function and type index","title":"SpeedyWeather.sigma_okay","text":"sigma_okay(nlev::Integer, σ_half::AbstractVector) -> Bool\n\n\nCheck that nlev and σ_half match.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.speedstring-Tuple{Any, Any}","page":"Function and type index","title":"SpeedyWeather.speedstring","text":"speedstring(sec_per_iter, dt_in_sec) -> String\n\n\ndefine a ProgressMeter.speedstring method that also takes a time step dt_in_sec to translate sec/iteration to days/days-like speeds.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.static_energy_diffusion!-Union{Tuple{NF}, Tuple{ColumnVariables{NF}, SpeedyWeather.StaticEnergyDiffusion}} where NF","page":"Function and type index","title":"SpeedyWeather.static_energy_diffusion!","text":"static_energy_diffusion!(\n column::ColumnVariables{NF},\n scheme::SpeedyWeather.StaticEnergyDiffusion\n)\n\n\nApply dry static energy diffusion.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.surface_pressure_tendency!-Tuple{SpeedyWeather.SurfaceVariables, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.surface_pressure_tendency!","text":"surface_pressure_tendency!( Prog::PrognosticVariables,\n Diag::DiagnosticVariables,\n lf::Int,\n M::PrimitiveEquation)\n\nComputes the tendency of the logarithm of surface pressure as\n\n-(ū*px + v̄*py) - D̄\n\nwith ū,v̄ being the vertically averaged velocities; px, py the gradients of the logarithm of surface pressure ln(p_s) and D̄ the vertically averaged divergence.\n\nCalculate ∇ln(p_s) in spectral space, convert to grid.\nMultiply ū,v̄ with ∇ln(p_s) in grid-point space, convert to spectral.\nD̄ is subtracted in spectral space.\nSet tendency of the l=m=0 mode to 0 for better mass conservation.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.temperature_anomaly!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, ImplicitPrimitiveEq}","page":"Function and type index","title":"SpeedyWeather.temperature_anomaly!","text":"Convert absolute and virtual temperature to anomalies wrt to the reference profile\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.temperature_average!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, LowerTriangularMatrix, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.temperature_average!","text":"temperature_average!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n temp::LowerTriangularMatrix,\n S::SpectralTransform\n) -> Any\n\n\nCalculates the average temperature of a layer from the l=m=0 harmonic and stores the result in diagn.temp_average\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.temperature_relaxation!-Tuple{ColumnVariables, JablonowskiRelaxation}","page":"Function and type index","title":"SpeedyWeather.temperature_relaxation!","text":"temperature_relaxation!(\n column::ColumnVariables,\n scheme::JablonowskiRelaxation\n)\n\n\nApply HeldSuarez-like temperature relaxation to the Jablonowski and Williamson vertical profile.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.temperature_relaxation!-Tuple{ColumnVariables, NoTemperatureRelaxation}","page":"Function and type index","title":"SpeedyWeather.temperature_relaxation!","text":"temperature_relaxation!(\n column::ColumnVariables,\n scheme::NoTemperatureRelaxation\n)\n\n\njust passes.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.temperature_relaxation!-Union{Tuple{NF}, Tuple{ColumnVariables{NF}, HeldSuarez}} where NF","page":"Function and type index","title":"SpeedyWeather.temperature_relaxation!","text":"temperature_relaxation!(\n column::ColumnVariables{NF},\n scheme::HeldSuarez\n)\n\n\nApply temperature relaxation following Held and Suarez 1996, BAMS.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.temperature_tendency!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, DynamicsConstants, Geometry, SpectralTransform, ImplicitPrimitiveEq}","page":"Function and type index","title":"SpeedyWeather.temperature_tendency!","text":"temperature_tendency!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n C::DynamicsConstants,\n G::Geometry,\n S::SpectralTransform,\n I::ImplicitPrimitiveEq\n)\n\n\nCompute the temperature tendency\n\n∂T/∂t += -∇⋅((u,v)*T') + T'D + κTᵥ*Dlnp/Dt\n\n+= because the tendencies already contain parameterizations and vertical advection. T' is the anomaly with respect to the reference/average temperature. Tᵥ is the virtual temperature used in the adiabatic term κTᵥ*Dlnp/Dt.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.temperature_tendency!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.temperature_tendency!","text":"temperature_tendency!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n model::PrimitiveEquation\n)\n\n\nFunction barrier to unpack model.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.time_stepping!-Tuple{PrognosticVariables, DiagnosticVariables, SpeedyWeather.ModelSetup}","page":"Function and type index","title":"SpeedyWeather.time_stepping!","text":"time_stepping!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::SpeedyWeather.ModelSetup\n) -> PrognosticVariables\n\n\nMain time loop that that initializes output and feedback, loops over all time steps and calls the output and feedback functions.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.timestep!","page":"Function and type index","title":"SpeedyWeather.timestep!","text":"timestep!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n dt::Real,\n model::Barotropic\n)\ntimestep!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n dt::Real,\n model::Barotropic,\n lf1::Int64\n)\ntimestep!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n dt::Real,\n model::Barotropic,\n lf1::Int64,\n lf2::Int64\n)\n\n\nCalculate a single time step for the model <: Barotropic.\n\n\n\n\n\n","category":"function"},{"location":"functions/#SpeedyWeather.timestep!-Union{Tuple{NF}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, DiagnosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Real, PrimitiveEquation}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, DiagnosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Real, PrimitiveEquation, Int64}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, DiagnosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Real, PrimitiveEquation, Int64, Int64}} where NF<:AbstractFloat","page":"Function and type index","title":"SpeedyWeather.timestep!","text":"timestep!(\n progn::PrognosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n diagn::DiagnosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n dt::Real,\n model::PrimitiveEquation\n) -> Any\ntimestep!(\n progn::PrognosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n diagn::DiagnosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n dt::Real,\n model::PrimitiveEquation,\n lf1::Int64\n) -> Any\ntimestep!(\n progn::PrognosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n diagn::DiagnosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n dt::Real,\n model::PrimitiveEquation,\n lf1::Int64,\n lf2::Int64\n) -> Any\n\n\nCalculate a single time step for the model<:PrimitiveEquation\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.timestep!-Union{Tuple{NF}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, DiagnosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Real, ShallowWater}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, DiagnosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Real, ShallowWater, Int64}, Tuple{PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, DiagnosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Real, ShallowWater, Int64, Int64}} where NF<:AbstractFloat","page":"Function and type index","title":"SpeedyWeather.timestep!","text":"timestep!(\n progn::PrognosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n diagn::DiagnosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n dt::Real,\n model::ShallowWater\n) -> Union{Nothing, SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat}\ntimestep!(\n progn::PrognosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n diagn::DiagnosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n dt::Real,\n model::ShallowWater,\n lf1::Int64\n) -> Union{Nothing, SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat}\ntimestep!(\n progn::PrognosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n diagn::DiagnosticVariables{NF<:AbstractFloat, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n dt::Real,\n model::ShallowWater,\n lf1::Int64,\n lf2::Int64\n) -> Union{Nothing, SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat}\n\n\nCalculate a single time step for the model <: ShallowWater.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.underflow!-Union{Tuple{T}, Tuple{AbstractArray{T}, Real}} where T","page":"Function and type index","title":"SpeedyWeather.underflow!","text":"underflow!(A::AbstractArray,ϵ::Real)\n\nUnderflows element a in A to zero if abs(a) < ϵ.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.unscale!-Tuple{AbstractArray, Real}","page":"Function and type index","title":"SpeedyWeather.unscale!","text":"unscale!(variable::AbstractArray, scale::Real) -> Any\n\n\nUndo the radius-scaling for any variable. Method used for netcdf output.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.unscale!-Tuple{PrognosticVariables}","page":"Function and type index","title":"SpeedyWeather.unscale!","text":"unscale!(progn::PrognosticVariables) -> Int64\n\n\nUndo the radius-scaling of vorticity and divergence from scale!(progn,scale::Real).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.vertical_integration!-Union{Tuple{NF}, Tuple{DiagnosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, PrognosticVariables{NF, Grid} where Grid<:SpeedyWeather.RingGrids.AbstractGrid{NF}, Int64, Geometry{NF}}} where NF","page":"Function and type index","title":"SpeedyWeather.vertical_integration!","text":"vertical_integration!(Diag::DiagnosticVariables,G::Geometry)\n\nCalculates the vertically averaged (weighted by the thickness of the σ level) velocities (*coslat) and divergence. E.g.\n\nu_mean = ∑_k=1^nlev Δσ_k * u_k\n\nu,v are averaged in grid-point space, divergence in spectral space.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.virtual_temperature!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, LowerTriangularMatrix, DynamicsConstants}","page":"Function and type index","title":"SpeedyWeather.virtual_temperature!","text":"virtual_temperature!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n temp::LowerTriangularMatrix,\n constants::DynamicsConstants\n)\n\n\nCalculates the virtual temperature Tᵥ as\n\nTᵥ = T(1+μq)\n\nWith absolute temperature T, specific humidity q and\n\nμ = (1-ξ)/ξ, ξ = R_dry/R_vapour.\n\nin grid-point space.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.virtual_temperature!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, LowerTriangularMatrix, PrimitiveDry}","page":"Function and type index","title":"SpeedyWeather.virtual_temperature!","text":"virtual_temperature!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n temp::LowerTriangularMatrix,\n model::PrimitiveDry\n) -> SpeedyWeather.RingGrids.AbstractGrid{NF} where NF<:AbstractFloat\n\n\nVirtual temperature in grid-point space: For the PrimitiveDry temperature and virtual temperature are the same (humidity=0). Just copy over the arrays.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.volume_flux_divergence!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, SpeedyWeather.SurfaceVariables, SpeedyWeather.AbstractOrography, DynamicsConstants, Geometry, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.volume_flux_divergence!","text":"volume_flux_divergence!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n surface::SpeedyWeather.SurfaceVariables,\n orog::SpeedyWeather.AbstractOrography,\n constants::DynamicsConstants,\n G::Geometry,\n S::SpectralTransform\n)\n\n\nComputes the (negative) divergence of the volume fluxes uh,vh for the continuity equation, -∇⋅(uh,vh).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.vordiv_tendencies!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, SpeedyWeather.SurfaceVariables, DynamicsConstants, Geometry, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.vordiv_tendencies!","text":"vordiv_tendencies!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n surf::SpeedyWeather.SurfaceVariables,\n C::DynamicsConstants,\n G::Geometry,\n S::SpectralTransform\n)\n\n\nTendencies for vorticity and divergence. Excluding Bernoulli potential with geopotential and linear pressure gradient inside the Laplace operator, which are added later in spectral space.\n\nu_tend += v*(f+ζ) - RTᵥ'*∇lnp_x\nv_tend += -u*(f+ζ) - RTᵥ'*∇lnp_y\n\n+= because the tendencies already contain the parameterizations and vertical advection. f is coriolis, ζ relative vorticity, R the gas constant Tᵥ' the virtual temperature anomaly, ∇lnp the gradient of surface pressure and _x and _y its zonal/meridional components. The tendencies are then curled/dived to get the tendencies for vorticity/divergence in spectral space\n\n∂ζ/∂t = ∇×(u_tend,v_tend)\n∂D/∂t = ∇⋅(u_tend,v_tend) + ...\n\n+ ... because there's more terms added later for divergence.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.vordiv_tendencies!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, SpeedyWeather.SurfaceVariables, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.vordiv_tendencies!","text":"vordiv_tendencies!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n surf::SpeedyWeather.SurfaceVariables,\n model::PrimitiveEquation\n)\n\n\nFunction barrier to unpack model.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.vorticity_flux!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, Barotropic}","page":"Function and type index","title":"SpeedyWeather.vorticity_flux!","text":"vorticity_flux!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n model::Barotropic\n)\n\n\nVorticity flux tendency in the barotropic vorticity equation\n\n∂ζ/∂t = ∇×(u_tend,v_tend)\n\nwith\n\nu_tend = Fᵤ + v*(ζ+f) v_tend = Fᵥ - u*(ζ+f)\n\nwith Fᵤ,Fᵥ the forcing from forcing! already in u_tend_grid/v_tend_grid and vorticity ζ, coriolis f.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.vorticity_flux!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, ShallowWater}","page":"Function and type index","title":"SpeedyWeather.vorticity_flux!","text":"vorticity_flux!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n model::ShallowWater\n)\n\n\nVorticity flux tendency in the shallow water equations\n\n∂ζ/∂t = ∇×(u_tend,v_tend) ∂D/∂t = ∇⋅(u_tend,v_tend)\n\nwith\n\nu_tend = Fᵤ + v*(ζ+f) v_tend = Fᵥ - u*(ζ+f)\n\nwith Fᵤ,Fᵥ the forcing from forcing! already in u_tend_grid/v_tend_grid and vorticity ζ, coriolis f.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.vorticity_flux_curldiv!-Tuple{SpeedyWeather.DiagnosticVariablesLayer, DynamicsConstants, Geometry, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.vorticity_flux_curldiv!","text":"vorticity_flux_curldiv!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n C::DynamicsConstants,\n G::Geometry,\n S::SpectralTransform;\n div,\n add\n)\n\n\nCompute the vorticity advection as the curl/div of the vorticity fluxes\n\n∂ζ/∂t = ∇×(u_tend,v_tend) ∂D/∂t = ∇⋅(u_tend,v_tend)\n\nwith\n\nu_tend = Fᵤ + v*(ζ+f) v_tend = Fᵥ - u*(ζ+f)\n\nwith Fᵤ,Fᵥ from u_tend_grid/v_tend_grid that are assumed to be alread set in forcing!. Set div=false for the BarotropicModel which doesn't require the divergence tendency.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.workgroup_size-Tuple{SpeedyWeather.AbstractDevice}","page":"Function and type index","title":"SpeedyWeather.workgroup_size","text":"workgroup_size(dev::AbstractDevice)\n\nReturns a workgroup size depending on dev. WIP: Will be expanded in the future to also include grid information. \n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.write_column_tendencies!-Tuple{DiagnosticVariables, ColumnVariables, Int64}","page":"Function and type index","title":"SpeedyWeather.write_column_tendencies!","text":"write_column_tendencies!(\n D::DiagnosticVariables,\n C::ColumnVariables,\n ij::Int64\n)\n\n\nWrite the parametrization tendencies from C::ColumnVariables into the horizontal fields of tendencies stored in D::DiagnosticVariables at gridpoint index ij.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.write_netcdf_time!-Tuple{OutputWriter, Dates.DateTime}","page":"Function and type index","title":"SpeedyWeather.write_netcdf_time!","text":"write_netcdf_time!(\n output::OutputWriter,\n time::Dates.DateTime\n)\n\n\nWrite the current time time::DateTime to the netCDF file in output::OutputWriter.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.write_netcdf_variables!-Union{Tuple{Model}, Tuple{Grid}, Tuple{NF}, Tuple{OutputWriter, DiagnosticVariables{NF, Grid, Model}}} where {NF, Grid, Model}","page":"Function and type index","title":"SpeedyWeather.write_netcdf_variables!","text":"write_netcdf_variables!(\n output::OutputWriter,\n diagn::DiagnosticVariables{NF, Grid, Model}\n)\n\n\nWrite diagnostic variables from diagn to the netCDF file in output::OutputWriter.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.write_output!-Tuple{OutputWriter, Dates.DateTime, DiagnosticVariables}","page":"Function and type index","title":"SpeedyWeather.write_output!","text":"write_output!(\n outputter::OutputWriter,\n time::Dates.DateTime,\n diagn::DiagnosticVariables\n)\n\n\nWrites the variables from diagn of time step i at time time into outputter.netcdf_file. Simply escapes for no netcdf output of if output shouldn't be written on this time step. Interpolates onto output grid and resolution as specified in outputter, converts to output number format, truncates the mantissa for higher compression and applies lossless compression.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.write_restart_file-Tuple{PrognosticVariables, OutputWriter}","page":"Function and type index","title":"SpeedyWeather.write_restart_file","text":"write_restart_file(\n progn::PrognosticVariables,\n output::OutputWriter\n) -> Union{Nothing, String}\n\n\nA restart file restart.jld2 with the prognostic variables is written to the output folder (or current path) that can be used to restart the model. restart.jld2 will then be used as initial conditions. The prognostic variables are bitrounded for compression and the 2nd leapfrog time step is discarded. Variables in restart file are unscaled.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.zero_tendencies!-Union{Tuple{DiagnosticVariables{NF, Grid, Model}}, Tuple{Model}, Tuple{Grid}, Tuple{NF}} where {NF, Grid, Model<:Barotropic}","page":"Function and type index","title":"SpeedyWeather.zero_tendencies!","text":"zero_tendencies!(\n diagn::DiagnosticVariables{NF, Grid, Model<:Barotropic}\n)\n\n\nSet the tendencies in diagn to zero.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.zero_tendencies!-Union{Tuple{DiagnosticVariables{NF, Grid, Model}}, Tuple{Model}, Tuple{Grid}, Tuple{NF}} where {NF, Grid, Model<:PrimitiveDry}","page":"Function and type index","title":"SpeedyWeather.zero_tendencies!","text":"zero_tendencies!(\n diagn::DiagnosticVariables{NF, Grid, Model<:PrimitiveDry}\n)\n\n\nSet the tendencies in diagn to zero.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.zero_tendencies!-Union{Tuple{DiagnosticVariables{NF, Grid, Model}}, Tuple{Model}, Tuple{Grid}, Tuple{NF}} where {NF, Grid, Model<:PrimitiveWet}","page":"Function and type index","title":"SpeedyWeather.zero_tendencies!","text":"zero_tendencies!(\n diagn::DiagnosticVariables{NF, Grid, Model<:PrimitiveWet}\n)\n\n\nSet the tendencies in diagn to zero.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.zero_tendencies!-Union{Tuple{DiagnosticVariables{NF, Grid, Model}}, Tuple{Model}, Tuple{Grid}, Tuple{NF}} where {NF, Grid, Model<:ShallowWater}","page":"Function and type index","title":"SpeedyWeather.zero_tendencies!","text":"zero_tendencies!(\n diagn::DiagnosticVariables{NF, Grid, Model<:ShallowWater}\n)\n\n\nSet the tendencies in diagn to zero.\n\n\n\n\n\n","category":"method"},{"location":"how_to_run_speedy/#How-to-run-SpeedyWeather.jl","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"Creating a SpeedyWeather.jl simulation and running it consists conceptually of 4 steps. In contrast to many other models, these steps are bottom-up rather then top-down. There is no monolithic interface to SpeedyWeather.jl, instead all options that a user may want to adjust are chosen and live in their respective model components.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"Create a SpectralGrid which defines the grid and spectral resolution.\nCreate model components and combine to a model.\nInitialize the model to create a simulation.\nRun that simulation.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"In the following we will describe these steps in more detail. If you want to start with some examples first, see Model setups which has easy and more advanced examples of how to set up SpeedyWeather.jl to run the simulation you want.","category":"page"},{"location":"how_to_run_speedy/#SpectralGrid","page":"How to run SpeedyWeather.jl","title":"SpectralGrid","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"The life of every SpeedyWeather.jl simulation starts with a SpectralGrid object. A SpectralGrid defines the physical domain of the simulation and its discretization. This domain has to be a sphere because of the spherical harmonics, but it can have a different radius. The discretization is for spectral, grid-point space and the vertical as this determines the size of many arrays for preallocation, for which als the number format is essential to know. That's why SpectralGrid is the beginning of every SpeedyWeather.jl simulation and that is why it has to be passed on to (most) model components.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"The default SpectralGrid is","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"using SpeedyWeather\nspectral_grid = SpectralGrid()","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"You can also get the help prompt by typing ?SpectralGrid. Let's explain the details: The spectral resolution is T31, so the largest wavenumber in spectral space is 31, and all the complex spherical harmonic coefficients of a given 2D field (see Spherical Harmonic Transform) are stored in a LowerTriangularMatrix in the number format Float32. The radius of the sphere is 6371km by default, which is the radius of the Earth.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"This spectral resolution is combined with an octahedral Gaussian grid of 3168 grid points. This grid has 48 latitude rings, 20 longitude points around the poles and up to 96 longitude points around the Equator. Data on that grid is also stored in Float32. The resolution is therefore on average about 400km. In the vertical 8 levels are used, using Sigma coordinates.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"The resolution of a SpeedyWeather.jl simulation is adjusted using the trunc argument, this defines the spectral resolution and the grid resolution is automatically adjusted to keep the aliasing between spectral and grid-point space constant (see Matching spectral and grid resolution).","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"spectral_grid = SpectralGrid(trunc=85)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"Typical values are 31,42,63,85,127,170,... although you can technically use any integer, see Available horizontal resolutions for details. Now with T85 (which is a common notation for trunc=85) the grid is of higher resolution too. You may play with the dealiasing factor, a larger factor increases the grid resolution that is matched with a given spectral resolution. You don't choose the resolution of the grid directly, but using the Grid argument you can change its type (see Grids)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"spectral_grid = SpectralGrid(trunc=85,dealiasing=3,Grid=HEALPixGrid)","category":"page"},{"location":"how_to_run_speedy/#Vertical-coordinates-and-resolution","page":"How to run SpeedyWeather.jl","title":"Vertical coordinates and resolution","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"The number of vertical layers or levels (we use both terms often interchangeably) is determined through the nlev argument. Especially for the BarotropicModel and the ShallowWaterModel you want to set this to","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"spectral_grid = SpectralGrid(nlev=1)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"For a single vertical level the type of the vertical coordinates does not matter, but in general you can change the spacing of the sigma coordinates which have to be discretized in 01","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"vertical_coordinates = SigmaCoordinates(0:0.2:1)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"These are regularly spaced Sigma coordinates, defined through their half levels. The cell centers or called full levels are marked with an ×.","category":"page"},{"location":"how_to_run_speedy/#create_model_components","page":"How to run SpeedyWeather.jl","title":"Creating model components","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"SpeedyWeather.jl deliberately avoids the namelists that are the main user interface to many old school models. Instead, we employ a modular approach whereby every non-default model component is created with its respective parameters to finally build the whole model from these components.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"If you know which components you want to adjust you would create them right away, however, a new user might first want to know which components there are, so let's flip the logic around and assume you know you want to run a ShallowWaterModel. You can create a default ShallowWaterModel like so and inspect its components","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"model = ShallowWaterModel()","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"So by default the ShallowWaterModel uses a Leapfrog time_stepping, which you can inspect like so","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"model.time_stepping","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"Model components often contain parameters from the SpectralGrid as they are needed to determine the size of arrays and other internal reasons. You should, in most cases, just ignore those. But the Leapfrog time stepper comes with Δt_at_T31 which is the parameter used to scale the time step automatically. This means at a spectral resolution of T31 it would use 30min steps, at T63 it would be ~half that, 15min, etc. Meaning that if you want to have a shorter or longer time step you can create a new Leapfrog time stepper. But remember that every model component depends on a SpectralGrid as first argument.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"spectral_grid = SpectralGrid(trunc=63,nlev=1)\ntime_stepping = Leapfrog(spectral_grid,Δt_at_T31=15)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"The actual time step at the given resolution (here T63) is then Δt_sec, there's also Δt which is a scaled time step used internally, because SpeedyWeather.jl scales the equations with the radius of the Earth, but this is largely hidden (except here) from the user. With this new Leapfrog time stepper constructed we can create a model by passing on the components (they are keyword arguments so either use ;time_stepping for which the naming must match, or time_stepping=my_time_stepping with any name)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"model = ShallowWaterModel(;spectral_grid,time_stepping)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"This logic continues for all model components. See the Model setups for examples. All model components are also subtype (i.e. <:) of some abstract supertype, this feature can be made use of to redefine not just constant parameters of existing model components but also to define new ones. This is more elaborated in Extending SpeedyWeather.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"A model in SpeedyWeather.jl is understood as a collection of model components that roughly belong into one of three groups. ","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"Components (numerics, dynamics, or physics) that have parameters, possibly contain some data (immutable, precomputed) and some functions associated with them. For example, a forcing term may be defined through some parameters, but also require precomputed arrays, or data to be loaded from file and a function that instructs how to calculate this forcing on every time step.\nComponents that are merely a collection of parameters that conceptually belong together. For example, Earth is an AbstractPlanet that contains all the orbital parameters important for weather and climate (rotation, gravity, etc).\nComponents for output purposes. For example, OutputWriter controls the NetCDF output, and Feedback controls the information printed to the REPL and to file.","category":"page"},{"location":"how_to_run_speedy/#create_model","page":"How to run SpeedyWeather.jl","title":"Creating a model","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"SpeedyWeather.jl implements (currently) 4 different models","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"BarotropicModel, see Barotropic vorticity model\nShallowWaterModel, see Shallow water model\nPrimitiveDryModel, see Primitive equation model but with zero humidity.\nPrimitiveWetModel, see Primitive equation model.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"Overall they are kept quite similar, but there are still fundamental differences arising from the different equations. For example, the barotropic and shallow water models do not have any physical parameterizations. Conceptually you construct these different models with","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"spectral_grid = SpectralGrid(trunc=...,...)\ncomponent1 = SomeComponent(spectral_grid,parameter1=...,...)\ncomponent2 = SomeOtherComponent(spectral_grid,parameter2=...,...)\nmodel = BarotropicModel(;spectral_grid,all_other_components...,...)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"or model = ShallowWaterModel(;spectral_grid,...), etc.","category":"page"},{"location":"how_to_run_speedy/#initialize","page":"How to run SpeedyWeather.jl","title":"Model initialization","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"In the previous section the model was created, but this is conceptually just gathering all its components together. However, many components need to be initialized. This step is used to precompute arrays, load necessary data from file or to communicate those between components. Furthermore, prognostic and diagnostic variables are allocated. It is (almost) all that needs to be done before the model can be run (exception being the output initialization). Many model components have a initialize! function associated with them that it executed here. However, from a user perspective all that needs to be done here is","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"simulation = initialize!(model)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"and we have initialized the ShallowWaterModel we have defined earlier. After this step you can continue to tweak your model setup but note that some model components are immutable, or that your changes may not be propagated to other model components that rely on it. But you can, for example, change the output time step (in hours) like so","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"simulation.model.output.output_dt = 1","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"Now, if there's output, it will be every hour. Furthermore the initial conditions can be set with the initial_conditions model component which are then set during initialize!(::ModelSetup), but you can also change them now, before the model runs","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"simulation.prognostic_variables.layers[1].timesteps[1].vor[1] = 0","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"So with this we have set the zero mode of vorticity of the first (and only) layer in the shallow water to zero. Because the leapfrogging is a 2-step time stepping scheme we set here the first. As it is often tricky to set the initial conditions in spectral space, it is generally advised to do so through the initial_conditions model component.","category":"page"},{"location":"how_to_run_speedy/#run","page":"How to run SpeedyWeather.jl","title":"Run a simulation","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"After creating a model, initializing it to a simulation, all that is left is to run the simulation.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"run!(simulation)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"By default this runs for 10 days without output. These are the options left to change, so with","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"model.output.id = \"test\" # hide\nrun!(simulation,n_days=5,output=true)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather.jl","title":"How to run SpeedyWeather.jl","text":"You would continue this simulation (the previous run! call already integrated 10 days!) for another 5 days and storing default NetCDF output.","category":"page"},{"location":"speedytransforms/#SpeedyTransforms","page":"Submodule: SpeedyTransforms","title":"SpeedyTransforms","text":"","category":"section"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"SpeedyTransforms is a submodule that has been developed for SpeedyWeather.jl which is technically independent (SpeedyWeather.jl however imports it) and can also be used without running simulations. It is just not put into its own respective repository for now.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"The SpeedyTransforms are based on RingGrids and LowerTriangularMatrices to hold data in either grid-point space or in spectral space. So you want to read these sections first for clarifications how to work with these. We will also not discuss mathematical details of the Spherical Harmonic Transform here, but will focus on the usage of the SpeedyTransforms module.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"The SpeedyTransforms module also implements the gradient operators nabla nabla cdot nabla times nabla^2 nabla^-2 in spectral space. Combined with the spectral transform, you could for example start with a velocity field in grid-point space, transform to spectral, compute its divergence and transform back to obtain the divergence in grid-point space. Examples are outlined in Gradient operators.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"info: SpeedyTransforms assumes a unit sphere\nThe operators in SpeedyTransforms generally assume a sphere of radius R=1. For the transforms themselves that does not make a difference, but the gradient operators div,curl,∇,∇²,∇⁻² omit the radius scaling. You will have to do this manually. Also note that the meridional derivate expects a cos^-1(theta) scaling. More in Gradient operators.","category":"page"},{"location":"speedytransforms/#Example-transform","page":"Submodule: SpeedyTransforms","title":"Example transform","text":"","category":"section"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Lets start with a simple transform. We could be using SpeedyWeather but to be more verbose these are the modules required to load","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"using SpeedyWeather.RingGrids\nusing SpeedyWeather.LowerTriangularMatrices\nusing SpeedyWeather.SpeedyTransforms","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"As an example, we want to transform the l=m=1 spherical harmonic from spectral space in alms to grid-point space.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"alms = zeros(LowerTriangularMatrix{ComplexF64},6,6) # spectral coefficients\nalms[2,2] = 1 # only l=1,m=1 harmonic\nalms","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Now gridded is the function that takes spectral coefficients alms and converts them a grid-point space map","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"map = gridded(alms)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"By default, the gridded transforms onto a FullGaussianGrid unravelled here into a vector west to east, starting at the prime meridian, then north to south, see RingGrids. We can visualize map quickly with a UnicodePlot via plot (see Visualising RingGrid data)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"plot(map)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Yay! This is the what the l=m=1 spherical harmonic is supposed to look like! Now let's go back to spectral space with spectral","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"alms2 = spectral(map)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Comparing with alms from above you can see that the transform is exact up to a typical rounding error from Float64. ","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"alms ≈ alms2","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"YAY! The transform is typically idempotent, meaning that either space may hold information that is not exactly representable in the other but the first two-way transform will remove that so that subsequent transforms do not change this any further. However, also note here that the default FullGaussianGrid is an exact grid, inexact grids usually have a transform error that is larger than the rounding error from floating-point arithmetic.","category":"page"},{"location":"speedytransforms/#Transform-onto-another-grid","page":"Submodule: SpeedyTransforms","title":"Transform onto another grid","text":"","category":"section"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"While the default grid for SpeedyTransforms is the FullGaussianGrid we can transform onto other grids by specifying Grid too","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"map = gridded(alms,Grid=HEALPixGrid)\nplot(map)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"which, if transformed back, however, can yield a larger transform error as discussed above","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"spectral(map)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"On such a coarse grid the transform error (absolute and relative) is about 10^-2, this decreases for higher resolution. The gridded and spectral functions will choose a corresponding grid-spectral resolution (see Matching spectral and grid resolution) following quadratic truncation, but you can always truncate/interpolate in spectral space with spectral_truncation, spectral_interpolation which takes trunc = l_max = m_max as second argument","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"spectral_truncation(alms,2)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Yay, we just chopped off l 2 from alms which contained the harmonics up to degree and order 5 before. If the second argument in spectral_truncation is larger than alms then it will automatically call spectral_interpolation and vice versa. Also see Interpolation on RingGrids to interpolate directly between grids. If you want to control directly the resolution of the grid gridded is supposed to transform onto you have to provide a SpectralTransform instance. More on that now.","category":"page"},{"location":"speedytransforms/#The-SpectralTransform-struct","page":"Submodule: SpeedyTransforms","title":"The SpectralTransform struct","text":"","category":"section"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Both spectral and gridded create an instance of SpectralTransform under the hood. This object contains all precomputed information that is required for the transform, either way: The Legendre polynomials, pre-planned Fourier transforms, precomputed gradient, divergence and curl operators, the spherical harmonic eigenvalues among others. Maybe the most intuitive way to create a SpectralTransform is to start with a SpectralGrid, which already defines which spectral resolution is supposed to be combined with a given grid.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(Float32,trunc=5,Grid=OctahedralGaussianGrid,dealiasing=3)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"(We using SpeedyWeather here as SpectralGrid is exported therein). We also specify the number format Float32 here to be used for the transform although this is the default anyway. From spectral_grid we now construct a SpectralTransform as follows","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"S = SpectralTransform(spectral_grid)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Note that because we chose dealiasing=3 (cubic truncation) we now match a T5 spectral field with a 12-ring octahedral Gaussian grid, instead of the 8 rings as above. So going from dealiasing=2 (default) to dealiasing=3 increased our resolution on the grid while the spectral resolution remains the same. The SpectralTransform also has options for the recomputation or pre-computation of the Legendre polynomials, fore more information see (P)recompute Legendre polynomials.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Passing on S the SpectralTransform now allows us to transform directly on the grid defined therein.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"map = gridded(alms,S)\nplot(map)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Yay, this is again the l=m=1 harmonic, but this time on a slightly higher resolution OctahedralGaussianGrid as specified in the SpectralTransform S. Note that also the number format was converted on the fly to Float32 because that is the number format we specified in S! And from grid to spectral","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"alms2 = spectral(map,S)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"As you can see the rounding error is now more like 10^-8 as we are using Float32 (the OctahedralGaussianGrid is another exact grid). Note, however, that the returned LowerTriangularMatrix is of size 7x6, not 6x6 what we started from. The underlying reason is that internally SpeedyWeather uses LowerTriangularMatrixs of size l_max + 2 times m_max + 1. One +1 on both degree and order for 0-based harmonics versus 1-based matrix sizes, but an additional +1 for the degrees which is required by the meridional derivative. For consistency, all LowerTriangularMatrixs in SpeedyWeather.jl carry this additional degree but only the vector quantities explicitly make use of it. See Meridional derivative for details.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"For this interface to SpeedyTransforms this means that on a grid-to-spectral transform you will get one more degree than orders of the spherical harmonics by default. You can, however, always truncate this additional degree, say to T5 (hence matrix size is 6x6)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"spectral_truncation(alms2,5,5)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"spectral_truncation(alms2,5) would have returned the same, a single argument is then assumed equal for both degrees and orders. Alternatively, you can also pass on the one_more_degree=false argument to the SpectralTransform constructor","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"S = SpectralTransform(spectral_grid,one_more_degree=false)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"As you can see the 7x6 LowerTriangularMatrix in the description above dropped down to 6x6 LowerTriangularMatrix, this is the size of the input that is expected (otherwise you will get a BoundsError).","category":"page"},{"location":"speedytransforms/#Power-spectrum","page":"Submodule: SpeedyTransforms","title":"Power spectrum","text":"","category":"section"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"How to take some data and compute a power spectrum with SpeedyTransforms you may ask. Say you have some global data in a matrix m that looks, for example, like","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"alms = randn(LowerTriangularMatrix{Complex{Float32}},32,32) # hide\nspectral_truncation!(alms,10) # hide\nmap = gridded(alms,Grid=FullClenshawGrid) # hide\nm = Matrix(map) # hide\nm","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"You hopefully know which grid this data comes on, let us assume it is a regular latitude-longitude grid, which we call the FullClenshawGrid. Note that for the spectral transform this should not include the poles, so the 96x47 matrix size here corresponds to ","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"We now wrap this matrix therefore to associate it with the necessary grid information","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"map = FullClenshawGrid(m)\nplot(map)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Now we transform into spectral space and call power_spectrum(::LowerTriangularMatrix)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"alms = spectral(map)\npower = SpeedyTransforms.power_spectrum(alms)\nnothing # hide","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Which returns a vector of power at every wavenumber. By default this is normalized as average power per degree, you can change that with the keyword argument normalize=false. Plotting this yields","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"using UnicodePlots\nk = 0:length(power)-1\nlineplot(k,power,yscale=:log10,ylim=(1e-15,10),xlim=extrema(k),\n ylabel=\"power\",xlabel=\"wavenumber\",height=10,width=60)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"The power spectrum of our data is about 1 up to wavenumber 10 and then close to zero for higher wavenumbers (which is in fact how we constructed this fake data). Let us turn this around and use SpeedyTransforms to create random noise in spectral space to be used in grid-point space!","category":"page"},{"location":"speedytransforms/#Example:-Creating-kn-distributed-noise","page":"Submodule: SpeedyTransforms","title":"Example: Creating k^n-distributed noise","text":"","category":"section"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"How would we construct random noise in spectral space that follows a certain power law and transform it back into grid-point space? Define the wavenumber k for T31, the spectral resolution we are interested in. (We start from 1 instead of 0 to avoid zero to the power of something negative). Now create some normally distributed spectral coefficients but scale them down for higher wavenumbers with k^-2","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"k = 1:32\nalms = randn(LowerTriangularMatrix{Complex{Float32}},32,32)\nalms .*= k.^-2","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Awesome. For higher degrees and orders the amplitude clearly decreases! Now to grid-point space and let us visualize the result","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"map = gridded(alms)\nplot(map)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"You can always access the underlying data in map via map.data in case you need to get rid of the wrapping into a grid again!","category":"page"},{"location":"speedytransforms/#(P)recompute-Legendre-polynomials","page":"Submodule: SpeedyTransforms","title":"(P)recompute Legendre polynomials","text":"","category":"section"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"The spectral transform uses a Legendre transform in meridional direction. For this the Legendre polynomials are required, at each latitude ring this is a l_max times m_max lower triangular matrix. Storing precomputed Legendre polynomials therefore quickly increase in size with resolution. One can recompute them to save memory, but that uses more arithmetic operations. There is therefore a memory-compute tradeoff.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"For a single transform, there is no need to precompute the polynomials as the SpectralTransform object will be garbage collected again anyway. For low resolution simulations with many repeated small transforms it makes sense to precompute the polynomials and SpeedyWeather.jl does that automatically anyway. At very high resolution the polynomials may, however, become prohibitively large. An example at T127, about 100km resolution","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"spectral_grid = SpectralGrid(trunc=127)\nSpectralTransform(spectral_grid,recompute_legendre=false)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"the polynomials are about 3MB in size. Easy that is not much. But at T1023 on the O1536 octahedral Gaussian grid, this is already 1.5GB, cubically increasing with the spectral truncation T. recompute_legendre=true (default false when constructing a SpectralTransform object which may be reused) would lower this to kilobytes","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"SpectralTransform(spectral_grid,recompute_legendre=true)","category":"page"},{"location":"speedytransforms/#Gradient-operators","page":"Submodule: SpeedyTransforms","title":"Gradient operators","text":"","category":"section"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"SpeedyTransforms also includes many gradient operators to take derivatives in spherical harmonics. These are in particular nabla nabla cdot nabla times nabla^2 nabla^-2. However, the actually implemented operators are, in contrast to the mathematical Derivatives in spherical coordinates due to reasons of scaling as follows. Let the implemented operators be hatnabla etc.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"hatnabla A = left(fracpartial Apartial lambda cos(theta)fracpartial Apartial theta right) =\nRcos(theta)nabla A","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"So the zonal derivative omits the radius and the cos^-1(theta) scaling. The meridional derivative adds a cos(theta) due to a recursion relation being defined that way, which, however, is actually convenient because the whole operator is therefore scaled by Rcos(theta). The curl and divergence operators expect the input velocity fields to be scaled by cos^-1(theta), i.e.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"beginaligned\nhatnabla cdot (cos^-1(theta)mathbfu) = fracpartial upartial lambda +\ncosthetafracpartial vpartial theta = Rnabla cdot mathbfu \nhatnabla times (cos^-1(theta)mathbfu) = fracpartial vpartial lambda -\ncosthetafracpartial upartial theta = Rnabla times mathbfu\nendaligned","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"And the Laplace operators omit a R^2 (radius R) scaling, i.e.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"hatnabla^-2A = frac1R^2nabla^-2A quad hatnabla^2A = R^2nabla^2A","category":"page"},{"location":"speedytransforms/#Example:-Geostrophy","page":"Submodule: SpeedyTransforms","title":"Example: Geostrophy","text":"","category":"section"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Now, we want to use the following example to illustrate their use: We have uv and want to calculate eta in the shallow water system from it following geostrophy. Analytically we have","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"-fv = -gpartial_lambda eta quad fu = -gpartial_theta eta","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"which becomes, if you take the divergence of these two equations","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"zeta = fracgfnabla^2 eta","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Meaning that if we start with uv we can obtain the relative vorticity zeta and, using Coriolis parameter f and gravity g, invert the Laplace operator to obtain displacement eta. How to do this with SpeedyTransforms? ","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Let us start by generating some data","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"using SpeedyWeather\n\nspectral_grid = SpectralGrid(trunc=31,nlev=1)\nforcing = SpeedyWeather.JetStreamForcing(spectral_grid)\ndrag = QuadraticDrag(spectral_grid)\nmodel = ShallowWaterModel(;spectral_grid,forcing,drag)\nmodel.feedback.verbose = false # hide\nsimulation = initialize!(model);\nrun!(simulation,n_days=30)\nnothing # hide","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Now pretend you only have u,v to get vorticity (which is actually the prognostic variable in the model, so calculated anyway...).","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"u = simulation.diagnostic_variables.layers[1].grid_variables.u_grid\nv = simulation.diagnostic_variables.layers[1].grid_variables.v_grid\nvor = SpeedyTransforms.curl(u,v) / spectral_grid.radius\nnothing # hide","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Here, u,v are the grid-point velocity fields, and the function curl takes in either LowerTriangularMatrixs (no transform needed as all gradient operators act in spectral space), or, as shown here, arrays of the same grid and size. In this case, the function actually runs through the following steps","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"RingGrids.scale_coslat⁻¹!(u_grid)\nRingGrids.scale_coslat⁻¹!(v_grid)\n\nS = SpectralTransform(u_grid,one_more_degree=true)\nus = spectral(u_grid,S)\nvs = spectral(v_grid,S)\n\nreturn curl(us,vs)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"(Copies of) the velocity fields are unscaled by the cosine of latitude (see above), then transformed into spectral space, and the returned vor requires a manual division by the radius. We always unscale vector fields by the cosine of latitude before any curl, or div operation, as these omit those.","category":"page"},{"location":"speedytransforms/#One-more-degree-for-spectral-fields","page":"Submodule: SpeedyTransforms","title":"One more degree for spectral fields","text":"","category":"section"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"The SpectralTransform in general takes a one_more_degree keyword argument, if otherwise the returned LowerTriangularMatrix would be of size 32x32, setting this to true would return 33x32. The reason is that while most people would expect square lower triangular matrices for a triangular spectral truncation, all vector quantities always need one more degree (= one more row) because of a recursion relation in the meridional gradient. So as we want to take the curl of us,vs here, they need this additional degree, but in the returned lower triangular matrix this row is set to zero.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"info: One more degree for vector quantities\nAll gradient operators expect the input lower triangular matrices of shape N+1 times N. This one more degree of the spherical harmonics is required for the meridional derivative. Scalar quantities contain this degree too for size compatibility but they should not make use of it. Use spectral_truncation to add or remove this degree manually.","category":"page"},{"location":"speedytransforms/#Example:-Geostrophy-(continued)","page":"Submodule: SpeedyTransforms","title":"Example: Geostrophy (continued)","text":"","category":"section"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Now we transfer vor into grid-point space, but specify that we want it on the grid that we also used in spectral_grid. The Coriolis parameter for a grid like vor_grid is obtained, and we do the following for fzetag.","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"vor_grid = gridded(vor,Grid=spectral_grid.Grid)\nf = SpeedyWeather.coriolis(vor_grid)\nfζ_g = spectral_grid.Grid(vor_grid .* f ./ model.planet.gravity)\nnothing # hide","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"The last line is a bit awkward for now, as the element-wise multiplication between two grids escapes the grid and returns a vector that we wrap again into a grid. We will fix that in future releases. Now we need to apply the inverse Laplace operator to fzetag which we do as follows","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"fζ_g_spectral = spectral(fζ_g,one_more_degree=true);\nη = SpeedyTransforms.∇⁻²(fζ_g_spectral) * spectral_grid.radius^2\nη_grid = gridded(η,Grid=spectral_grid.Grid)\nnothing # hide","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Note the manual scaling with the radius R^2 here. We now compare the results","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"plot(η_grid)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Which is the interface displacement assuming geostrophy. The actual interface displacement contains also ageostrophy","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"plot(simulation.diagnostic_variables.surface.pres_grid)","category":"page"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Strikingly similar! The remaining differences are the ageostrophic motions but also note that the mean is off. This is because geostrophy only use/defines the gradient of eta not the absolute values itself. Our geostrophic eta_g has by construction a mean of zero (that is how we define the inverse Laplace operator) but the actual eta is some 1400m higher.","category":"page"},{"location":"speedytransforms/#Functions-and-type-index","page":"Submodule: SpeedyTransforms","title":"Functions and type index","text":"","category":"section"},{"location":"speedytransforms/","page":"Submodule: SpeedyTransforms","title":"Submodule: SpeedyTransforms","text":"Modules = [SpeedyWeather.SpeedyTransforms]","category":"page"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.SpectralTransform","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.SpectralTransform","text":"S = SpectralTransform{NF<:AbstractFloat}(...)\n\nSpectralTransform struct that contains all parameters and preallocated arrays for the spectral transform.\n\n\n\n\n\n","category":"type"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.SpectralTransform-Union{Tuple{AbstractArray{Complex{NF}, 2}}, Tuple{NF}} where NF","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.SpectralTransform","text":"S = SpectralTransform( alms::AbstractMatrix{Complex{NF}};\n recompute_legendre::Bool=true,\n Grid::Type{<:AbstractGrid}=DEFAULT_GRID)\n\nGenerator function for a SpectralTransform struct based on the size of the spectral coefficients alms and the grid Grid. Recomputes the Legendre polynomials by default.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.SpectralTransform-Union{Tuple{NF}, Tuple{Type{NF}, Type{<:SpeedyWeather.RingGrids.AbstractGrid}, Int64, Int64}} where NF","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.SpectralTransform","text":"SpectralTransform(\n ::Type{NF},\n Grid::Type{<:SpeedyWeather.RingGrids.AbstractGrid},\n lmax::Int64,\n mmax::Int64;\n recompute_legendre,\n legendre_shortcut,\n dealiasing\n) -> SpectralTransform\n\n\nGenerator function for a SpectralTransform struct. With NF the number format, Grid the grid type <:AbstractGrid and spectral truncation lmax,mmax this function sets up necessary constants for the spetral transform. Also plans the Fourier transforms, retrieves the colatitudes, and preallocates the Legendre polynomials (if recompute_legendre == false) and quadrature weights.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.SpectralTransform-Union{Tuple{SpeedyWeather.RingGrids.AbstractGrid{NF}}, Tuple{NF}} where NF","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.SpectralTransform","text":"S = SpectralTransform( map::AbstractGrid;\n recompute_legendre::Bool=true)\n\nGenerator function for a SpectralTransform struct based on the size and grid type of gridded field map. Recomputes the Legendre polynomials by default.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.RingGrids.get_nlat_half","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.RingGrids.get_nlat_half","text":"get_nlat_half(trunc::Integer) -> Any\nget_nlat_half(trunc::Integer, dealiasing::Real) -> Any\n\n\nFor the spectral truncation trunc (e.g. 31 for T31) return the grid resolution parameter nlat_half (number of latitude rings on one hemisphere including the Equator) following a dealiasing parameter (default 2) to match spectral and grid resolution.\n\n\n\n\n\n","category":"function"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.UV_from_vor!-Union{Tuple{NF}, Tuple{LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, SpectralTransform{NF}}} where NF<:AbstractFloat","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.UV_from_vor!","text":"UV_from_vor!(\n U::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n V::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n vor::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n S::SpectralTransform{NF<:AbstractFloat}\n)\n\n\nGet U,V (=(u,v)*coslat) from vorticity ζ spectral space (divergence D=0) Two operations are combined into a single linear operation. First, invert the spherical Laplace ∇² operator to get stream function from vorticity. Then compute zonal and meridional gradients to get U,V.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.UV_from_vordiv!-Union{Tuple{NF}, Tuple{LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, SpectralTransform{NF}}} where NF<:AbstractFloat","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.UV_from_vordiv!","text":"UV_from_vordiv!(\n U::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n V::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n vor::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n div::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n S::SpectralTransform{NF<:AbstractFloat}\n) -> Any\n\n\nGet U,V (=(u,v)*coslat) from vorticity ζ and divergence D in spectral space. Two operations are combined into a single linear operation. First, invert the spherical Laplace ∇² operator to get stream function from vorticity and velocity potential from divergence. Then compute zonal and meridional gradients to get U,V.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms._divergence!-Union{Tuple{NF}, Tuple{Any, LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, SpectralTransform{NF}}} where NF<:AbstractFloat","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms._divergence!","text":"_divergence!(\n kernel,\n div::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n u::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n v::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n S::SpectralTransform{NF<:AbstractFloat}\n)\n\n\nGeneric divergence function of vector u,v that writes into the output into div. Generic as it uses the kernel kernel such that curl, div, add or flipsign options are provided through kernel, but otherwise a single function is used.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.curl!-Tuple{LowerTriangularMatrix, LowerTriangularMatrix, LowerTriangularMatrix, SpectralTransform}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.curl!","text":"curl!(\n curl::LowerTriangularMatrix,\n u::LowerTriangularMatrix,\n v::LowerTriangularMatrix,\n S::SpectralTransform;\n flipsign,\n add\n)\n\n\nCurl of a vector u,v written into curl, curl = ∇×(u,v). u,v are expected to have a 1/coslat-scaling included, then curl is not scaled. flipsign option calculates -∇×(u,v) instead. add option calculates curl += ∇×(u,v) instead. flipsign and add can be combined. This functions only creates the kernel and calls the generic divergence function _divergence! subsequently with flipped u,v -> v,u for the curl.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.curl-Tuple{LowerTriangularMatrix, LowerTriangularMatrix}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.curl","text":"curl(\n u::LowerTriangularMatrix,\n v::LowerTriangularMatrix\n) -> Any\n\n\nCurl (∇×) of two vector components u,v of size (n+1)xn, the last row will be set to zero in the returned LowerTriangularMatrix. This function requires both u,v to be transforms of fields that are scaled with 1/cos(lat). An example usage is therefore\n\nRingGrids.scale_coslat⁻¹!(u_grid)\nRingGrids.scale_coslat⁻¹!(v_grid)\nu = spectral(u_grid)\nv = spectral(v_grid)\nvor = curl(u,v)\nvor_grid = gridded(div)\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.curl-Union{Tuple{Grid}, Tuple{Grid, Grid}} where Grid<:SpeedyWeather.RingGrids.AbstractGrid","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.curl","text":"curl(\n u::SpeedyWeather.RingGrids.AbstractGrid,\n v::SpeedyWeather.RingGrids.AbstractGrid\n) -> Any\n\n\nCurl (∇×) of two vector components u,v on a grid. Applies 1/coslat scaling, transforms to spectral space and returns the spectral curl, which is scaled with the radius of the sphere. Divide by radius for unscaling.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.divergence!-Tuple{LowerTriangularMatrix, LowerTriangularMatrix, LowerTriangularMatrix, SpectralTransform}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.divergence!","text":"divergence!(\n div::LowerTriangularMatrix,\n u::LowerTriangularMatrix,\n v::LowerTriangularMatrix,\n S::SpectralTransform;\n flipsign,\n add\n)\n\n\nDivergence of a vector u,v written into div, div = ∇⋅(u,v). u,v are expected to have a 1/coslat-scaling included, then div is not scaled. flipsign option calculates -∇⋅(u,v) instead. add option calculates div += ∇⋅(u,v) instead. flipsign and add can be combined. This functions only creates the kernel and calls the generic divergence function _divergence! subsequently.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.divergence-Tuple{LowerTriangularMatrix, LowerTriangularMatrix}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.divergence","text":"divergence(\n u::LowerTriangularMatrix,\n v::LowerTriangularMatrix\n) -> Any\n\n\nDivergence (∇⋅) of two vector components u,v which need to have size (n+1)xn, the last row will be set to zero in the returned LowerTriangularMatrix. This function requires both u,v to be transforms of fields that are scaled with 1/cos(lat). An example usage is therefore\n\nRingGrids.scale_coslat⁻¹!(u_grid)\nRingGrids.scale_coslat⁻¹!(v_grid)\nu = spectral(u_grid,one_more_degree=true)\nv = spectral(v_grid,one_more_degree=true)\ndiv = divergence(u,v)\ndiv_grid = gridded(div)\n\nBoth div and div_grid are scaled with the radius.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.divergence-Union{Tuple{Grid}, Tuple{Grid, Grid}} where Grid<:SpeedyWeather.RingGrids.AbstractGrid","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.divergence","text":"divergence(\n u::SpeedyWeather.RingGrids.AbstractGrid,\n v::SpeedyWeather.RingGrids.AbstractGrid\n) -> Any\n\n\nDivergence (∇⋅) of two vector components u,v on a grid. Applies 1/coslat scaling, transforms to spectral space and returns the spectral divergence, which is scaled with the radius of the sphere. Divide by radius for unscaling.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.get_recursion_factors-Union{Tuple{NF}, Tuple{Type{NF}, Int64, Int64}} where NF<:AbstractFloat","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.get_recursion_factors","text":"get_recursion_factors( ::Type{NF}, # number format NF\n lmax::Int, # max degree l of spherical harmonics (0-based here)\n mmax::Int # max order m of spherical harmonics\n ) where {NF<:AbstractFloat}\n\nReturns a matrix of recursion factors ϵ up to degree lmax and order mmax of number format NF.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.get_truncation","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.get_truncation","text":"get_truncation(nlat_half::Integer) -> Any\nget_truncation(nlat_half::Integer, dealiasing::Real) -> Any\n\n\nFor the grid resolution parameter nlat_half (e.g. 24 for a 48-ring FullGaussianGrid) return the spectral truncation trunc (max degree of spherical harmonics) following a dealiasing parameter (default 2) to match spectral and grid resolution.\n\n\n\n\n\n","category":"function"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.gridded!-Union{Tuple{NF}, Tuple{SpeedyWeather.RingGrids.AbstractGrid{NF}, LowerTriangularMatrix{Complex{NF}}, SpectralTransform{NF}}} where NF<:AbstractFloat","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.gridded!","text":"gridded!( map::AbstractGrid,\n alms::LowerTriangularMatrix,\n S::SpectralTransform)\n\nSpectral transform (spectral to grid) of the spherical harmonic coefficients alms to a gridded field map. The spectral transform is number format-flexible as long as the parametric types of map, alms, S are identical. The spectral transform is grid-flexible as long as the typeof(map)<:AbstractGrid. Uses the precalculated arrays, FFT plans and other constants in the SpectralTransform struct S.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.gridded-Union{Tuple{AbstractMatrix{T}}, Tuple{T}, Tuple{NF}} where {NF, T<:Complex{NF}}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.gridded","text":"gridded(\n alms::AbstractArray{T<:Complex{NF}, 2};\n recompute_legendre,\n Grid,\n kwargs...\n) -> Any\n\n\nSpectral transform (spectral to grid space) from spherical coefficients alms to a newly allocated gridded field map. Based on the size of alms the grid type grid, the spatial resolution is retrieved based on the truncation defined for grid. SpectralTransform struct S is allocated to execute gridded(alms,S).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.gridded-Union{Tuple{NF}, Tuple{AbstractMatrix, SpectralTransform{NF}}} where NF","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.gridded","text":"gridded(\n alms::AbstractMatrix,\n S::SpectralTransform{NF};\n kwargs...\n) -> Any\n\n\nSpectral transform (spectral to grid space) from spherical coefficients alms to a newly allocated gridded field map with precalculated properties based on the SpectralTransform struct S. alms is converted to a LowerTriangularMatrix to execute the in-place gridded!.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.is_power_2-Tuple{Integer}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.is_power_2","text":"true/false = is_power_2(i::Integer)\n\nChecks whether an integer i is a power of 2, i.e. i = 2^k, with k = 0,1,2,3,....\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.roundup_fft-Union{Tuple{Integer}, Tuple{T}} where T<:Integer","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.roundup_fft","text":"m = roundup_fft(n::Int;\n small_primes::Vector{Int}=[2,3,5])\n\nReturns an integer m >= n with only small prime factors 2, 3 (default, others can be specified with the keyword argument small_primes) to obtain an efficiently fourier-transformable number of longitudes, m = 2^i * 3^j * 5^k >= n, with i,j,k >=0.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral!-Union{Tuple{NF}, Tuple{LowerTriangularMatrix{Complex{NF}}, SpeedyWeather.RingGrids.AbstractGrid{NF}, SpectralTransform{NF}}} where NF<:AbstractFloat","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.spectral!","text":"spectral!( alms::LowerTriangularMatrix,\n map::AbstractGrid,\n S::SpectralTransform)\n\nSpectral transform (grid to spectral space) from the gridded field map on a grid<:AbstractGrid to a LowerTriangularMatrix of spherical harmonic coefficients alms. Uses FFT in the zonal direction, and a Legendre Transform in the meridional direction exploiting symmetries. The spectral transform is number format-flexible as long as the parametric types of map, alms, S are identical. The spectral transform is grid-flexible as long as the typeof(map)<:AbstractGrid. Uses the precalculated arrays, FFT plans and other constants in the SpectralTransform struct S.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral-Tuple{AbstractMatrix}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.spectral","text":"spectral(\n map::AbstractMatrix;\n Grid,\n kwargs...\n) -> LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat\n\n\nConverts map to grid(map) to execute spectral(map::AbstractGrid;kwargs...).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral-Union{Tuple{NF}, Tuple{SpeedyWeather.RingGrids.AbstractGrid, SpectralTransform{NF}}} where NF","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.spectral","text":"spectral(\n map::SpeedyWeather.RingGrids.AbstractGrid,\n S::SpectralTransform{NF}\n) -> LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat\n\n\nSpectral transform (grid to spectral) map to grid(map) to execute spectral(map::AbstractGrid;kwargs...).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral-Union{Tuple{SpeedyWeather.RingGrids.AbstractGrid{NF}}, Tuple{NF}} where NF","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.spectral","text":"spectral(\n map::SpeedyWeather.RingGrids.AbstractGrid{NF};\n recompute_legendre,\n one_more_degree\n) -> LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat\n\n\nConverts map to Grid(map) to execute spectral(map::AbstractGrid;kwargs...).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_interpolation-Union{Tuple{NF}, Tuple{Type{NF}, LowerTriangularMatrix, Integer, Integer}} where NF","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.spectral_interpolation","text":"alms_interp = spectral_interpolation( ::Type{NF},\n alms::LowerTriangularMatrix,\n ltrunc::Integer,\n mtrunc::Integer\n ) where NF\n\nReturns a spectral coefficient matrix alms_interp that is alms padded with zeros to interpolate in spectral space. If trunc is smaller or equal to the implicit truncation in alms obtained from its size than spectral_truncation is automatically called instead, returning alms_trunc, a coefficient matrix that is smaller than alms, implicitly setting higher degrees and orders to zero.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_smoothing!-Tuple{LowerTriangularMatrix, Real}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.spectral_smoothing!","text":"spectral_smoothing!(A::LowerTriangularMatrix,c;power=1)\n\nSmooth the spectral field A following A = (1-(1-c)∇²ⁿ) with power n of a normalised Laplacian so that the highest degree lmax is dampened by multiplication with c. Anti-diffusion for c>1.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_smoothing-Tuple{LowerTriangularMatrix, Real}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.spectral_smoothing","text":"A_smooth = spectral_smoothing(A::LowerTriangularMatrix,c;power=1)\n\nSmooth the spectral field A following A_smooth = (1-c*∇²ⁿ)A with power n of a normalised Laplacian so that the highest degree lmax is dampened by multiplication with c. Anti-diffusion for c<0.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_truncation!-Tuple{AbstractMatrix, Int64}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.spectral_truncation!","text":"spectral_truncation!(alms,trunc)\n\nTruncate spectral coefficients alms in-place by setting (a) the upper right triangle to zero and (b) all coefficients for which the degree l is larger than the truncation trunc.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_truncation!-Tuple{AbstractMatrix}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.spectral_truncation!","text":"spectral_truncation!(alms)\n\nTruncate spectral coefficients alms in-place by setting the upper right triangle to zero. This is to enforce that all coefficients for which the degree l is larger than order m are zero.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_truncation!-Union{Tuple{NF}, Tuple{AbstractMatrix{NF}, Integer, Integer}} where NF","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.spectral_truncation!","text":"spectral_truncation!(alms::AbstractMatrix,ltrunc::Integer,mtrunc::Integer)\n\nTruncate spectral coefficients alms in-place by setting (a) the upper right triangle to zero and (b) all coefficients for which the degree l is larger than the truncation ltrunc or order m larger than the truncaction mtrunc.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_truncation!-Union{Tuple{NF}, Tuple{LowerTriangularMatrix{NF}, Integer, Integer}} where NF","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.spectral_truncation!","text":"spectral_truncation!(alms::LowerTriangularMatrix,ltrunc::Integer,mtrunc::Integer)\n\nTruncate spectral coefficients alms in-place by setting all coefficients for which the degree l is larger than the truncation ltrunc or order m larger than the truncaction mtrunc. Similar to spectral_truncation!(::AbstractMatrix, ...) but skips the upper triangle which is zero by design for LowerTriangularMatrix.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_truncation-Union{Tuple{NF}, Tuple{Type{NF}, LowerTriangularMatrix, Integer, Integer}} where NF","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.spectral_truncation","text":"alms_trunc = spectral_truncation(alms,trunc)\n\nReturns a spectral coefficient matrix alms_trunc that is truncated from alms to the size (trunc+1)². alms_trunc only contains those coefficient of alms for which m,l ≤ trunc, and l ≥ m are zero anyway. If trunc is larger than the implicit truncation in alms obtained from its size than spectral_interpolation is automatically called instead, returning alms_interp, a coefficient matrix that is larger than alms with padded zero coefficients.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.ϵlm-Tuple{Int64, Int64}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.ϵlm","text":"ϵ = ϵ(l,m)\n\nRecursion factors ϵ as a function of degree l and order m (0-based) of the spherical harmonics. ϵ(l,m) = sqrt((l^2-m^2)/(4*l^2-1)) with default number format Float64.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.ϵlm-Union{Tuple{NF}, Tuple{Type{NF}, Int64, Int64}} where NF","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.ϵlm","text":"ϵ = ϵ(NF,l,m)\n\nRecursion factors ϵ as a function of degree l and order m (0-based) of the spherical harmonics. ϵ(l,m) = sqrt((l^2-m^2)/(4*l^2-1)) and then converted to number format NF.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇!-Union{Tuple{NF}, Tuple{LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, SpectralTransform{NF}}} where NF<:AbstractFloat","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.∇!","text":"∇!(\n dpdx::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n dpdy::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n p::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},\n S::SpectralTransform{NF<:AbstractFloat}\n) -> Tuple{LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat, LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat}\n\n\nApplies the gradient operator ∇ applied to input p and stores the result in dpdx (zonal derivative) and dpdy (meridional derivative). The gradient operator acts on the unit sphere and therefore omits the radius scaling\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇²!-Union{Tuple{NF}, Tuple{LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, SpectralTransform{NF}}} where NF<:AbstractFloat","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.∇²!","text":"∇²!( ∇²alms::LowerTriangularMatrix,\n alms::LowerTriangularMatrix,\n S::SpectralTransform;\n add::Bool=false,\n flipsign::Bool=false,\n inverse::Bool=false)\n\nLaplace operator ∇² applied to the spectral coefficients alms in spherical coordinates. The radius R is omitted in the eigenvalues which are precomputed in S. ∇²! is the in-place version which directly stores the output in the first argument ∇²alms.\n\nKeyword arguments\n\nadd=true adds the ∇²(alms) to the output\nflipsign=true computes -∇²(alms) instead\ninverse=true computes ∇⁻²(alms) instead\n\nDefault is add=false, flipsign=false, inverse=false. These options can be combined.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇²-Tuple{LowerTriangularMatrix, SpectralTransform}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.∇²","text":"∇²(alms::LowerTriangularMatrix, S::SpectralTransform) -> Any\n\n\nLaplace operator ∇² applied to input alms, using precomputed eigenvalues from S. The Laplace operator acts on the unit sphere and therefore omits the 1/radius^2 scaling\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇²-Tuple{LowerTriangularMatrix}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.∇²","text":"∇²(alms::LowerTriangularMatrix) -> Any\n\n\nReturns the Laplace operator ∇² applied to input alms. The Laplace operator acts on the unit sphere and therefore omits the 1/radius^2 scaling\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇⁻²!-Union{Tuple{NF}, Tuple{LowerTriangularMatrix{Complex{NF}}, LowerTriangularMatrix{Complex{NF}}, SpectralTransform{NF}}} where NF<:AbstractFloat","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.∇⁻²!","text":"∇⁻²!( ∇⁻²alms::LowerTriangularMatrix,\n alms::LowerTriangularMatrix,\n S::SpectralTransform;\n add::Bool=false,\n flipsign::Bool=false)\n\nCalls ∇²!(∇⁻²alms, alms, S; add, flipsign, inverse=true).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇⁻²-Tuple{LowerTriangularMatrix, SpectralTransform}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.∇⁻²","text":"∇⁻²(\n ∇²alms::LowerTriangularMatrix,\n S::SpectralTransform\n) -> Any\n\n\nInverseLaplace operator ∇⁻² applied to input alms, using precomputed eigenvalues from S. The Laplace operator acts on the unit sphere and therefore omits the radius^2 scaling\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇⁻²-Tuple{LowerTriangularMatrix}","page":"Submodule: SpeedyTransforms","title":"SpeedyWeather.SpeedyTransforms.∇⁻²","text":"∇⁻²(∇²alms::LowerTriangularMatrix) -> Any\n\n\nReturns the inverse Laplace operator ∇⁻² applied to input alms. The Laplace operator acts on the unit sphere and therefore omits the radius^2 scaling\n\n\n\n\n\n","category":"method"},{"location":"grids/#Grids","page":"Grids","title":"Grids","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"The spectral transform (the Spherical Harmonic Transform) in SpeedyWeather.jl supports any ring-based equi-longitude grid. Several grids are already implemented but other can be added. The following pages will describe an overview of these grids and but let's start but how they can be used","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(Grid = FullGaussianGrid)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"The life of every SpeedyWeather.jl simulation starts with a SpectralGrid object which defines the resolution in spectral and in grid-point space. The generator SpectralGrid() can take as a keyword argument Grid which can be any of the grids described below. The resolution of the grid, however, is not directly chosen, but determined from the spectral resolution trunc and the dealiasing factor. More in SpectralGrid and Matching spectral and grid resolution.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"info: RingGrids is a module too!\nWhile RingGrids is the underlying module that SpeedyWeather.jl uses for data structs on the sphere, the module can also be used independently of SpeedyWeather, for example to interpolate between data on different grids. See RingGrids","category":"page"},{"location":"grids/#Ring-based-equi-longitude-grids","page":"Grids","title":"Ring-based equi-longitude grids","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"SpeedyWeather.jl's spectral transform supports all ring-based equi-longitude grids. These grids have their grid points located on rings with constant latitude and on these rings the points are equi-spaced in longitude. There is technically no constrain on the spacing of the latitude rings, but the Legendre transform requires a quadrature to map those to spectral space and back. Common choices for latitudes are the Gaussian latitudes which use the Gaussian quadrature, or equi-angle latitudes (i.e. just regular latitudes but excluding the poles) that use the Clenshaw-Curtis quadrature. The longitudes have to be equi-spaced on every ring, which is necessary for the fast Fourier transform, as one would otherwise need to use a non-uniform Fourier transform. In SpeedyWeather.jl the first grid point on any ring can have a longitudinal offset though, for example by spacing 4 points around the globe at 45˚E, 135˚E, 225˚E, and 315˚E. In this case the offset is 45˚E as the first point is not at 0˚E.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"info: Is the FullClenshawGrid a longitude-latitude grid?\nShort answer: Yes. The FullClenshawGrid is a specific longitude-latitude grid with equi-angle spacing. The most common grids for geoscientific data use regular spacings for 0-360˚E in longitude and 90˚N-90˚S. The FullClenshawGrid does that too, but it does not have a point on the North or South pole, and the central latitude ring sits exactly on the Equator. We name it Clenshaw following the Clenshaw-Curtis quadrature that is used in the Legendre transfrom in the same way as Gaussian refers to the Gaussian quadrature.","category":"page"},{"location":"grids/#Implemented-grids","page":"Grids","title":"Implemented grids","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"All grids in SpeedyWeather.jl are a subtype of AbstractGrid, i.e. <: AbstractGrid. We further distinguish between full, and reduced grids. Full grids have the same number of longitude points on every latitude ring (i.e. points converge towards the poles) and reduced grids reduce the number of points towards the poles to have them more evenly spread out across the globe. More evenly does not necessarily mean that a grid is equal-area, meaning that every grid cell covers exactly the same area (although the shape changes).","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"Currently the following full grids <: AbstractFullGrid are implemented","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"FullGaussianGrid, a full grid with Gaussian latitudes\nFullClenshawGrid, a full grid with equi-angle latitudes","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"and additionally we have FullHEALPixGrid and FullOctaHEALPixGrid which are the full grid equivalents to the HEALPix grid and the OctaHEALPix grid discussed below. Full grid equivalent means that they have the same latitude rings, but no reduction in the number of points per ring towards the poles and no longitude offset. Other implemented reduced grids are","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"OctahedralGaussianGrid, a reduced grid with Gaussian latitudes based on an octahedron\nOctahedralClenshawGrid, similar but based on equi-angle latitudes\nHEALPixGrid, an equal-area grid based on a dodecahedron with 12 faces\nOctaHEALPixGrid, an equal-area grid from the class of HEALPix grids but based on an octahedron.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"An overview of these grids is visualised here, and a more detailed description follows below.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"(Image: Overview of implemented grids in SpeedyWeather.jl)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"Visualised are each grid's grid points (white dots) and grid faces (white lines). All grids shown have 16 latitude rings on one hemisphere, Equator included. The total number of grid points is denoted in the top left of every subplot. The sphere is shaded with grey, orange and turquoise regions to denote the hemispheres in a and b, the 8 octahedral faces c, d,f and the 12 dodecahedral faces (or base pixels) in e. Coastlines are added for orientation.","category":"page"},{"location":"grids/#Grid-resolution","page":"Grids","title":"Grid resolution","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"All grids use the same resolution parameter nlat_half, i.e. the number of rings on one hemisphere, Equator included. The Gaussian grids (full and reduced) do not have a ring on the equator, so their total number of rings nlat is always even and twice nlat_half. Clenshaw-Curtis grids and the HEALPix grids have a ring on the equator such their total number of rings is always odd and one less than the Gaussian grids at the same nlat_half. ","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"info: HEALPix grids do not use Nside as resolution parameter\nThe original formulation for HEALPix grids use N_side, the number of grid points along the edges of each basepixel (8 in the figure above), SpeedyWeather.jl uses nlat_half, the number of rings on one hemisphere, Equator included, for all grids. This is done for consistency across grids. We may use N_side for the documentation or within functions though.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"Related: Effective grid resolution and Available horizontal resolutions.","category":"page"},{"location":"grids/#Matching-spectral-and-grid-resolution","page":"Grids","title":"Matching spectral and grid resolution","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"A given spectral resolution can be matched to a variety of grid resolutions. A cubic grid, for example, combines a spectral truncation T with a grid resolution N (=nlat_half) such that T + 1 = N. Using T31 and an O32 is therefore often abbreviated as Tco31 meaning that the spherical harmonics are truncated at l_max=31 in combination with N=32, i.e. 64 latitude rings in total on an octahedral Gaussian grid. In SpeedyWeather.jl the choice of the order of truncation is controlled with the dealiasing parameter in the SpectralGrid construction.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"Let J be the total number of rings. Then we have","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"T approx J for linear truncation, i.e. dealiasing = 1\nfrac32T approx J for quadratic truncation, i.e. dealiasing = 2\n2T approx J for cubic truncation, , i.e. dealiasing = 3","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"and in general fracm+12T approx J for m-th order truncation. So the higher the truncation order the more grid points are used in combination with the same spectral resolution. A higher truncation order therefore makes all grid-point calculations more expensive, but can represent products of terms on the grid (which will have higher wavenumber components) to a higher accuracy as more grid points are available within a given wavelength. Using a sufficiently high truncation is therefore one way to avoid aliasing. A quick overview of how the grid resolution changes when dealiasing is passed onto SpectralGrid on the FullGaussianGrid","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"trunc dealiasing FullGaussianGrid size\n31 1 64x32\n31 2 96x48\n31 3 128x64\n42 1 96x48\n42 2 128x64\n42 3 192x96\n... ... ...","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"You will obtain this information every time you create a SpectralGrid(;Grid,trunc,dealiasing).","category":"page"},{"location":"grids/#FullGaussianGrid","page":"Grids","title":"Full Gaussian grid","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"(called FullGaussianGrid)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"The full Gaussian grid is a grid that uses regularly spaced longitudes which points that do not reduce in number towards the poles. That means for every latitude theta the longitudes phi are","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"phi_i = frac2pi (i-1)N_phi","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"with i = 1N_phi the in-ring index (1-based, counting from 0˚ eastward) and N_phi the number of longitudinal points on the grid. The first longitude is therefore 0˚, meaning that there is no longitudinal offset on this grid. There are always twice as many points in zonal direction as there are in meridional, i.e. N_phi = 2N_theta. The latitudes, however, are not regular, but chosen from the j-th zero crossing z_j(l) of the l-th Legendre polynomial. For theta in latitudes","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"sin(theta_j) = z_j(l) ","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"As it can be easy to mix up latitudes, colatitudes and as the Legendre polynomials are defined in 01 an overview of the first Gaussian latitudes (approximated for l2 for brevity)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"l Zero crossings z_j Latitudes [˚N]\n2 pm tfrac1sqrt3 pm 353\n4 pm 034 pm 086 pm 199 pm 5944\n6 pm 024 pm 066 pm 093 pm 138 pm 414 pm 688","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"Only even Legendre polynomials are used, such that there is always an even number of latitudes, with no latitude on the Equator. As you can already see from this short table, the Gaussian latitudes do not nest, i.e. different resolutions through different l do not share latitudes. The latitudes are also only approximately evenly spaced. Due to the Gaussian latitudes, a spectral transform with a full Gaussian grid is exact as long as the truncation is at least quadratic, see Matching spectral and grid resolution. Exactness here means that only rounding errors occur in the transform, meaning that the transform error is very small compared to other errors in a simulation. This property arises from that property of the Gauss-Legendre quadrature, which is used in the Spherical Harmonic Transform with a full Gaussian grid.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"On the full Gaussian grid there are in total N_phi N_theta grid points, which are squeezed towards the poles, making the grid area smaller and smaller following a cosine. But no points are on the poles as z=-1 or 1 is never a zero crossing of the Legendre polynomials.","category":"page"},{"location":"grids/#OctahedralGaussianGrid","page":"Grids","title":"Octahedral Gaussian grid","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"(called OctahedralGaussianGrid)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"The octahedral Gaussian grid is a reduced grid, i.e. the number of longitudinal points reduces towards the poles. It still uses the Gaussian latitudes from the full Gaussian grid so the exactness property of the spherical harmonic transform also holds for this grid. However, the longitudes phi_i with i = 116+4j on the j-th latitude ring (starting with 1 around the north pole), j=1tfracN_theta2, are now, on the northern hemisphere,","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"phi_i = frac2pi (i-1)16 + 4j","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"We start with 20 points, evenly spaced, starting at 0˚E, around the first latitude ring below the north pole. The next ring has 24 points, then 28, and so on till reaching the Equator (which is not a ring). For the southern hemisphere all points are mirrored around the Equator. For more details see Malardel, 2016[M16].","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"Note that starting with 20 grid points on the first ring is a choice that ECMWF made with their grid for accuracy reasons. An octahedral Gaussian grid can also be defined starting with fewer grid points on the first ring. However, in SpeedyWeather.jl we follow ECMWF's definition. ","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"The grid cells of an octahedral Gaussian grid are not exactly equal area, but are usually within a factor of two. This largely solves the efficiency problem of having too many grid points near the poles for computational, memory and data storage reasons.","category":"page"},{"location":"grids/#FullClenshawGrid","page":"Grids","title":"Full Clenshaw-Curtis grid","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"(called FullClenshawGrid)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"The full Clenshaw-Curtis grid is a regular longitude-latitude grid, but a specific one: The colatitudes theta_j, and the longitudes phi_i are","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"theta_j = fracjN_theta + 1pi quad phi_i = frac2pi (i-1)N_phi","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"with i the in-ring zonal index i = 1N_phi and j = 1 N_theta the ring index starting with 1 around the north pole. There is no grid point on the poles, but in contrast to the Gaussian grids there is a ring on the Equator. The longitudes are shared with the full Gaussian grid. Being a full grid, also the full Clenshaw-Curtis grid suffers from too many grid points around the poles, this is addressed with the octahedral Clenshaw-Curtis grid.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"The full Clenshaw-Curtis grid gets its name from the Clenshaw-Curtis quadrature that is used in the Legendre transform (see Spherical Harmonic Transform). This quadrature relies on evenly spaced latitudes, which also means that this grid nests, see Hotta and Ujiie[HU18]. More importantly for our application, the Clenshaw-Curtis grids (including the octahedral described below) allow for an exact transform with cubic truncation (see Matching spectral and grid resolution). Recall that the Gaussian latitudes allow for an exact transform with quadratic truncation, so the Clenshaw-Curtis grids require more grid points for the same spectral resolution to be exact. But compared to other errors during a simulation this error may be masked anyway.","category":"page"},{"location":"grids/#OctahedralClenshawGrid","page":"Grids","title":"Octahedral Clenshaw-Curtis grid","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"(called OctahedralClenshawGrid)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"In the same as we constructed the octahedral Gaussian grid from the full Gaussian grid, the octahedral Clenshaw-Curtis grid can be constructed from the full Clenshaw-Curtis grid. It therefore shares the latitudes with the full grid, but the longitudes with the octahedral Gaussian grid.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"theta_j = fracjN_theta + 1pi quad phi_i = frac2pi (i-1)16 + 4j","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"Notation as before, but note that the definition for phi_i only holds for the northern hemisphere, Equator included. The southern hemisphere is mirrored. The octahedral Clenshaw-Curtis grid inherits the exactness properties from the full Clenshaw-Curtis grid, but as it is a reduced grid, it is more efficient in terms of computational aspects and memory than the full grid. Hotta and Ujiie[HU18] describe this grid in more detail.","category":"page"},{"location":"grids/#HEALPixGrid","page":"Grids","title":"HEALPix grid","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"(called HEALPixGrid)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"Technically, HEALPix grids are a class of grids that tessalate the sphere into faces that are often called basepixels. For each member of this class there are N_varphi basepixels in zonal direction and N_theta basepixels in meridional direction. For N_varphi = 4 and N_theta = 3 we obtain the classical HEALPix grid with N_varphi N_theta = 12 basepixels shown above in Implemented grids. Each basepixel has a quadratic number of grid points in them. There's an equatorial zone where the number of zonal grid points is constant (always 2N, so 32 at N=16) and there are polar caps above and below the equatorial zone with the border at cos(theta) = 23 (theta in colatitudes).","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"Following Górski, 2004[G04], the z=cos(theta) colatitude of the j-th ring in the north polar cap, j=1N_side with 2N_side = N is ","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"z = 1 - fracj^23N_side^2","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"and on that ring, the longitude phi of the i-th point (i is the in-ring-index) is at","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"phi = fracpi2j(i-tfrac12)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"The in-ring index i goes from i=14 for the first (i.e. northern-most) ring, i=18 for the second ring and i = 14j for the j-th ring in the northern polar cap.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"In the north equatorial belt j=N_side2N_side this changes to","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"z = frac43 - frac2j3N_side","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"and the longitudes change to (i is always i = 14N_side in the equatorial belt meaning the number of longitude points is constant here)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"phi = fracpi2N_side(i - fracs2) quad s = (j - N_side + 1) mod 2","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"The modulo function comes in as there is an alternating longitudinal offset from the prime meridian (see Implemented grids). For the southern hemisphere the grid point locations can be obtained by mirror symmetry.","category":"page"},{"location":"grids/#Grid-cell-boundaries","page":"Grids","title":"Grid cell boundaries","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"The cell boundaries are obtained by setting i = k + 12 or i = k + 12 + j (half indices) into the equations above, such that z(phik), a function for the cosine of colatitude z of index k and the longitude phi is obtained. These are then (northern polar cap)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"z = 1 - frack^23N_side^2left(fracpi2phi_tright)^2 quad z = 1 - frack^23N_side^2left(fracpi2phi_t - piright)^2","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"with phi_t = phi mod tfracpi2 and in the equatorial belt","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"z = frac23-frac4k3N_side pm frac8phi3pi","category":"page"},{"location":"grids/#OctaHEALPixGrid","page":"Grids","title":"OctaHEALPix grid","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"(called OctaHEALPixGrid)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"While the classic HEALPix grid is based on a dodecahedron, other choices for N_varphi and N_theta in the class of HEALPix grids will change the number of faces there are in zonal/meridional direction. With N_varphi = 4 and N_theta = 1 we obtain a HEALPix grid that is based on an octahedron, which has the convenient property that there are twice as many longitude points around the equator than there are latitude rings between the poles. This is a desirable for truncation as this matches the distances too, 2pi around the Equator versus pi between the poles. N_varphi = 6 N_theta = 2 or N_varphi = 8 N_theta = 3 are other possible choices for this, but also more complicated. See Górski, 2004[G04] for further examples and visualizations of these grids.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"We call the N_varphi = 4 N_theta = 1 HEALPix grid the OctaHEALPix grid, which combines the equal-area property of the HEALPix grids with the octahedron that's also used in the OctahedralGaussianGrid or the OctahedralClenshawGrid. As N_theta = 1 there is no equatorial belt which simplifies the grid. The latitude of the j-th isolatitude ring on the OctaHEALPixGrid is defined by","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"z = 1 - fracj^2N^2","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"with j=1N, and similarly for the southern hemisphere by symmetry. On this grid N_side = N where N= nlat_half, the number of latitude rings on one hemisphere, Equator included, because each of the 4 basepixels spans from pole to pole and covers a quarter of the sphere. The longitudes with in-ring- index i = 14j are","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"phi = fracpi2j(i - tfrac12)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"and again, the southern hemisphere grid points are obtained by symmetry.","category":"page"},{"location":"grids/#Grid-cell-boundaries-2","page":"Grids","title":"Grid cell boundaries","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"Similar to the grid cell boundaries for the HEALPix grid, the OctaHEALPix grid's boundaries are","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"z = 1 - frack^2N^2left(fracpi2phi_tright)^2 quad z = 1 - frack^2N^2left(fracpi2phi_t - piright)^2","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"The 3N_side^2 in the denominator of the HEALPix grid came simply N^2 for the OctaHEALPix grid and there's no separate equation for the equatorial belt (which doesn't exist in the OctaHEALPix grid).","category":"page"},{"location":"grids/#References","page":"Grids","title":"References","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"[G04]: Górski, Hivon, Banday, Wandelt, Hansen, Reinecke, Bartelmann, 2004. HEALPix: A FRAMEWORK FOR HIGH-RESOLUTION DISCRETIZATION AND FAST ANALYSIS OF DATA DISTRIBUTED ON THE SPHERE, The Astrophysical Journal. doi:10.1086/427976","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"[M16]: S Malardel, et al., 2016: A new grid for the IFS, ECMWF Newsletter 146. https://www.ecmwf.int/sites/default/files/elibrary/2016/17262-new-grid-ifs.pdf","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"[HU18]: Daisuke Hotta and Masashi Ujiie, 2018: A nestable, multigrid-friendly grid on a sphere for global spectralmodels based on Clenshaw–Curtis quadrature, Quarterly Journal of the Royal Meteorological Society, DOI: 10.1002/qj.3282","category":"page"},{"location":"primitiveequation/#Primitive-equation-model","page":"Primitive equation model","title":"Primitive equation model","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The primitive equations are a hydrostatic approximation of the compressible Navier-Stokes equations for an ideal gas on a rotating sphere. We largely follow the idealised spectral dynamical core developed by GFDL[GFDL1] and documented therein[GFDL2].","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The primitive equations solved by SpeedyWeather.jl for relative vorticity zeta, divergence mathcalD, logarithm of surface pressure ln p_s, temperature T and specific humidity q are","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"beginaligned\nfracpartial zetapartial t = nabla times (mathbfmathcalP_mathbfu\n+ (f+zeta)mathbfu_perp - W(mathbfu) - R_dT_vnabla ln p_s) \nfracpartial mathcalDpartial t = nabla cdot (mathcalP_mathbfu\n+ (f+zeta)mathbfu_perp - W(mathbfu) - R_dT_vnabla ln p_s) - nabla^2(frac12(u^2 + v^2) + Phi) \nfracpartial ln p_spartial t = -frac1p_s nabla cdot int_0^p_s mathbfudp \nfracpartial Tpartial t = mathcalP_T -nablacdot(mathbfuT) + TmathcalD - W(T) + kappa T_v fracD ln pDt \nfracpartial qpartial t = mathcalP_q -nablacdot(mathbfuq) + qmathcalD - W(q)\nendaligned","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"with velocity mathbfu = (uv), rotated velocity mathbfu_perp = (v-u), Coriolis parameter f, W the Vertical advection operator, dry air gas constant R_d, Virtual temperature T_v, Geopotential Phi, pressure p and surface pressure p_s, thermodynamic kappa = R_dc_p with c_p the heat capacity at constant pressure. Horizontal hyper diffusion of the form (-1)^n+1nunabla^2n with coefficient nu and power n is added for every variable that is advected, meaning zeta mathcalD T q, but left out here for clarity, see Horizontal diffusion.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The parameterizations for the tendencies of uvTq from physical processes are denoted as mathcalP_mathbfu = (mathcalP_u mathcalP_v) mathcalP_T mathcalP_q and are further described in the corresponding sections, see Parameterizations.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"SpeedyWeather.jl implements a PrimitiveWet and a PrimitiveDry dynamical core. For a dry atmosphere, we have q = 0 and the virtual temperature T_v = T equals the temperature (often called absolute to distinguish from the virtual temperature). The terms in the primitive equations and their discretizations are discussed in the following sections. ","category":"page"},{"location":"primitiveequation/#Virtual-temperature","page":"Primitive equation model","title":"Virtual temperature","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"info: In short: Virtual temperature\nVirtual temperature is the temperature dry air would need to have to be as light as moist air. It is used in the dynamical core to include the effect of humidity on the density while replacing density through the ideal gas law with temperature.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"We assume the atmosphere to be composed of two ideal gases: Dry air and water vapour. Given a specific humidity q both gases mix, their pressures p_d, p_w (d for dry, w for water vapour), and densities rho_d rho_w add in a given air parcel that has temperature T. The ideal gas law then holds for both gases","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"beginaligned\np_d = rho_d R_d T \np_w = rho_w R_w T \nendaligned","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"with the respective specific gas constants R_d = Rm_d and R_w = Rm_w obtained from the universal gas constant R divided by the molecular masses of the gas. The total pressure p in the air parcel is","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"p = p_d + p_w = (rho_d R_d + rho_w R_w)T","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"We ultimately want to replace the density rho = rho_w + rho_d in the dynamical core, using the ideal gas law, with the temperature T, so that we never have to calculate the density explicitly. However, in order to not deal with two densities (dry air and water vapour) we would like to replace temperature with a virtual temperature that includes the effect of humidity on the density. So, wherever we use the ideal gas law to replace density with temperature, we would use the virtual temperature, which is a function of the absolute temperature and specific humidity, instead. A higher specific humidity in an air parcel lowers the density as water vapour is lighter than dry air. Consequently, the virtual temperature of moist air is higher than its absolute temperature because warmer air is lighter too at constant pressure. We therefore think of the virtual temperature as the temperature dry air would need to have to be as light as moist air.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Starting with the last equation, with some manipulation we can write the ideal gas law as total density rho times a gas constant times the virtual temperature that is supposed to be a function of absolute temperature, humidity and some constants","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"p = (rho R_d + rho_w (R_w - R_d)) T = rho R_d (1 +\nfrac1 - tfracR_dR_wtfracR_dR_w fracrho_wrho_w + rho_d)T","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Now we identify","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"mu = frac1 - tfracR_dR_wtfracR_dR_w","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"as some constant that is positive for water vapour being lighter than dry air (tfracR_dR_w = tfracm_wm_d 1) and","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"q = fracrho_wrho_w + rho_d","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"as the specific humidity. Given temperature T and specific humidity q, we can therefore calculate the virtual temperature T_v as","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"T_v = (1 + mu q)T","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"For completeness we want to mention here that the above product, because it is a product of two variables qT has to be computed in grid-point space, see Spherical Harmonic Transform. To obtain an approximation to the virtual temperature in spectral space without expensive transforms one can linearize","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"T_v approx T + mu qbarT","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"with a global constant temperature barT, for example obtained from the l=m=0 mode, barT = T_00frac1sqrt4pi but depending on the normalization of the spherical harmonics that factor needs adjustment. We call this the linear virtual temperature which is used for the geopotential calculation, see #254.","category":"page"},{"location":"primitiveequation/#Vertical-coordinates","page":"Primitive equation model","title":"Vertical coordinates","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"We start with some general considerations that apply when changing the vertical coordinate from height z to something else. Let Psi(xyzt) be some variable that depends on space and time. Now we want to express Psi using some other coordinate eta in the vertical. Regardless of the coordinate system the value of Psi at the to z corresponding eta (and vice versa) has to be the same as we only want to change the coordinate, not Psi itself.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Psi(xyetat) = Psi(xyz(xyetat)t)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"So you can think of z as a function of eta and eta as a function of z. The chain rule lets us differentiate Psi with respect to z or eta","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial Psipartial z = fracpartial Psipartial etafracpartial etapartial z\nqquad fracpartial Psipartial eta = fracpartial Psipartial zfracpartial zpartial eta","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"But for derivatives with respect to xyt we have to apply the multi-variable chain-rule as both Psi and eta depend on it. So a derivative with respect to x on eta levels (where eta constant) becomes","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"left fracpartial Psipartial xrightvert_eta = \nleft fracpartial Psipartial xrightvert_z +\nfracpartial Psipartial z\nleft fracpartial zpartial xrightvert_eta","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"So we first take the derivative of Psi with respect to x, but then also have to account for the fact that, at a given eta, z depends on x which is again dealt with using the univariate chain rule from above. We will make use of that for the Pressure gradient.","category":"page"},{"location":"primitiveequation/#Sigma-coordinates","page":"Primitive equation model","title":"Sigma coordinates","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The problem with pure pressure coordinates is that they are not terrain-following. For example, the 1000 hPa level in the Earth's atmosphere cuts through mountains. A flow field on such a level is therefore not continuous and one would need to deal with boundaries. Especially with spherical harmonics we need a terrain-following vertical coordinate to transform between continuous fields in grid-point space and spectral space.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"SpeedyWeather.jl currently uses so-called sigma coordinates for the vertical. This coordinate system uses fraction of surface pressure in the vertical, i.e.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"sigma = fracpp_s","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"with sigma = 01 and sigma = 0 being the top (zero pressure) and sigma = 1 the surface (at surface pressure). As a consequence the vertical dimension is also indexed from top to surface.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"info: Vertical indexing\nPressure, sigma, or hybrid coordinates in the vertical range from lowest values at the top to highest values at the surface. Consistently, we also index the vertical dimension top to surface. This means that k=1 is the top-most layer, and k=N_lev (or similar) is the layer that sits directly above the surface.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Sigma coordinates are therefore terrain-following, as sigma = 1 is always at surface pressure and so this level bends itself around every mountain, although the actual pressure on this level can vary. For a visualisation see #329.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"One chooses sigma levels associated with the k-th layer and the pressure can be reobtained from the surface pressure p_s","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"p_k = sigma_kp_s","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The layer thickness in terms of pressure is","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Delta p_k = p_k+tfrac12 - p_k-tfrac12 =\n(sigma_k+tfrac12 - sigma_k-tfrac12) p_s = Delta sigma_k p_s","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"which can also be expressed with the layer thickness in sigma coordinates Delta sigma_k times the surface pressure. In SpeedyWeather.jl one chooses the half levels sigma_k+tfrac12 first and then obtains the full levels through averaging","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"sigma_k = fracsigma_k+tfrac12 + sigma_k-tfrac122","category":"page"},{"location":"primitiveequation/#Geopotential","page":"Primitive equation model","title":"Geopotential","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"In the hydrostatic approximation the vertical momentum equation becomes","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial ppartial z = -rho g","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"meaning that the (negative) vertical pressure gradient is given by the density in that layer times the gravitational acceleration. The heavier the fluid the more the pressure will increase below. Inserting the ideal gas law","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial gzpartial p = -fracR_dT_vp","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"with the geopotential Phi = gz we can write this in terms of the logarithm of pressure","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial Phipartial ln p = -R_dT_v","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Note that we use the Virtual temperature here as we replaced the density through the ideal gas law with temperature. Given a vertical temperature profile T_v and the (constant) surface geopotential Phi_s = gz_s where z_s is the orography, we can integrate this equation from the surface to the top to obtain Phi_k on every layer k. The surface is at k = N+tfrac12 (see Vertical coordinates) with N vertical levels. We can integrate the geopotential onto half levels as (T_k^v is the virtual temperature at layer k, the subscript v has been moved to be a superscript)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Phi_k-tfrac12 = Phi_k+tfrac12 + R_dT^v_k(ln p_k+12 - ln p_k-12)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"or onto full levels with","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Phi_k = Phi_k+tfrac12 + R_dT^v_k(ln p_k+12 - ln p_k)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"We use this last formula first to get from Phi_s to Phi_N, and then for every k twice to get from Phi_k to Phi_k-1 via Phi_k-tfrac12. For the first half-level integration we use T_k for the second T_k-1.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"warning: Semi-implicit time integration: Geopotential\nWith the semi-implicit time integration in SpeedyWeather the Geopotential is not calculated from the spectral temperature at the current, but at the previous time step. This is because this is a linear term that we solve implicitly to avoid instabilities from gravity waves. For details see section Semi-implicit time stepping.","category":"page"},{"location":"primitiveequation/#Surface-pressure-tendency","page":"Primitive equation model","title":"Surface pressure tendency","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The surface pressure increases with a convergence of the flow above. Written in terms of the surface pressure directly, and not its logarithm","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial p_spartial t = -nabla cdot int_0^p_s mathbfudp","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"For k discrete layers from 1 at the top to N at the surface layer this can be written as","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial p_spartial t = - sum_k=1^N nabla cdot (mathbfu_k Delta p_k)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"which can be thought of as a vertical integration of the pressure thickness-weighted divergence. In sigma-coordinates with Delta p_k = Delta sigma_k p_s (see Vertical coordinates) this becomes","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial p_spartial t = - sum_k=1^N sigma_k nabla cdot (mathbfu_k p_s)\n= -sum_k=1^N sigma_k (mathbfu_k cdot nabla p_s + p_s nabla cdot mathbfu_k)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Using the logarithm of pressure ln p as the vertical coordinate this becomes","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial ln p_spartial t = \n-sum_k=1^N sigma_k (mathbfu_k cdot nabla ln p_s + nabla cdot mathbfu_k)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The second term is the divergence mathcalD_k at layer k. We introduce bara = sum_k Delta sigma_k a_k, the sigma-weighted vertical integration operator applied to some variable a. This is essentially an average as sum_k Delta sigma_k = 1. The surface pressure tendency can then be written as","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial ln p_spartial t = \n-mathbfbaru cdot nabla ln p_s - barmathcalD","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"which is form used by SpeedyWeather.jl to calculate the tendency of (the logarithm of) surface pressure.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"As we will have ln p_s available in spectral space at the beginning of a time step, the gradient can be easily computed (see Derivatives in spherical coordinates). However, we then need to transform both gradients to grid-point space for the scalar product with the (vertically sigma-averaged) velocity vector mathbfbaru before transforming it back to spectral space where the tendency is needed. In general, we can do the sigma-weighted average in spectral or in grid-point space, although it is computationally cheaper in spectral space. We therefore compute - barmathcalD entirely in spectral space. With () denoting spectral space and grid-point space (hence, () and () are the transforms in the respective directions) we therefore do","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"left(fracpartial ln p_spartial tright) = \nleft(-mathbfoverlineu cdot nabla (ln p_s)right) - overline(mathcalD)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"But note that it would also be possible to do","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"left(fracpartial ln p_spartial tright) = \nleft(-mathbfoverlineu cdot nabla (ln p_s) - overlinemathcalDright)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Meaning that we would compute the vertical average in grid-point space, subtract from the pressure gradient flux before transforming to spectral space. The same amount of transforms are performed but in the latter, the vertical averaging is done in grid-point space.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"warning: Semi-implicit time integration: Surface pressure tendency\nWith the semi-implicit time integration in SpeedyWeather the - overline(mathcalD) term is not evaluated from the spectral divergence mathcalD at the current, but at the previous time step. This is because this is a linear term that we solve implicitly to avoid instabilities from gravity waves. For details see section Semi-implicit time stepping.","category":"page"},{"location":"primitiveequation/#Vertical-advection","page":"Primitive equation model","title":"Vertical advection","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The advection equation tfracDTDt = 0 for a tracer T is, in flux form, for layer k","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial (T_k Delta p_k)partial t = - nabla cdot (mathbfu_k T_k Delta p_k)\n- (M_k+tfrac12T_k+tfrac12 - M_k-tfrac12T_k-tfrac12)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"which can be through the gradient product rule, and using the conservation of mass (see Vertical velocity) transformed into an advective form. In sigma coordinates this simplifies to","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial T_kpartial t = - mathbfu_k cdot nabla T_k\n- frac1Delta sigma_kleft(dotsigma_k+tfrac12(T_k+tfrac12 - T_k) - dotsigma_k-tfrac12(T_k - T_k-tfrac12)right)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"With the reconstruction at the faces, T_k+tfrac12, and T_k-tfrac12 depending on one's choice of the advection scheme. For a second order centered scheme, we choose T_k+tfrac12 = tfrac12(T_k + T_k+1) and obtain","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial T_kpartial t = - mathbfu_k cdot nabla T_k\n- frac12Delta sigma_kleft(dotsigma_k+tfrac12(T_k+1 - T_k) + dotsigma_k-tfrac12(T_k - T_k-1)right)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"However, note that this scheme is dispersive and easily leads to instabilities at higher resolution, where a more advanced vertical advection scheme becomes necessary. For convenience, we may write W(T) to denote the vertical advection term dotsigmapartial_sigma T, without specifying which schemes is used. The vertical velocity dotsigma is calculated as described in the following.","category":"page"},{"location":"primitiveequation/#Vertical-velocity","page":"Primitive equation model","title":"Vertical velocity","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"In the section Surface pressure tendency we used that the surface pressure changes with the convergence of the flow above, which derives from the conservation of mass. Similarly, the conservation of mass for layer k can be expressed as (setting T=1 in the advection equation in section Vertical advection)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracpartial Delta p_kpartial t = -nabla cdot (mathbfu_k Delta p_k)\n- (M_k+tfrac12 - M_k-tfrac12)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Meaning that the pressure thickness Delta p_k of layer k changes with a horizontal divergence -nabla cdot (mathbfu_k Delta p_k) if not balanced by a net vertical mass flux M into of the layer through the bottom and top boundaries of k at kpmtfrac12. M is defined positive downward as this is the direction in which both pressure and sigma coordinates increase. The boundary conditions are M_tfrac12 = M_N+tfrac12 = 0, such that there is no mass flux into the top layer from above or out of the surface layer N and into the ground or ocean.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"When integrating from the top down to layer k we obtain the mass flux downwards out of layer k","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"M_k+tfrac12 = - sum_r=1^k nabla cdot (mathbfu_k Delta p_k) - fracpartial p_k+tfrac12partial t","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"In sigma coordinates we have M_k+tfrac12 = p_s dotsigma_k+tfrac12 with dotsigma being the vertical velocity in sigma coordinates, also defined at interfaces between layers. To calculate dotsigma we therefore compute","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"dotsigma_k+tfrac12 = fracM_k+tfrac12p_s = \n- sum_r=1^k Delta sigma_r (mathbfu_k cdot nabla ln p_s + mathcalD_r) \n+ sigma_k+tfrac12(-mathbfbaru cdot nabla ln p_s - barmathcalD)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"With barA denoting a sigma thickness-weighted vertical average as in section Surface pressure tendency. Now let barA_k be that average from r=1 to r=k only and not necessarily down to the surface, as required in the equation above, then we can also write","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"dotsigma_k+tfrac12 = \n- overlinemathbfu_k cdot nabla ln p_s - barmathcalD_k\n+ sigma_k+tfrac12(-mathbfbaru cdot nabla ln p_s - barmathcalD)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"See also Hoskins and Simmons, 1975[HS75]. These vertical averages are the same as required by the Surface pressure tendency and in the Temperature equation, they are therefore all calculated at once, storing the partial averages overlinemathbfu_k cdot nabla ln p_s and barmathcalD_k on the fly.","category":"page"},{"location":"primitiveequation/#Pressure-gradient","page":"Primitive equation model","title":"Pressure gradient","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The pressure gradient term in the primitive equations is","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"-frac1rhonabla_z p","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"with density rho and pressure p. The gradient here is taken at constant z hence the subscript. If we move to a pressure-based vertical coordinate system we will need to evaluate gradients on constant levels of pressure though, i.e. nabla_p. There is, by definition, no gradient of pressure on constant levels of pressure, but we can use the chain rule (see Vertical coordinates) to rewrite this as (use only x but y is equivalent)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"0 = left fracpartial ppartial x rightvert_p =\nleft fracpartial ppartial x rightvert_z +\nfracpartial ppartial zleft fracpartial zpartial x rightvert_p","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Using the hydrostatic equation partial_z p = -rho g this becomes","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"left fracpartial ppartial x rightvert_z = rho g left fracpartial zpartial x rightvert_p","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Or, in terms of the geopotential Phi = gz","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"frac1rhonabla_z p = nabla_p Phi","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"which is the actual reason why we use pressure coordinates: As density rho also depends on the pressure p the left-hand side means an implicit system when solving for pressure p. To go from pressure to sigma coordinates we apply the chain rule from section Vertical coordinates again and obtain","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"nabla_p Phi = nabla_sigma Phi - fracpartial Phipartial pnabla_sigma p\n= nabla_sigma Phi + frac1rhonabla_sigma p","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"where the last step inserts the hydrostatic equation again. With the ideal gas law, and note that we use Virtual temperature T_v everywhere where the ideal gas law is used, but in combination with the dry gas constant R_d","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"nabla_p Phi = nabla_sigma Phi + fracR_dT_vp nabla_sigma p","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Combining the pressure in denominator and gradient to the logarithm and with nabla ln p = nabla ln p_s in Sigma coordinates (the logarithm of sigma_k adds a constant that drops out in the gradient) we therefore have","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"- frac1rhonabla_z p = -nabla_p Phi = -nabla_sigma Phi - R_dT_v nabla_sigma ln p_s","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"From left to right: The pressure gradient force in z-coordinates; in pressure coordinates; and in sigma coordinates. Each denoted with the respective subscript on gradients. SpeedyWeather.jl uses the latter. In sigma coordinates we may drop the sigma subscript on gradients, but still meaning that the gradient is evaluated on a surface of our vertical coordinate. In vorticity-divergence formulation of the momentum equations the nabla_sigma Phi drops out in the vorticity equation (nabla times nabla Phi = 0), but becomes a -nabla^2 Phi in the divergence equation, which is therefore combined with the kinetic energy term -nabla^2(tfrac12(u^2 + v^2)) similar as it is done in the Shallow water equations. You can think of tfrac12(u^2 + v^2) + Phi as the Bernoulli potential in the primitive equations. However, due to the change into sigma coordinates the surface pressure gradient also has to be accounted for. Now highlighting only the pressure gradient force, we have in total","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"beginaligned\nfracpartial zetapartial t = nabla times ( - R_dT_vnabla ln p_s) + \nfracpartial mathcalDpartial t = nabla cdot ( - R_dT_vnabla ln p_s) - nabla^2Phi + \nendaligned","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"In our vorticity-divergence formulation and with sigma coordinates.","category":"page"},{"location":"primitiveequation/#Semi-implicit-pressure-gradient","page":"Primitive equation model","title":"Semi-implicit pressure gradient","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"With the semi-implicit time integration in SpeedyWeather.jl the pressure gradient terms are further modified as follows. See that section for details why, but here is just to mention that we need to split the terms into linear and non-linear terms. The linear terms are then evaluated at the previous time step for the implicit scheme such that we can avoid instabilities from gravity waves.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"We split the (virtual) temperature into a reference vertical profile T_k and its anomaly, T_v = T_k + T_v. The reference profile T_k has to be a global constant for the spectral transform but can depend on the vertical. With this, the previous equation becomes","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"beginaligned\nfracpartial zetapartial t = nabla times ( - R_dT_vnabla ln p_s) + \nfracpartial mathcalDpartial t = nabla cdot ( - R_dT_vnabla ln p_s) - nabla^2(Phi + R_d T_k ln p_s) + \nendaligned","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"In the vorticity equation the term with the reference profile drops out as nabla times nabla = 0, and in the divergence equation we move it into the Laplace operator. Now the linear terms are gathered with the Laplace operator and for the semi-implicit scheme we calculate both the Geopotential Phi and the contribution to the \"linear pressure gradient\" R_dT_k ln p_s at the previous time step for the semi-implicit time integration for details see therein.","category":"page"},{"location":"primitiveequation/#Vorticity-advection","page":"Primitive equation model","title":"Vorticity advection","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Vorticity advection in the primitive equation takes the form","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"beginaligned\nfracpartial upartial t = (f+zeta)v \nfracpartial vpartial t = -(f+zeta)u \nendaligned","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Meaning that we add the Coriolis parameter f and the relative vorticity zeta and multiply by the respective velocity component. While the primitive equations here are written with vorticity and divergence, we use uv here as other tendencies will be added and the curl and divergence are only taken once after transform into spectral space. To obtain a tendency for vorticity and divergence, we rewrite this as","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"beginaligned\nfracpartial zetapartial t = nabla times (f+zeta)mathbfu_perp \nfracpartial mathcalDpartial t = nabla cdot (f+zeta)mathbfu_perp \nendaligned","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"with mathbfu_perp = (v-u) the rotated velocity vector, see Barotropic vorticity equation.","category":"page"},{"location":"primitiveequation/#Humidity-equation","page":"Primitive equation model","title":"Humidity equation","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The dynamical core treats humidity as an (active) tracer, meaning that after the physical parameterizations for humidity mathcalP are calculated in grid-point space, humidity is only advected with the flow. The only exception is the Virtual temperature as high levels of humidity will lower the effective density, which is why we use the virtual instead of the absolute temperature. The equation to be solved for humidity is therefore,","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"left( fracpartial qpartial t right) = left(leftmathcalP_q - W_q +\nqmathcalD rightright) -nablacdot(mathbfuq)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"With () denoting spectral space and grid-point space, so that () and () are the transforms in the respective directions. To avoid confusion with that notation, we write the tendency of humidity due to Vertical advection as W_q. This equation is identical to a tracer equation, with mathcalP_q denoting sources and sinks. Note that Horizontal diffusion should be applied to every advected variable.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"A very similar equation is solved for (absolute) temperature as described in the following.","category":"page"},{"location":"primitiveequation/#Temperature-equation","page":"Primitive equation model","title":"Temperature equation","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The first law of thermodynamic states that the internal energy I is increased by the heat Q applied minus the work W done by the system. We neglect changes in chemical composition ([Vallis], chapter 1.5). For an ideal gas, the internal energy is c_vT with c_v the heat capacity at constant volume and temperature T. The work done is pV, with pressure p and the specific volume V","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"dI = Q - p dV","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"For fluids we replace the differential d here with the material derivative tfracDDt. With V = tfrac1rho and density rho we then have","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"c_v fracDTDt = -p fracD (1rho)Dt + Q","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Using the ideal gas law to replace tfrac1rho with tfracRT_vp (we are using the Virtual temperature again), and using","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"pfracD (1p)Dt = -frac1p fracDpDt","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"we have","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"(c_v + R)fracDTDt = fracRT_vpfracDpDt + Q","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"And further, with c_p = c_v + R the heat capacity at constant pressure, kappa = tfracRc_p, and using the logarithm of pressure","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"fracDTDt = kappa T_vfracD ln pDt + fracQc_p","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"This is the form of the temperature equation that SpeedyWeather.jl uses. Temperature is advected through the material derivative and first term on the right-hand side represents an adiabatic conversion term describing how the temperature changes with changes in pressure. Recall that this term originated from the work term in the first law of thermodynamics. The forcing term tfracQc_p is here identified as the physical parameterizations changing the temperature, for example radiation, and hence we will call it P_T.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Similar to the Humidity equation we write the equation for (absolute) temperature T as","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"left( fracpartial Tpartial t right) = left(leftmathcalP_T - W_T +\nTmathcalD + kappa T_v fracD ln pDt rightright) -nablacdot(mathbfuT)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"W_T is the Vertical advection of temperature. We evaluate the adiabatic conversion term completely in grid-point space following Simmons and Burridge, 1981[SB81] Equation 3.12 and 3.13. Leaving out the kappa T_v for clarity, the term at level k is","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"left(fracD ln pD tright)_k = mathbfu_k cdot nabla ln p_k\n- frac1Delta p_k leftleft( ln fracp_k+tfrac12p_k-tfrac12right)\nsum_r=1^k-1nabla cdot (mathbfu_k Delta p_k) + alpha_k nabla cdot (mathbfu_k Delta p_k) right","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"with","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"alpha_k = 1 - fracp_k-tfrac12Delta p_k ln fracp_k+tfrac12p_k-tfrac12","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"In sigma coordinates this simplifies to, following similar steps as in Surface pressure tendency","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"beginaligned\nleft(fracD ln pD tright)_k = mathbfu_k cdot nabla ln p_s \n- frac1Delta sigma_k left( ln fracsigma_k+tfrac12sigma_k-tfrac12right)\nsum_r=1^k-1Delta sigma_r (mathcalD_r + mathbfu_r cdot nabla ln p_s) -\nalpha_k (mathcalD_k + mathbfu_k cdot nabla ln p_s)\nendaligned","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Let A_k = mathcalD_k + mathbfu_k cdot nabla ln p_s and beta_k = tfrac1Delta sigma_k left( ln tfracsigma_k+tfrac12sigma_k-tfrac12right), then this can also be summarised as","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"left(fracD ln pD tright)_k = mathbfu_k cdot nabla ln p_s\n- beta_k sum_r=1^k-1Delta sigma_r A_r - alpha_k A_k","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The alpha_k beta_k are constants and can be precomputed. The surface pressure flux mathbfu_k cdot nabla ln p_s has to be computed, so does the vertical sigma-weighted average from top to k-1, which is done when computing other vertical averages for the Surface pressure tendency.","category":"page"},{"location":"primitiveequation/#Semi-implicit-temperature-equation","page":"Primitive equation model","title":"Semi-implicit temperature equation","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"For the semi-implicit scheme we need to split the temperature equation into linear and non-linear terms, as the linear terms need to be evaluated at the previous time step. Decomposing temperature T into T = T_k + T with the reference profile T_k and its anomaly T, the temperature equation becomes","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"left( fracpartial Tpartial t right) = mathcalP_T - W_T +\nTmathcalD + kappa T_v fracD ln pDt -nablacdot(mathbfuT)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Note that we do not change the adiabatic conversion term. While its linear component kappa T_k^v tfracD ln p_sD t (the subscript v for Virtual temperature as been raised) would need to be evaluated at the previous time step, we still evaluate this term at the current time step and move it within the semi-implicit corrections to the previous time step afterwards.","category":"page"},{"location":"primitiveequation/#implicit_primitive","page":"Primitive equation model","title":"Semi-implicit time stepping","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Conceptually, the semi-implicit time stepping in the Primitive equation model is the same as in the Shallow water model, but","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"tendencies for divergence mathcalD, logarithm of surface pressure ln p_s but also temperature T are computed semi-implicitly,\nthe vertical layers are coupled, creating a linear equation system that is solved via matrix inversion.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The linear terms of the primitive equations follow a linearization around a state of rest without orography and a reference vertical temperature profile. The scheme described here largely follows Hoskins and Simmons [HS75], which has also been used in Simmons and Burridge [SB81].","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"As before, let delta V = tfracV_i+1 - V_i-12Delta t be the tendency we need for the Leapfrog time stepping. With the implicit time step xi = 2alphaDelta t, alpha in tfrac121 we have","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"delta V = N_E(V_i) + N_I(V_i-1) + xi N_I(delta V)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"with N_E being the explicitly-treated non-linear terms and N_I the implicitly-treated linear terms, such that N_I is a linear operator. We can therefore solve for delta V by inverting N_I, ","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"delta V = (1-xi N_I)^-1G","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"where we gathered the uncorrected right-hand side as G","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"G = N_E(V_i) + N_I(V_i-1) = N(V_i) + N_I(V_i-1 - V_i)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"So for every linear term in N_I we have two options corresponding to two sides of this equation","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Evaluate it at the previous time step i-1\nOr, evaluate it at the current time step i as N(V_i), but then move it back to the previous time step i-1 by adding (in spectral space) the linear operator N_I evaluated with the difference between the two time steps.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"If there is a tendency that is easily evaluated in spectral space it is easier to follow 1. However, a term that is costly to evaluate in grid-point space should usually follow the latter. The reason is that the previous time step is generally not available in grid-point space (unless recalculated through a costly transform or stored with additional memory requirements) so it is easier to follow 2 where the N_I is available in spectral space. For the adiabatic conversion term in the Temperature equation we follow 2 as one would otherwise need to split this term into a non-linear and linear term, evaluating it essentially twice in grid-point space. ","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"So what is G in the Primitive equation model? ","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"beginaligned\nG_mathcalD = N^E_mathcalD - nabla^2(Phi^i-1 + R_dT_k^v (ln p_s)^i-1)\n= N^E_mathcalD - nabla^2( mathbfRT^i-1 + mathbfUln p_s^i-1) \nG_ln p_s = N_ln p_s^E - overlinemathcalD^i-1\n= N_ln p_s^E + mathbfWmathcalD^i-1 \nG_T = N_T + mathbfL(mathcalD^i-1 - mathcalD^i) \nendaligned","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"G is for the divergence, pressure and temperature equation the \"uncorrected\" tendency. Moving time step i - 1 to i we would be back with a fully explicit scheme. In the divergence equation the Geopotential Phi is calculated from temperature T at the previous time step i-1 (denoted as superscript) and the \"linear\" Pressure gradient from the logarithm of surface pressure at the previous time step. One can think of these two calculations as linear operators, mathbfR and mathbfU. We will shortly discuss their properties. While we could combine them with the Laplace operator nabla^2 (which is also linear) we do not do this as mathbfR U do not depend on the degree and order of the spherical harmonics (their wavenumber) but on the vertical, but nabla^2 does not depend on the vertical, only on the wavenumber. All other terms are gathered in N_mathcalD^E (subscript E has been raised) and calculated as described in the respective section at the current time step i.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"For the pressure tendency, the subtraction with the thickness-weighted vertical average barmathcalD is the linear term that is treated implicitly. We call this operator mathbfW. For the temperature tendency, we evaluate all terms explicitly at the current time step in N_T but then move the linear term in the adiabatic conversion term with the operator mathbfL back to the previous time step. For details see Semi-implicit temperature equation.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The operators mathbfR U L W are all linear, meaning that we can apply them in spectral space to each spherical harmonic independently – the vertical is coupled however. With N being the number of vertical levels and the prognostic variables like temperature for a given degree l and order m being a column vector in the vertical, T_lm in mathbbR^N, these operators have the following shapes","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"beginaligned\nmathbfR in mathbbR^Ntimes N \nmathbfU in mathbbR^Ntimes 1 \nmathbfL in mathbbR^Ntimes N \nmathbfW in mathbbR^1times N \nendaligned","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"mathbfR is an integration in the vertical hence it is an upper triangular matrix such that the first (an top-most) k=1 element of the resulting vector depends on all vertical levels of the temperature mode T_lm, but the surface k=N only on the temperature mode at the surface. mathbfU takes the surface value of the lm mode of the logarithm of surface pressure (ln p_s)_lm and multiplies it element-wise with the reference temperature profile and the dry gas constant. So the result is a column vector. mathbfL is an N times N matrix as the adiabatic conversion term couples all layers. mathbfW is a row vector as it represents the vertical averaging of the spherical harmonics of a divergence profile. So, mathbfWmathcalD is a scalar product for every lm giving a contribution of all vertical layers in divergence to the (single-layer!) logarithm of surface pressure tendency.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"With the Gs defined we can now write the semi-implicit tendencies delta mathcalD, delta T, delta ln p_s as (first equation in this section)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"beginaligned\ndelta mathcalD = G_D - xi nabla^2(mathbfRdelta T + mathbfU delta ln p_s)\ndelta T = G_T + xi mathbfLdelta mathcalD \ndelta ln p_s = G_ln p_s + xi mathbfWdelta mathcalD\nendaligned","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Solving for delta mathcalD with the \"combined\" tendency","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"G = G_D - xi nabla^2(mathbfRG_T + mathbfUG_ln p_s)","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"via","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"delta mathcalD = G - xi^2nabla^2(mathbfRL + UW)delta mathcalD","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"(mathbfUW is a matrix of size N times N) yields","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"delta D = left( 1 + xi^2nabla^2(mathbfRL + UW) right)^-1G = mathbfS^-1G","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The other tendencies delta T and delta ln p_s are then obtained through insertion above. We may call the operator to be inverted mathbfS which is of size l_max times N times N, hence for every degree l of the spherical harmonics (which the Laplace operator depends on) a N times N matrix coupling the N vertical levels. Furthermore, S depends","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"through xi on the time step Delta t,\nthrough mathbfRWL on the vertical level spacing Delta sigma_k\nthrough mathbfU on the reference temperature profile T_k","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"so for any changes of these the matrix inversion of mathbfS has to be recomputed. Otherwise the algorithm for the semi-implicit scheme is as follows","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"0. Precompute the linear operators mathbfRULW and with them the matrix inversion mathbfS^-1.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Then for every time step","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Compute the uncorrected tendencies evaluated at the current time step for the explicit terms and the previous time step for the implicit terms.\nException in SpeedyWeather.jl is the adiabatic conversion term, which is, using mathbfL moved afterwards from the current i to the previous time step i-1.\nCompute the combined tendency G from the uncorrected tendencies G_mathcalD, G_T, G_ln p_s.\nWith the inverted operator get the corrected tendency for divergence, delta mathcalD = mathbfS^-1G.\nObtain the corrected tendencies for temperature delta T and surface pressure delta ln p_s from delta mathcalD.\nApply Horizontal diffusion (which is only mentioned here as it further updates the tendencies).\nUse delta mathcalD, delta T and delta ln p_s in the Leapfrog time integration.","category":"page"},{"location":"primitiveequation/#Horizontal-diffusion","page":"Primitive equation model","title":"Horizontal diffusion","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Horizontal diffusion in the primitive equations is applied to vorticity zeta, divergence mathcalD, temperature T and humidity q. In short, all variables that are advected. For the dry equations, q=0 and no diffusion has to be applied.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The horizontal diffusion is applied implicitly in spectral space, as already described in Horizontal diffusion for the barotropic vorticity equation.","category":"page"},{"location":"primitiveequation/#Algorithm","page":"Primitive equation model","title":"Algorithm","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"The following algorithm describes a time step of the PrimitiveWetModel, for the PrimitiveDryModel humidity can be set to zero and respective steps skipped.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"0. Start with initial conditions of relative vorticity zeta_lm, divergence D_lm, temperature T_lm, humidity q_lm and the logarithm of surface pressure (ln p_s)_lm in spectral space. Variables zeta D T q are defined on all vertical levels, the logarithm of surface pressure only at the surface. Transform this model state to grid-point space, obtaining velocities is done as in the shallow water model","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Invert the Laplacian of zeta_lm to obtain the stream function Psi_lm in spectral space\nInvert the Laplacian of D_lm to obtain the velocity potential Phi_lm in spectral space\nobtain velocities U_lm = (cos(theta)u)_lm V_lm = (cos(theta)v)_lm from nabla^perpPsi_lm + nablaPhi_lm\nTransform velocities U_lm, V_lm to grid-point space UV\nUnscale the cos(theta) factor to obtain uv","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Additionally we","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Transform zeta_lm, D_lm, T_lm (ln p_s)_lm to zeta D eta T ln p_s in grid-point space\nCompute the (non-linearized) Virtual temperature in grid-point space.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Now loop over","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"Compute all tendencies of u v T q due to physical parameterizations in grid-point space.\nCompute the gradient of the logarithm of surface pressure nabla (ln p_s)_lm in spectral space and convert the two fields to grid-point space. Unscale the cos(theta) on the fly.\nFor every layer k compute the pressure flux mathbfu_k cdot nabla ln p_s in grid-point space. \nFor every layer k compute a linearized Virtual temperature in spectral space.\nFor every layer k compute a temperature anomaly (virtual and absolute) relative to a vertical reference profile T_k in grid-point space.\nCompute the Geopotential Phi by integrating the virtual temperature vertically in spectral space from surface to top.\nIntegrate uvD vertically to obtain barubarvbarD in grid-point space and also barD_lm in spectral space. Store on the fly also for every layer k the partial integration from 1 to k-1 (top to layer above). These will be used in the adiabatic term of the Temperature equation.\nCompute the Surface pressure tendency with the vertical averages from the previous step. For the semi-implicit time stepping\nFor every layer k compute the Vertical velocity.\nFor every layer k add the linear contribution of the Pressure gradient RT_k (ln p_s)_lm to the geopotential Phi in spectral space.\nFor every layer k compute the Vertical advection for uvTq and add it to the respective tendency.\nFor every layer k compute the tendency of uv due to Vorticity advection and the Pressure gradient RT_v nabla ln p_s and add to the respective existing tendency. Unscale cos(theta), transform to spectral space, take curl and divergence to obtain tendencies for zeta_lmmathcalD_lm.\nFor every layer k compute the adiabatic term and the horizontal advection in the Temperature equation in grid-point space, add to existing tendency and transform to spectral.\nFor every layer k compute the horizontal advection of humidity q in the Humidity equation in grid-point space, add to existing tendency and transform to spectral.\nFor every layer k compute the kinetic energy tfrac12(u^2 + v^2), transform to spectral and add to the Geopotential. For the semi-implicit time stepping also add the linear pressure gradient calculated from the previous time step. Now apply the Laplace operator and subtract from the divergence tendency.\nCorrect the tendencies following the semi-implicit time integration to prevent fast gravity waves from causing numerical instabilities.\nCompute the horizontal diffusion for the advected variables zetamathcalDTq\nCompute a leapfrog time step as described in Time integration with a Robert-Asselin and Williams filter\nTransform the new spectral state of zeta_lm, mathcalD_lm, T_lm, q_lm and (ln p_s)_lm to grid-point uvzetamathcalDTqln p_s as described in 0.\nPossibly do some output\nRepeat from 1.","category":"page"},{"location":"primitiveequation/#Scaled-primitive-equations","page":"Primitive equation model","title":"Scaled primitive equations","text":"","category":"section"},{"location":"primitiveequation/#References","page":"Primitive equation model","title":"References","text":"","category":"section"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"[GFDL1]: Geophysical Fluid Dynamics Laboratory, Idealized models with spectral dynamics","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"[GFDL2]: Geophysical Fluid Dynamics Laboratory, The Spectral Dynamical Core","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"[Vallis]: GK Vallis, 2006. Atmopsheric and Ocean Fluid Dynamics, Cambridge University Press.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"[SB81]: Simmons and Burridge, 1981. An Energy and Angular-Momentum Conserving Vertical Finite-Difference Scheme and Hybrid Vertical Coordinates, Monthly Weather Review. DOI: 10.1175/1520-0493(1981)109<0758:AEAAMC>2.0.CO;2.","category":"page"},{"location":"primitiveequation/","page":"Primitive equation model","title":"Primitive equation model","text":"[HS75]: Hoskins and Simmons, 1975. A multi-layer spectral model and the semi-implicit method, Quart. J. R. Met. Soc. DOI: 10.1002/qj.49710142918","category":"page"},{"location":"lowertriangularmatrices/#lowertriangularmatrices","page":"Submodule: LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"LowerTriangularMatrices 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.","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"This module defines LowerTriangularMatrix, a lower triangular matrix, which in contrast to LinearAlgebra.LowerTriangular does not store the entries above the diagonal. SpeedyWeather.jl uses LowerTriangularMatrix which is defined as a subtype of AbstractMatrix to store the spherical harmonic coefficients (see Spectral packing). ","category":"page"},{"location":"lowertriangularmatrices/#Creation-of-LowerTriangularMatrix","page":"Submodule: LowerTriangularMatrices","title":"Creation of LowerTriangularMatrix","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"A LowerTriangularMatrix can be created using zeros,ones,rand, or randn","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"julia> using SpeedyWeather.LowerTriangularMatrices\n\njulia> L = rand(LowerTriangularMatrix{Float32},5,5)\n5×5 LowerTriangularMatrix{Float32}:\n 0.912744 0.0 0.0 0.0 0.0\n 0.0737592 0.230592 0.0 0.0 0.0\n 0.799679 0.0765255 0.888098 0.0 0.0\n 0.670835 0.997938 0.505276 0.492966 0.0\n 0.949321 0.193692 0.793623 0.152817 0.357968","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"or the undef initializor LowerTriangularMatrix{Float32}(undef,3,3). The element type is arbitrary though, you can use any type T too.","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"Alternatively, it can be created through conversion from Matrix, which drops the upper triangle entries and sets them to zero","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"julia> M = rand(Float16,3,3)\n3×3 Matrix{Float16}:\n 0.2222 0.694 0.3452\n 0.2158 0.04443 0.274\n 0.9746 0.793 0.6294\n\njulia> LowerTriangularMatrix(M)\n3×3 LowerTriangularMatrix{Float16}:\n 0.2222 0.0 0.0\n 0.2158 0.04443 0.0\n 0.9746 0.793 0.6294","category":"page"},{"location":"lowertriangularmatrices/#Indexing-LowerTriangularMatrix","page":"Submodule: LowerTriangularMatrices","title":"Indexing LowerTriangularMatrix","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"LowerTriangularMatrix supports two types of indexing: 1) by denoting two indices, column and row [l,m] or 2) by denoting a single index [lm]. The double index works as expected","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"julia> L\n3×3 LowerTriangularMatrix{Float16}:\n 0.1499 0.0 0.0\n 0.1177 0.478 0.0\n 0.1709 0.756 0.3223\n\njulia> L[2,2]\nFloat16(0.478)","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"But the single index skips the zero entries in the upper triangle, i.e.","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"julia> L[4]\nFloat16(0.478)","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"which, important, is different from single indices of an AbstractMatrix","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"julia> Matrix(L)[4]\nFloat16(0.0)","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"In performance-critical code a single index should be used, as this directly maps to the index of the underlying data vector. The double index is somewhat slower as it first has to be converted to the corresponding single index.","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"Consequently, many loops in SpeedyWeather.jl are build with the following structure","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"n,m = size(L)\nij = 0\nfor j in 1:m\n for i in j:n\n ij += 1\n L[ij] = i+j\n end\nend","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"which loops over all lower triangle entries of L::LowerTriangularMatrix and the single index ij is simply counted up. However, one could also use [i,j] as indices in the loop body or to perform any calculation (i+j here). An iterator over all entries in the lower triangle can be created by","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"for ij in eachindex(L)\n # do something\nend","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"The setindex! functionality of matrixes will throw a BoundsError when trying to write into the upper triangle of a LowerTriangularMatrix, for example","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"julia> L[2,1] = 0 # valid index\n0\n\njulia> L[1,2] = 0 # invalid index in the upper triangle\nERROR: BoundsError: attempt to access 3×3 LowerTriangularMatrix{Float32} at index [1, 2]","category":"page"},{"location":"lowertriangularmatrices/#Linear-algebra-with-LowerTriangularMatrix","page":"Submodule: LowerTriangularMatrices","title":"Linear algebra with LowerTriangularMatrix","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"The LowerTriangularMatrices module's main purpose is not linear algebra, and it's implementation may not be efficient, however, many operations work as expected","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"julia> L = rand(LowerTriangularMatrix{Float32},3,3)\n3×3 LowerTriangularMatrix{Float32}:\n 0.57649 0.0 0.0\n 0.348685 0.875371 0.0\n 0.881923 0.850552 0.998306\n\njulia> L + L\n3×3 LowerTriangularMatrix{Float32}:\n 1.15298 0.0 0.0\n 0.697371 1.75074 0.0\n 1.76385 1.7011 1.99661\n\njulia> L * L\n3×3 Matrix{Float32}:\n 0.332341 0.0 0.0\n 0.506243 0.766275 0.0\n 1.68542 1.59366 0.996616","category":"page"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"Note, however, that the latter includes a conversion to Matrix, which is true for many operations, including inv or \\. Hence when trying to do more sophisticated linear algebra with LowerTriangularMatrix we quickly leave lower triangular-land and go back to normal matrix-land.","category":"page"},{"location":"lowertriangularmatrices/#Function-and-type-index","page":"Submodule: LowerTriangularMatrices","title":"Function and type index","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"Submodule: LowerTriangularMatrices","title":"Submodule: LowerTriangularMatrices","text":"Modules = [SpeedyWeather.LowerTriangularMatrices]","category":"page"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.LowerTriangularMatrix","page":"Submodule: LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.LowerTriangularMatrix","text":"L = LowerTriangularMatrix{T}(v::Vector{T},m::Int,n::Int)\n\nA lower triangular matrix implementation that only stores the non-zero entries explicitly. L<:AbstractMatrix although in general we have L[i] != Matrix(L)[i], the former skips zero entries, tha latter includes them.\n\n\n\n\n\n","category":"type"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.LowerTriangularMatrix-Union{Tuple{AbstractMatrix{T}}, Tuple{T}} where T","page":"Submodule: LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.LowerTriangularMatrix","text":"L = LowerTriangularMatrix(M)\n\nCreate a LowerTriangularMatrix L from Matrix M by copying over the non-zero elements in M.\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#Base.fill!-Union{Tuple{T}, Tuple{LowerTriangularMatrix{T}, Any}} where T","page":"Submodule: LowerTriangularMatrices","title":"Base.fill!","text":"fill!(L::LowerTriangularMatrix,x)\n\nFills the elements of L with x. Faster than fill!(::AbstractArray,x) as only the non-zero elements in L are assigned with x.\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.eachharmonic-Tuple{LowerTriangularMatrix}","page":"Submodule: LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.eachharmonic","text":"unit_range = eachharmonic(L::LowerTriangular)\n\ncreates unit_range::UnitRange to loop over all non-zeros in a LowerTriangularMatrix L. Like eachindex but skips the upper triangle with zeros in L.\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.eachharmonic-Tuple{Vararg{LowerTriangularMatrix}}","page":"Submodule: LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.eachharmonic","text":"unit_range = eachharmonic(Ls::LowerTriangularMatrix...)\n\ncreates unit_range::UnitRange to loop over all non-zeros in the LowerTriangularMatrices provided as arguments. Checks bounds first. All LowerTriangularMatrix's need to be of the same size. Like eachindex but skips the upper triangle with zeros in L.\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.ij2k-Tuple{Integer, Integer, Integer}","page":"Submodule: LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.ij2k","text":"k = ij2k( i::Integer, # row index of matrix\n j::Integer, # column index of matrix\n m::Integer) # number of rows in matrix\n\nConverts the index pair i,j of an mxn LowerTriangularMatrix L to a single index k that indexes the same element in the corresponding vector that stores only the lower triangle (the non-zero entries) of L.\n\n\n\n\n\n","category":"method"},{"location":"setups/#Model-setups","page":"Model setups","title":"Model setups","text":"","category":"section"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"The following is a collection of model setups, starting with an easy setup of the Barotropic vorticity equation and continuing with more complicated setups.","category":"page"},{"location":"setups/#D-turbulence-on-a-non-rotating-sphere","page":"Model setups","title":"2D turbulence on a non-rotating sphere","text":"","category":"section"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"info: Setup script\nusing SpeedyWeather\nspectral_grid = SpectralGrid(trunc=63,nlev=1)\nstill_earth = Earth(rotation=0)\ninitial_conditions = StartWithRandomVorticity()\nmodel = BarotropicModel(;spectral_grid, initial_conditions, planet=still_earth)\nsimulation = initialize!(model)\nrun!(simulation,n_days=20)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"We want to use the barotropic model to simulate some free-decaying 2D turbulence on the sphere without rotation. We start by defining the SpectralGrid object. To have a resolution of about 200km, we choose a spectral resolution of T63 (see Available horizontal resolutions) and nlev=1 vertical levels. The SpectralGrid object will provide us with some more information","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=63,nlev=1)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"Next step we create a planet that's like Earth but not rotating","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"still_earth = Earth(rotation=0)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"There are other options to create a planet but they are irrelevant for the barotropic vorticity equations. We also want to specify the initial conditions, randomly distributed vorticity is already defined","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"using Random # hide\nRandom.seed!(1234) # hide\ninitial_conditions = StartWithRandomVorticity()","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"By default, the power of vorticity is spectrally distributed with k^-3, k being the horizontal wavenumber, and the amplitude is 10^-5texts^-1.","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"Now we want to construct a BarotropicModel with these","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"model = BarotropicModel(;spectral_grid, initial_conditions, planet=still_earth)\nnothing # hide","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"The model contains all the parameters, but isn't initialized yet, which we can do with and then run it. The run! command will always return the prognostic variables, which, by default, are plotted for surface relative vorticity with a unicode plot. The resolution of the plot is not necessarily representative but it lets us have a quick look at the result","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"simulation = initialize!(model)\nrun!(simulation,n_days=20)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"Woohoo! Something is moving! You could pick up where this simulation stopped by simply doing run!(simulation,n_days=50) again. We didn't store any output, which you can do by run!(simulation,output=true), which will switch on NetCDF output with default settings. More options on output in NetCDF output.","category":"page"},{"location":"setups/#Shallow-water-with-mountains","page":"Model setups","title":"Shallow water with mountains","text":"","category":"section"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"info: Setup script\nusing SpeedyWeather\nspectral_grid = SpectralGrid(trunc=63,nlev=1)\norography = NoOrography(spectral_grid)\ninitial_conditions = ZonalJet()\nmodel = ShallowWaterModel(;spectral_grid, orography, initial_conditions)\nsimulation = initialize!(model)\nrun!(simulation,n_days=6)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"As a second example, let's investigate the Galewsky et al.[G04] test case for the shallow water equations with and without mountains. As the shallow water system has also only one level, we can reuse the SpectralGrid from Example 1.","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=63,nlev=1)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"Now as a first simulation, we want to disable any orography, so we create a NoOrography","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"orography = NoOrography(spectral_grid)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"Although the orography is zero, you have to pass on spectral_grid so that it can still initialize zero-arrays of the correct size and element type. Awesome. This time the initial conditions should be set the the Galewsky et al.[G04] zonal jet, which is already defined as","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"initial_conditions = ZonalJet()","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"The jet sits at 45˚N with a maximum velocity of 80m/s and a perturbation as described in their paper. Now we construct a model, but this time a ShallowWaterModel","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"model = ShallowWaterModel(;spectral_grid, orography, initial_conditions)\nsimulation = initialize!(model)\nrun!(simulation,n_days=6)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"Oh yeah. That looks like the wobbly jet in their paper. Let's run it again for another 6 days but this time also store NetCDF output.","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"run!(simulation,n_days=6,output=true)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"The progress bar tells us that the simulation run got the identification \"0001\" (which just counts up, so yours might be higher), meaning that data is stored in the folder /run_0001, so let's plot that data properly (and not just using UnicodePlots).","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"using PythonPlot, NCDatasets\nioff() # hide\nds = NCDataset(\"run_0001/output.nc\")\nds[\"vor\"]","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"Vorticity vor is stored as a lon x lat x vert x time array, we may want to look at the first time step, which is the end of the previous simulation (time=6days) which we didn't store output for.","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"t = 1\nvor = Matrix{Float32}(ds[\"vor\"][:,:,1,t]) # convert from Matrix{Union{Missing,Float32}} to Matrix{Float32}\nlat = ds[\"lat\"][:]\nlon = ds[\"lon\"][:]\n\nfig,ax = subplots(1,1,figsize=(10,6))\nax.pcolormesh(lon,lat,vor')\nax.set_xlabel(\"longitude\")\nax.set_ylabel(\"latitude\")\nax.set_title(\"Relative vorticity\")\ntight_layout() # hide\nsavefig(\"galewsky1.png\") # hide\nnothing # hide","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"(Image: Galewsky jet pyplot1)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"You see that in comparison the unicode plot heavily coarse-grains the simulation, well it's unicode after all! And now the last time step, that means time = 12days is","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"t = ds.dim[\"time\"]\nvor = Matrix{Float32}(ds[\"vor\"][:,:,1,t])\nax.pcolormesh(lon,lat,vor')\nsavefig(\"galewsky2.png\") # hide\nnothing # hide","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"(Image: Galewsky jet pyplot2)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"The jet broke up into many small eddies, but the turbulence is still confined to the northern hemisphere, cool! How this may change when we add mountains (we had NoOrography above!), say Earth's orography, you may ask? Let's try it out! We create an EarthOrography struct like so","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"orography = EarthOrography(spectral_grid)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"It will read the orography from file as shown, and there are some smoothing options too, but let's not change them. Same as before, create a model, initialize into a simulation, run. This time directly for 12 days so that we can compare with the last plot","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"model = ShallowWaterModel(;spectral_grid, orography, initial_conditions)\nsimulation = initialize!(model)\nrun!(simulation,n_days=12,output=true)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"This time the run got a new run id, which you see in the progress bar, but can also always check after the run! call (the automatic run id is only determined just before the main time loop starts) with model.output.id, but otherwise we do as before.","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"id = model.output.id","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"ds = NCDataset(\"run_$id/output.nc\")\ntime = 49\nvor = Matrix{Float32}(ds[\"vor\"][:,:,1,time])\n\nfig,ax = subplots(1,1,figsize=(10,6))\nax.pcolormesh(lon,lat,vor')\nax.set_xlabel(\"longitude\")\nax.set_ylabel(\"latitude\")\nax.set_title(\"Relative vorticity\")\ntight_layout() # hide\nsavefig(\"galewsky3.png\") # hide\nnothing # hide","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"(Image: Galewsky jet pyplot3)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"Interesting! The initial conditions have zero velocity in the southern hemisphere, but still, one can see some imprint of the orography on vorticity. You can spot the coastline of Antarctica; the Andes and Greenland are somewhat visible too. Mountains also completely changed the flow after 12 days, probably not surprising!","category":"page"},{"location":"setups/#Polar-jet-streams-in-shallow-water","page":"Model setups","title":"Polar jet streams in shallow water","text":"","category":"section"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"Setup script:","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=63,nlev=1)\nforcing = JetStreamForcing(spectral_grid,latitude=60)\ndrag = QuadraticDrag(spectral_grid)\noutput = OutputWriter(spectral_grid,ShallowWater,output_dt=6,output_vars=[:u,:v,:pres,:orography])\nmodel = ShallowWaterModel(;spectral_grid,output,drag,forcing)\nsimulation = initialize!(model)\nmodel.feedback.verbose = false # hide\nrun!(simulation,n_days=20) # discard first 20 days \nrun!(simulation,n_days=20,output=true)\nnothing # hide","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"We want to simulate polar jet streams in the shallow water model. We add a JetStreamForcing that adds momentum at 60˚N to inject kinetic energy into the model. This energy needs to be removed (the diffusion is likely not sufficient) through a drag, we have implemented a QuadraticDrag and use the default drag coefficient. Outputting uveta (called :pres, as it is the pressure equivalent in the shallow water system) we run 20 days without output to give the system some time to adapt to the forcing. And visualize zonal wind after another 20 days with","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"using PythonPlot, NCDatasets\nioff() # hide\n\nid = model.output.id\nds = NCDataset(\"run_$id/output.nc\")\ntimestep = ds.dim[\"time\"]\nu = Matrix{Float32}(ds[\"u\"][:,:,1,timestep])\nlat = ds[\"lat\"][:]\nlon = ds[\"lon\"][:]\n\nfig,ax = subplots(1,1,figsize=(10,6))\nq = ax.pcolormesh(lon,lat,u')\nax.set_xlabel(\"longitude\")\nax.set_ylabel(\"latitude\")\nax.set_title(\"Zonal wind [m/s]\")\ncolorbar(q,ax=ax)\ntight_layout() # hide\nsavefig(\"polar_jets.png\") # hide\nnothing # hide","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"(Image: Polar jets pyplot)","category":"page"},{"location":"setups/#Gravity-waves-on-the-sphere","page":"Model setups","title":"Gravity waves on the sphere","text":"","category":"section"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"Setup script:","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"using Random # hide\nRandom.seed!(1234) # hide\nusing SpeedyWeather\nspectral_grid = SpectralGrid(trunc=127,nlev=1)\ntime_stepping = SpeedyWeather.Leapfrog(spectral_grid,Δt_at_T31=30)\nimplicit = SpeedyWeather.ImplicitShallowWater(spectral_grid,α=0.5)\norography = EarthOrography(spectral_grid,smoothing=false)\ninitial_conditions = SpeedyWeather.RandomWaves()\noutput = OutputWriter(spectral_grid,ShallowWater,output_dt=12,output_vars=[:u,:pres,:div,:orography])\nmodel = ShallowWaterModel(;spectral_grid,orography,output,initial_conditions,implicit,time_stepping)\nsimulation = initialize!(model)\nmodel.feedback.verbose = false # hide\nrun!(simulation,n_days=2,output=true)\nnothing # hide","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"How are gravity waves propagating around the globe? We want to use the shallow water model to start with some random perturbations of the interface displacement (the \"sea surface height\") but zero velocity and let them propagate around the globe. We set the alpha parameter of the semi-implicit time integration to 05 to have a centred implicit scheme which dampens the gravity waves less than a backward implicit scheme would do. But we also want to keep orography, and particularly no smoothing on it, to have the orography as rough as possible. The initial conditions are set to RandomWaves which set the spherical harmonic coefficients of eta to between given wavenumbers to some random values","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"SpeedyWeather.RandomWaves()","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"so that the amplitude A is as desired, here 2000m. Our layer thickness is by default","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"model.atmosphere.layer_thickness","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"8.5km so those waves are with an amplitude of 2000m quite strong. But the semi-implicit time integration can handle that even with fairly large time steps of","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"model.time_stepping.Δt_sec","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"seconds. Note that the gravity wave speed here is sqrtgH so almost 300m/s. Let us also output divergence, as gravity waves are quite pronounced in that variable. But given the speed of gravity waves we don't have to integrate for long. Visualise with","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"using PythonPlot, NCDatasets\nioff() # hide\n\nid = model.output.id\nds = NCDataset(\"run_$id/output.nc\")\ntimestep = ds.dim[\"time\"]\ndiv = Matrix{Float32}(ds[\"div\"][:,:,1,timestep])\nlat = ds[\"lat\"][:]\nlon = ds[\"lon\"][:]\n\nfig,ax = subplots(1,1,figsize=(10,6))\nax.pcolormesh(lon,lat,div')\nax.set_xlabel(\"longitude\")\nax.set_ylabel(\"latitude\")\nax.set_title(\"Divergence\")\ntight_layout() # hide\nsavefig(\"gravity_waves.png\") # hide\nnothing # hide","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"(Image: Gravity waves pyplot)","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"Can you spot the Himalayas or the Andes?","category":"page"},{"location":"setups/#Jablonowski-Williamson-baroclinic-wave","page":"Model setups","title":"Jablonowski-Williamson baroclinic wave","text":"","category":"section"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=31,nlev=8,Grid=FullGaussianGrid,dealiasing=3)\norography = ZonalRidge(spectral_grid)\ninitial_conditions = ZonalWind()\nmodel = PrimitiveDryModel(;spectral_grid,orography,initial_conditions,physics=false)\nsimulation = initialize!(model)\nmodel.feedback.verbose = false # hide\nrun!(simulation,n_days=9,output=true)\nnothing # hide","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"The Jablonowski-Williamson baroclinic wave test case[JW06] using the Primitive equation model particularly the dry model, as we switch off all physics with physics=false. We want to use 8 vertical levels, and a lower resolution of T31 on a full Gaussian grid. The Jablonowski-Williamson initial conditions are in ZonalWind, the orography is just a ZonalRidge. There is no forcing and the initial conditions are baroclinically unstable which kicks off a wave propagating eastward. This wave becomes obvious when visualised with","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"using PythonPlot, NCDatasets\nioff() # hide\n\nid = model.output.id\nds = NCDataset(\"run_$id/output.nc\")\ntimestep = ds.dim[\"time\"]\nsurface = ds.dim[\"lev\"]\nvor = Matrix{Float32}(ds[\"vor\"][:,:,surface,timestep])\nlat = ds[\"lat\"][:]\nlon = ds[\"lon\"][:]\n\nfig,ax = subplots(1,1,figsize=(10,6))\nax.pcolormesh(lon,lat,vor')\nax.set_xlabel(\"longitude\")\nax.set_ylabel(\"latitude\")\nax.set_title(\"Surface relative vorticity\")\ntight_layout() # hide\nsavefig(\"jablonowski.png\") # hide\nnothing # hide","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"(Image: Jablonowski pyplot)","category":"page"},{"location":"setups/#References","page":"Model setups","title":"References","text":"","category":"section"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"[G04]: Galewsky, Scott, Polvani, 2004. An initial-value problem for testing numerical models of the global shallow-water equations, Tellus A. DOI: 10.3402/tellusa.v56i5.14436","category":"page"},{"location":"setups/","page":"Model setups","title":"Model setups","text":"[JW06]: Jablonowski, C. and Williamson, D.L. (2006), A baroclinic instability test case for atmospheric model dynamical cores. Q.J.R. Meteorol. Soc., 132: 2943-2975. 10.1256/qj.06.12","category":"page"},{"location":"shallowwater/#Shallow-water-model","page":"Shallow water model","title":"Shallow water model","text":"","category":"section"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"The shallow water model describes the evolution of a 2D flow described by its velocity and an interface height that conceptually represents pressure. A divergent flow affects the interface height which in turn can impose a pressure gradient force onto the flow. The dynamics include advection, forces, dissipation, and continuity.","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"The following description of the shallow water model largely follows the idealized models with spectral dynamics developed at the Geophysical Fluid Dynamics Laboratory[1]: The Shallow Water Equations[2].","category":"page"},{"location":"shallowwater/#Shallow-water-equations","page":"Shallow water model","title":"Shallow water equations","text":"","category":"section"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"The shallow water equations of velocity mathbfu = (uv) and interface height eta (i.e. the deviation from the fluid's rest height H) are, formulated in terms of relative vorticity zeta = nabla times mathbfu, divergence mathcalD = nabla cdot mathbfu","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"beginaligned\nfracpartial zetapartial t + nabla cdot (mathbfu(zeta + f)) =\nF_zeta + nabla times mathbfF_mathbfu + (-1)^n+1nunabla^2nzeta \nfracpartial mathcalDpartial t - nabla times (mathbfu(zeta + f)) =\nF_mathcalD + nabla cdot mathbfF_mathbfu\n-nabla^2(tfrac12(u^2 + v^2) + geta) + (-1)^n+1nunabla^2nmathcalD \nfracpartial etapartial t + nabla cdot (mathbfuh) = F_eta\nendaligned","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"We denote time t, Coriolis parameter f, hyperdiffusion (-1)^n+1 nu nabla^2n (n is the hyperdiffusion order, see Horizontal diffusion), gravitational acceleration g, dynamic layer thickness h, and a forcing for the interface height F_eta. In the shallow water model the dynamics layer thickness h is","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"h = eta + H - H_b","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"that is, the layer thickness at rest H plus the interface height eta minus orography H_b. We also add various possible forcing terms F_zeta F_mathcalD F_eta mathbfF_mathbfu = (F_uF_v) which can be defined to force the respective variables, see Extending SpeedyWeather. ","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"In the shallow water system the flow can be described through uv or zetamathcalD which are related through the stream function Psi and the velocity potential Phi (which is zero in the Barotropic vorticity equation).","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"beginaligned\nzeta = nabla^2 Psi \nmathcalD = nabla^2 Phi \nmathbfu = nabla^perp Psi + nabla Phi\nendaligned","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"With nabla^perp being the rotated gradient operator, in cartesian coordinates xy: nabla^perp = (-partial_y partial_x). See Derivatives in spherical coordinates for further details. Especially because the inversion of the Laplacian and the gradients of Psi Phi can be computed in a single pass, see U,V from vorticity and divergence.","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"The divergence/curl of the vorticity flux mathbfu(zeta + f) are combined with the divergence/curl of the forcing vector mathbfF, as","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"beginaligned\n- nabla cdot (mathbfu(zeta + f)) + nabla times mathbfF =\nnabla times (mathbfF + mathbfu_perp(zeta + f)) \nnabla times (mathbfu(zeta + f)) + nabla cdot mathbfF =\nnabla cdot (mathbfF + mathbfu_perp(zeta + f))\nendaligned","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"equivalently to how this is done in the Barotropic vorticity equation with mathbfu_perp = (v-u).","category":"page"},{"location":"shallowwater/#Algorithm","page":"Shallow water model","title":"Algorithm","text":"","category":"section"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"0. Start with initial conditions of relative vorticity zeta_lm, divergence D_lm, and interface height eta_lm in spectral space and transform this model state to grid-point space:","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"Invert the Laplacian of zeta_lm to obtain the stream function Psi_lm in spectral space\nInvert the Laplacian of D_lm to obtain the velocity potential Phi_lm in spectral space\nobtain velocities U_lm = (cos(theta)u)_lm V_lm = (cos(theta)v)_lm from nabla^perpPsi_lm + nablaPhi_lm\nTransform velocities U_lm, V_lm to grid-point space UV\nUnscale the cos(theta) factor to obtain uv\nTransform zeta_lm, D_lm, eta_lm to zeta D eta in grid-point space","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"Now loop over","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"Compute the forcing (or drag) terms F_zeta F_mathcalD F_eta mathbfF_mathbfu\nMultiply uv with zeta+f in grid-point space\nAdd A = F_u + v(zeta + f) and B = F_v - u(zeta + f)\nTransform these vector components to spectral space A_lm, B_lm\nCompute the curl of (AB)_lm in spectral space and add to forcing of zeta_lm\nCompute the divergence of (AB)_lm in spectral space and add to forcing of divergence mathcalD_lm\nCompute the kinetic energy frac12(u^2 + v^2) and transform to spectral space\nAdd to the kinetic energy the \"geopotential\" geta_lm in spectral space to obtain the Bernoulli potential\nTake the Laplacian of the Bernoulli potential and subtract from the divergence tendency\nCompute the volume fluxes uhvh in grid-point space via h = eta + H - H_b\nTransform to spectral space and take the divergence for -nabla cdot (mathbfuh). Add to forcing for eta.\nCorrect the tendencies following the semi-implicit time integration to prevent fast gravity waves from causing numerical instabilities\nCompute the horizontal diffusion based on the zetamathcalD tendencies\nCompute a leapfrog time step as described in Time integration with a Robert-Asselin and Williams filter\nTransform the new spectral state of zeta_lm, mathcalD_lm, eta_lm to grid-point uvzetamathcalDeta as described in 0.\nPossibly do some output\nRepeat from 1.","category":"page"},{"location":"shallowwater/#implicit_swm","page":"Shallow water model","title":"Semi-implicit time integration","text":"","category":"section"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"Probably the biggest advantage of a spectral model is its ability to solve (parts of) the equations implicitly a low computational cost. The reason is that a linear operator can be easily inverted in spectral space, removing the necessity to solve large equation systems. An operation like Psi = nabla^-2zeta in grid-point space is costly because it requires a global communication, coupling all grid points. In spectral space nabla^2 is a diagonal operator, meaning that there is no communication between harmonics and its inversion is therefore easily done on a mode-by-mode basis of the harmonics.","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"This can be made use of when facing time stepping constraints with explicit schemes, where ridiculously small time steps to resolve fast waves would otherwise result in a horribly slow simulation. In the shallow water system there are gravity waves that propagate at a wave speed of sqrtgH (typically 300m/s), which, in order to not violate the CFL criterion for explicit time stepping, would need to be resolved. Therefore, treating the terms that are responsible for gravity waves implicitly would remove that time stepping constraint and allows us to run the simulation at the time step needed to resolve the advective motion of the atmosphere, which is usually one or two orders of magnitude longer than gravity waves.","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"In the following we will describe how the semi implicit time integration can be combined with the Leapfrog time stepping and the Robert-Asselin and Williams filter for a large increase in numerical stability with gravity waves. Let V_i be the model state of all prognostic variables at time step i, the leapfrog time stepping is then","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"fracV_i+1 - V_i-12Delta t = N(V_i)","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"with the right-hand side operator N evaluated at the current time step i. Now the idea is to split the terms in N into non-linear terms that are evaluated explicitly in N_E and into the linear terms N_I, solved implicitly, that are responsible for the gravity waves. Linearization happens around a state of rest without orography.","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"We could already assume to evaluate N_I at i+1, but in fact, we can introduce alpha in 01 so that for alpha=0 we use i-1 (i.e. explicit), for alpha=12 it is centred implicit tfrac12N_I(V_i-1) + tfrac12N_I(V_i+1), and for alpha=1 a fully backwards scheme N_I(V_i+1) evaluated at i+1.","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"fracV_i+1 - V_i-12Delta t = N_E(V_i) + alpha N_I(V_i+1) + (1-alpha)N_I(V_i-1)","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"Let delta V = tfracV_i+1 - V_i-12Delta t be the tendency we need for the Leapfrog time stepping. Introducing xi = 2alphaDelta t we have","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"delta V = N_E(V_i) + N_I(V_i-1) + xi N_I(delta V)","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"because N_I is a linear operator. This is done so that we can solve for delta V by inverting N_I, but let us gather the other terms as G first.","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"G = N_E(V_i) + N_I(V_i-1) = N(V_i) + N_I(V_i-1 - V_i)","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"For the shallow water equations we will only make use of the last formulation, meaning we first evaluate the whole right-hand side N(V_i) at the current time step as we would do with fully explicit time stepping but then add the implicit terms N_I(V_i-1 - V_i) afterwards to move those terms from i to i-1. Note that we could also directly evaluate the implicit terms at i-1 as it is suggested in the previous formulation N_E(V_i) + N_I(V_i-1), the result would be the same. But in general it can be more efficient to do it one or the other way, and in fact it is also possible to combine both ways. This will be discussed in the semi-implicit time stepping for the primitive equations.","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"We can now implicitly solve for delta V by","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"delta V = (1-xi N_I)^-1G","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"So what is N_I? In the shallow water system the gravity waves are caused by","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"beginaligned\nfracpartial mathcalDpartial t = -gnabla^2eta \nfracpartial etapartial t = -HmathcalD\nendaligned","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"which is a linearization of the equations around a state of rest with uniform constant layer thickness h = H. The continuity equation with the -nabla(mathbfuh) term, for example, is linearized to -nabla(mathbfuH) = -HmathcalD. The divergence and continuity equations can now be written following the delta V = G + xi N_I(delta V) formulation from above as a coupled system (The vorticity equation is zero for the linear gravity wave equation in the shallow water equations, hence no semi-implicit correction has to be made to the vorticity tendency).","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"beginaligned\ndelta mathcalD = G_mathcalD - xi g nabla^2 delta eta \ndelta eta = G_mathcaleta - xi H deltamathcalD\nendaligned","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"with","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"beginaligned\nG_mathcalD = N_mathcalD - xi g nabla^2 (eta_i-1 - eta_i) \nG_mathcaleta = N_eta - xi H (mathcalD_i-1 - mathcalD_i)\nendaligned","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"Inserting the second equation into the first, we can first solve for delta mathcalD, and then for delta eta. Reminder that we do this in spectral space to every harmonic independently, so the Laplace operator nabla^2 = -l(l+1) takes the form of its eigenvalue -l(l+1) (normalized to unit sphere, as are the scaled shallow water equations) and its inversion is therefore just the inversion of this scalar.","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"delta D = fracG_mathcalD - xi gnabla^2 G_eta1 - xi^2 H nabla^2 = S^-1(G_mathcalD - xi gnabla^2 G_eta) ","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"Where the last formulation just makes it clear that S = 1 - xi^2 H nabla^2 is the operator to be inverted. delta eta is then obtained via insertion as written above. Equivalently, by adding a superscript l for every degree of the spherical harmonics, we have","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"delta mathcalD^l = fracG_mathcalD^l + xi g l(l+1) G_eta^l1 + xi^2 H l(l+1)","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"The idea of the semi-implicit time stepping is now as follows:","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"Evaluate the right-hand side explicitly at time step i to obtain the explicit, preliminary tendencies N_mathcalDN_eta (and N_zeta without a need for semi-implicit correction)\nMove the implicit terms from i to i-1 when calculating G_mathcalD G_eta\nSolve for delta mathcalD, the new, corrected tendency for divergence.\nWith delta mathcalD obtain delta eta, the new, corrected tendency for eta.\nApply horizontal diffusion as a correction to N_zeta delta mathcalD as outlined in Horizontal diffusion.\nLeapfrog with tendencies that have been corrected for both semi-implicit and diffusion.","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"Some notes on the semi-implicit time stepping","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"The inversion of the semi-implicit time stepping depends on delta t, that means every time the time step changes, the inversion has to be recalculated.\nYou may choose alpha = 12 to dampen gravity waves but initialization shocks still usually kick off many gravity waves that propagate around the sphere for many days.\nWith increasing alpha 12 these waves are also slowed down, such that for alpha = 1 they quickly disappear in several hours.\nUsing the scaled shallow water equations the time step delta t has to be the scaled time step tildeDelta t = delta tR which is divided by the radius R. Then we use the normalized eigenvalues -l(l+1) which also omit the 1R^2 scaling, see scaled shallow water equations for more details.","category":"page"},{"location":"shallowwater/#scaled_swm","page":"Shallow water model","title":"Scaled shallow water equations","text":"","category":"section"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"Similar to the scaled barotropic vorticity equations, SpeedyWeather.jl scales in the shallow water equations. The vorticity and the divergence equation are scaled with R^2, the radius of the sphere squared, but the continuity equation is scaled with R. We also combine the vorticity flux and forcing into a single divergence/curl operation as mentioned in Shallow water equations above","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"beginaligned\nfracpartial tildezetapartial tildet =\ntildenabla times (tildemathbfF + mathbfu_perp(tildezeta + tildef)) +\n(-1)^n+1tildenutildenabla^2ntildezeta \nfracpartial tildemathcalDpartial tildet =\ntildenabla cdot (tildemathbfF + mathbfu_perp(tildezeta + tildef)) -\ntildenabla^2left(tfrac12(u^2 + v^2) + geta right) +\n(-1)^n+1tildenutildenabla^2ntildemathcalD \nfracpartial etapartial tildet =\n- tildenabla cdot (mathbfuh) + tildeF_eta\nendaligned","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"As in the scaled barotropic vorticity equations, one needs to scale the time step, the Coriolis force, the forcing and the diffusion coefficient, but then enjoys the luxury of working with dimensionless gradient operators. As before, SpeedyWeather.jl will scale vorticity and divergence just before the model integration starts and unscale them upon completion and for output. In the semi-implicit time integration we solve an equation that also has to be scaled. It is with radius squared scaling (because it is the tendency for the divergence equation which is also scaled with R^2)","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"R^2 delta D = R^2fracG_mathcalD - xi gnabla^2 G_eta1 - xi^2 H nabla^2","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"As G_eta is only scaled with R we have","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"tildedelta D = fractildeG_mathcalD - tildexi gtildenabla^2 tildeG_eta1 - tildexi^2 H tildenabla^2","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"The R^2 normalizes the Laplace operator in the numerator, but using the scaled G_eta we also scale xi (which is convenient, because the time step within is the one we use anyway). The denominator S does not actually change because xi^2nabla^2 = tildexi^2tildenabla^2 as xi^2 is scaled with 1R^2, but the Laplace operator with R^2. So overall we just have to use the scaled time step tildeDelta t and normalized eigenvalues for tildenabla^2.","category":"page"},{"location":"shallowwater/#References","page":"Shallow water model","title":"References","text":"","category":"section"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"[1]: Geophysical Fluid Dynamics Laboratory, Idealized models with spectral dynamics","category":"page"},{"location":"shallowwater/","page":"Shallow water model","title":"Shallow water model","text":"[2]: Geophysical Fluid Dynamics Laboratory, The Shallow Water Equations.","category":"page"},{"location":"extending/#Extending-SpeedyWeather","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"","category":"section"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Generally, SpeedyWeather is built in a very modular, extensible way. While that sounds fantastic in general, it does not save you from understanding its modular logic before you can extend SpeedyWeather.jl easily yourself. We highly recommend you to read the following sections if you would like to extend SpeedyWeather in some way, but it also gives you a good understanding of how we build SpeedyWeather in the first place. Because in the end there is no difference between internally or externally defined model components. Having said that, there is a question of the Scope of variables meaning that some functions or types require its module to be explicitly named, like SpeedyWeather.some_function instead of just some_function. But that's really it.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Before and especially after reading this section you are welcome to raise an issue about whatever you would like to do with SpeedyWeather. We are happy to help.","category":"page"},{"location":"extending/#logic","page":"Extending SpeedyWeather","title":"SpeedyWeather's modular logic","text":"","category":"section"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Almost every component in SpeedyWeather is implemented in three steps:","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Define a new type,\ndefine its initialization,\nextend a function that defines what it does.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"To be a bit more explicit (Julia always encourages you to think more abstractly, which can be difficult to get started...) we will use the example of defining a new forcing for the Barotropic or ShallowWater models. But the concept is the same whether you want to define a forcing, a drag, or a new parameterization for the primitive equations, etc. In general, you can define a new component also just in a notebook or in the Julia REPL, you do not have to branch off from the repository and write directly into it. However, note the Scope of variables if you define a component externally.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"To define a new forcing type, at the most basic level you would do","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"struct MyForcing <: AbstractForcing\n # define some parameters and work arrays here\n a::Float64\n v::Vector{Float64}\nend","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"In Julia this introduces a new (so-called compound) type that is a subtype of AbstractForcing, we have a bunch of these abstract super types defined and you want to piggy-back on them because of multiple-dispatch. This new type could also be a mutable struct, could have keywords defined with Base.@kwdef and can also be parametric with respect to the number format or grid, but let's skip those details for now. Conceptually you include into the type any parameters (example the float a here) that you may need and especially those that you want to change. This type will get a user-facing interface so that one can quickly create a new forcing but with altered parameters. Generally you should also include any kind of precomputed or work arrays (here a vector v). For example, you want to apply your forcing only in certain parts of the globe? Then you probably want to define a mask here that somehow includes the information of your region. For a more concrete example see Custom forcing: define.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"To define the new type's initialization, at the most basic level you need to extend the initialize! function for this new type","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"function initialize!(forcing::MyForcing,model::ModelSetup)\n # fill in/change any fields of your new forcing here\n forcing.v[1] = 1\n # you can use information from other model components too\n forcing.v[2] = model.planet.gravity\nend","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"This function is called once during model initialisation (which is in fact just the initialisation of all its components, like the forcing here) and it allows you to precompute values or arrays also based on parameters of other model components. Like in this example, we want to use the gravity that is defined in model.planet. If you need a value for gravity in your forcing you could add a gravity field therein, but then if you change planet.gravity this change would not propagate into your forcing! Another example would be to use model.geometry.coslat if you need to use the cosine of latitude for some precomputation, which, however, depends on the resolution and so should not be hardcoded into your forcing.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"As the last step we have to extend the forcing! function which is the function that is called on every step of the time integration. This new method for forcing! needs to have the following function signature","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"function forcing!( diagn::DiagnosticVariablesLayer,\n forcing::MyForcing,\n time::DateTime,\n model::ModelSetup)\n # whatever the forcing is supposed to do, in the end you want\n # to write into the tendency fields\n diagn.tendencies.u_tend_grid[1] = forcing.a\n diagn.tendencies.v_tend_grid[1] = forcing.a\n diagn.tendencies.vor_tend[1] = forcing.a\nend","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"DiagnosticVariablesLayer is the type of the first argument, because it contains the tendencies you will want to change, so this is supposed to be read and write. The other arguments should be treated read-only. You make use of the time or anything else in the model, but the latter likely comes with a performance penalty which is why we often unpack the model in a function barrier. But let's skip that detail for now. Generally, try to precompute what you can in initialize!. For the forcing you will need to force the velocities u,v in grid-point space or the vorticity vor, divergence div in spectral space. This is not a constrain in most applications we came across, but in case it is in yours please reach out.","category":"page"},{"location":"extending/#Scope-of-variables","page":"Extending SpeedyWeather","title":"Scope of variables","text":"","category":"section"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"The above (conceptual!) examples leave out some of the details, particularly around the scope of variables when you want to define a new forcing interactively inside a notebook or the REPL (which is actually the recommended way!!). To respect the scope of variables, a bunch of functions will need their module to be explicit specified. In general, you should be familiar with Julia's scope of variables logic.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Defining a new type for example as a subtype of AbstractForcing, you will actually want to do","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"struct MyForcing <: SpeedyWeather.AbstractForcing\n # fields\nend","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Because AbstractForcing is defined inside SpeedyWeather, but we do not export all functions/types in order to not flood your global scope to avoid naming conflicts. Rule of thumb: If you get an error hinting at something is not defined, make sure you are in the right scope!","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Then the initialize! function is a function inside the SpeedyWeather module, as we want to define a new method for it outside that can be called inside we actually need to write","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"function SpeedyWeather.initialize!(forcing::MyForcing,model::SpeedyWeather.ModelSetup)\n # how to initialize it\nend","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"And similar for SpeedyWeather.forcing!.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"You also probably want to make use of functions that are already defined inside SpeedyWeather or its submodules SpeedyTransforms, or RingGrids. If something does not seem to be defined, although you can see it in the documentation or directly in the code, you probably need to specify its module too! Alternatively, note that you can also always do import SpeedWeather: ModelSetup to bring a given variable into global scope which removes the necessity to write SpeedyWeather.ModelSetup.","category":"page"},{"location":"extending/#Custom-forcing:-define","page":"Extending SpeedyWeather","title":"Custom forcing: define","text":"","category":"section"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"The following example is a bit more concrete than the previous conceptual example, but we try to add a few more details that are important, or you at least should be aware of it. In this example we want to add a StochasticStirring forcing as defined in Vallis et al., 2004","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"beginaligned\nfracpartial zetapartial t + nabla cdot (mathbfu(zeta + f)) =\nS - rzeta - nunabla^4zeta \nS_lm^i = A(1-exp(-2tfracDelta ttau))Q^i_lm + exp(-tfracdttau)S_lm^i-1 \nendaligned","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"So there is a term S that is supposed to force the vorticity equation in the [Barotropic vorticity model]. However, this term is also stochastically evolving in time, meaning we have to store the previous time steps, i-1, in spectral space, because that's where the forcing is defined: for degree l and order m of the spherical harmonics. A is a real amplitude. Delta t the time step of the model, tau the decorrelation time scale of the stochastic process. Q is for every spherical harmonic a complex random uniform number in -11 in both its real and imaginary components. So we actually define our StochasticStirring forcing as follows and will explain the details in second","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"using SpeedyWeather, Dates\n\nBase.@kwdef struct StochasticStirring{NF} <: SpeedyWeather.AbstractForcing{NF}\n \n # DIMENSIONS from SpectralGrid\n \"Spectral resolution as max degree of spherical harmonics\"\n trunc::Int\n \n \"Number of latitude rings, used for latitudinal mask\"\n nlat::Int\n\n \n # OPTIONS\n \"Decorrelation time scale τ [days]\"\n decorrelation_time::Float64 = 2\n\n \"Stirring strength A [1/s²]\"\n strength::Float64 = 1e-11\n\n \"Stirring latitude [˚N]\"\n latitude::Float64 = 45\n\n \"Stirring width [˚]\"\n width::Float64 = 24\n\n \n # TO BE INITIALISED\n \"Stochastic stirring term S\"\n S::LowerTriangularMatrix{Complex{NF}} = zeros(LowerTriangularMatrix{Complex{NF}},trunc+2,trunc+1)\n \n \"a = A*sqrt(1 - exp(-2dt/τ)), the noise factor times the stirring strength [1/s²]\"\n a::Base.RefValue{NF} = Ref(zero(NF))\n \n \"b = exp(-dt/τ), the auto-regressive factor [1]\"\n b::Base.RefValue{NF} = Ref(zero(NF))\n \n \"Latitudinal mask, confined to mid-latitude storm track by default [1]\"\n lat_mask::Vector{NF} = zeros(NF,nlat)\nend","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"So, first the scalar parameters, are added as fields of type Float64 with some default values as suggested in the Vallis et al., 2004 paper. In order to be able to define the default values, we add the Base.@kwdef macro before the struct definition. Then we need the term S as coefficients of the spherical harmonics, which is a LowerTriangularMatrix, however we want its elements to be of number format NF, which is also the parametric type of StochasticStirring{NF}, this is done because it will allow us to use multiple dispatch not just based on StochasticStirring but also based on the number format. Neat. In order to allocate S with some default though we need to know the size of the matrix, which is given by the spectral resolution trunc. So in order to automatically allocate S based on the right size we add trunc as another field, which does not have a default but will be initialised with the help of a SpectralGrid, as explained later. So once we call StochasticStirring{NF}(trunc=31) then S will automatically have the right size.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Then we also see in the definition of S that there are prefactors A(1-exp(-2tfracDelta ttau)) which depend on the forcing's parameters but also on the time step, which, at the time of the creation of StochasticStirring we might not know about! And definitely do not want to hardcode in. So to illustrate what you can do in this case we define two additional parameters a,b that are just initialized as zero, but that will be precalculated in the initialize! function. However, we decided to define our struct as immutable (meaning you cannot change it after creation unless its elements have mutable fields, like the elements in vectors). In order to make it mutable, we could write mutable struct instead, or as outlined here use RefValues. Another option would be to just recalculate a,b in forcing! on every time step. Depending on exactly what you would like to do, you can choose your way. Anyway, we decide to include a,b as RefValues so that we can always access the scalar underneath with a[] and b[] and also change it with a[] = 1 etc.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Lastly, the Vallis et al., 2004 paper also describes how the forcing is not supposed to be applied everywhere on the globe but only over a range of latitudes, meaning we want to scale down certain latitudes with a factor approaching zero. For this we want to define a latitudinal mask lat_mask that is a vector of length nlat, the number of latitude rings. Similar to S, we want to allocate it with zeros (or any other value for that matter), but then precompute this mask in the initialize! step. For this we need to know nlat at creation time meaning we add this field similar as to how we added trunc. This mask requires the parameters latitude and a width which are therefore also added to the definition of StochasticStirring.","category":"page"},{"location":"extending/#Custom-forcing:-generator-function","page":"Extending SpeedyWeather","title":"Custom forcing: generator function","text":"","category":"section"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Cool. Now you could create our new StochasticStirring forcing with StochasticStirring{NF}(trunc=31,nlat=48), and the default values would be chosen as well as the correct size of the arrays S and lat_mask we need. Furthermore, note that because StochasticStirring{NF} is parametric on the number format NF, these arrays are also allocated with the correct number format that will be used throughout model integration.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"But in SpeedyWeather we typically use the SpectralGrid object to pass on the information of the resolution (and number format) so we want a generator function like","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"function StochasticStirring(SG::SpectralGrid;kwargs...)\n (;trunc,Grid,nlat_half) = SG\n nlat = RingGrids.get_nlat(Grid,nlat_half)\n return StochasticStirring{SG.NF}(;trunc,nlat,kwargs...)\nend\nnothing # hide","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Which allows us to do","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"julia> spectral_grid = SpectralGrid(trunc=42,nlev=1)\njulia> stochastic_stirring = StochasticStirring(spectral_grid,latitude=30,decorrelation_time=5)","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"So the respective resolution parameters and the number format are just pulled from the SpectralGrid as a first argument and the remaining parameters are just keyword arguments that one can change at creation. This evolved as a SpeedyWeather convention: The first argument of the generating function to a model component is a SpectralGrid and other keyword arguments specific to that component follow.","category":"page"},{"location":"extending/#Custom-forcing:-initialize","page":"Extending SpeedyWeather","title":"Custom forcing: initialize","text":"","category":"section"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Now let us have a closer look at the details of the initialize! function, in our example we would actually do","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"function SpeedyWeather.initialize!( forcing::StochasticStirring,\n model::SpeedyWeather.ModelSetup)\n \n # precompute forcing strength, scale with radius^2 as is the vorticity equation\n (;radius) = model.spectral_grid\n A = radius^2 * forcing.strength\n \n # precompute noise and auto-regressive factor, packed in RefValue for mutability\n dt = model.time_stepping.Δt_sec\n τ = forcing.decorrelation_time*24*3600 # convert to seconds\n forcing.a[] = A*sqrt(1 - exp(-2dt/τ))\n forcing.b[] = exp(-dt/τ)\n \n # precompute the latitudinal mask\n (;Grid,nlat_half) = model.spectral_grid\n latd = RingGrids.get_latd(Grid,nlat_half)\n \n for j in eachindex(forcing.lat_mask)\n # Gaussian centred at forcing.latitude of width forcing.width\n forcing.lat_mask[j] = exp(-(forcing.latitude-latd[j])^2/forcing.width^2*2)\n end\n\n return nothing\nend","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"As we want to add a method for the StochasticStirring to the initialize! function from within SpeedyWeather we add the SpeedyWeather. to add this method in the right Scope of variables. The initialize! function must have that function signature, instance of your new type StochasticStirring first, then the second argument a model of type ModelSetup or, if your forcing (and in general component) only makes sense in a specific model, you could also write model::Barotropic for example, to be more restrictive. Inside the initialize! method we are defining we can use parameters from other components. For example, the definition of the S term includes the time step Delta t, which should be pulled from the model.time_stepping. We also pull the Grid and its resolution parameter nlat_half (see Grids) to get the latitudes with get_latd from the RingGrids module. Alternatively, we could have used model.geometry.latd which is contains a bunch of similar arrays describing the geometry of the grid we use and at its given resolution.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Note that initialize! is expected to be read and write on the forcing argument (hence using Julia's !-notation) but read-only on the model, except for model.forcing which points to the same object. You technically can initialize or generally alter several model components in one, but that not advised and can easily lead to unexpected behaviour because of multiple dispatch.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"As a last note on initialize!, you can see that we scale the amplitude/strength A with the radius squared, this is because the Barotropic vorticity equation are scaled that way, so we have to scale S too.","category":"page"},{"location":"extending/#Custom-forcing:-forcing!-function","page":"Extending SpeedyWeather","title":"Custom forcing: forcing! function","text":"","category":"section"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Now that we have defined how to create and initialize our new StochasticStirring forcing, we need to define what it actually is supposed to do. For this SpeedyWeather will call the forcing! function within a time step. However, this function is not yet defined for our new StochasticStirring forcing. But if you define it as follows then this will be called automatically with multiple dispatch.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"function SpeedyWeather.forcing!(diagn::SpeedyWeather.DiagnosticVariablesLayer,\n forcing::StochasticStirring,\n time::DateTime,\n model::SpeedyWeather.ModelSetup)\n # function barrier only\n forcing!(diagn,forcing,model.spectral_transform)\nend","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"The function has to be as outlined above. The first argument has to be of type DiagnosticVariablesLayer as it acts on a layer of the diagnostic variables, where the current model state in grid-point space and the tendencies (in spectral space) are defined. The second argument has to be of the type of our new forcing, the third argument is time which you may use or skip, the last element is a ModelSetup, but as before you can be more restrictive to define a forcing only for the BarotropicModel for example, use modelBarotropic in that case.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"As you can see, for now not much is actually happening inside this function, this is what is often called a function barrier, the only thing we do in here is to unpack the model to the specific model components we actually need. You can omit this function barrier and jump straight to the definition below, but often this is done for performance reasons: model has several abstract fields which the compiler cannot optimize for, but unpacking them makes that possible. So we define the actual forcing! function that's then called as follows","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"function forcing!( diagn::SpeedyWeather.DiagnosticVariablesLayer,\n forcing::StochasticStirring{NF},\n spectral_transform::SpectralTransform) where NF\n \n # noise and auto-regressive factors\n a = forcing.a[] # = sqrt(1 - exp(-2dt/τ))\n b = forcing.b[] # = exp(-dt/τ)\n \n (;S) = forcing\n for lm in eachindex(S)\n # Barnes and Hartmann, 2011 Eq. 2\n Qi = 2rand(Complex{NF}) - (1 + im) # ~ [-1,1] in complex\n S[lm] = a*Qi + b*S[lm]\n end\n\n # to grid-point space\n S_grid = diagn.dynamics_variables.a_grid\n SpeedyTransforms.gridded!(S_grid,S,spectral_transform)\n \n # mask everything but mid-latitudes\n RingGrids._scale_lat!(S_grid,forcing.lat_mask)\n \n # back to spectral space\n (;vor_tend) = diagn.tendencies\n SpeedyTransforms.spectral!(vor_tend,S_grid,spectral_transform)\n SpeedyTransforms.spectral_truncation!(vor_tend) # set lmax+1 to zero\n \n return nothing\nend\nnothing # hide","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"The function signature can then just match to whatever we need. In our case we have a forcing defined in spectral space which, however, is masked in grid-point space. So we will need the model.spectral_transform. You could recompute the spectral_transform object inside the function but that is inefficient.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Now this is actually where we implement the equation we started from in Custom forcing: define simply by looping over the spherical harmonics in S and updating its entries. Then we transform S into grid-point space using the a_grid work array that is in dynamics_variables, b_grid is another one you can use, so are a,b in spectral space. However, these are really work arrays, meaning you should expect them to be overwritten momentarily once the function concludes and no information will remain. Equivalently, these arrays may have an undefined state prior to the forcing! call. We then use the _scale_lat! function from RingGrids which takes every element in the latitude mask lat_mask and multiplies it with every grid-point on the respective latitude ring.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Now for the last lines we have to know the order in which different terms are written into the tendencies for vorticity, diagn.tendencies.vor_tend. In SpeedyWeather, the forcing! comes first, then the drag! (see Custom drag) then the curl of the vorticity flux (the vorticity advection). This means we can transform S_grid directly back into vor_tend without overwriting other terms which, in fact, will be added to this array afterwards. In general, you can also force the momentum equations in grid-point space by writing into u_tend_grid and v_tend_grid.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Then one last detail is the call to spectral_truncation!. Despite the triangular spectral truncation in SpeedyWeather, the lower triangular matrices for the spherical harmonics are actually of size N+1 times N, because this additional degree (=row) is needed for vector quantities (see One more degree for spectral fields). Here particularly, this means that the last spectral transform will compute the spherical harmonics of this last degree which, however, scalar quantities like vorticity should not make use of. We therefore set this degree to zero with the call to spectral_truncation!, which does exactly that.","category":"page"},{"location":"extending/#Custom-forcing:-model-construction","page":"Extending SpeedyWeather","title":"Custom forcing: model construction","text":"","category":"section"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Now that we have defined a new forcing, how to initialize it and what it is supposed to execute on every time step, we explain how to construct a model with this new forcing. We generally follow other [Model setups], start with the SpectralGrid, use that to get a StochasticStirring forcing instance. This calls the generator function from Custom forcing: generator function. Here we want to stir vorticity not at the default latitude of 45N, but on the southern hemisphere to illustrate how to pass on non-default parameters. We explicitly set the initial_conditions to rest and pass them as well as forcing=stochastic_stirring on to the BarotropicModel constructor. That's it! This is really the beauty of our modular interface that you can create instances of individual model components and just put them together as you like, and as long as you follow some rules.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"spectral_grid = SpectralGrid(trunc=42,nlev=1)\nstochastic_stirring = StochasticStirring(spectral_grid,latitude=-45)\ninitial_conditions = StartFromRest()\nmodel = BarotropicModel(;spectral_grid,initial_conditions,forcing=stochastic_stirring)\nsimulation = initialize!(model)\nrun!(simulation)","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"Yay! As you can see the vorticity does something funky on the southern hemisphere but not on the northern, as we do not force there. Awesome! Adding new components other than forcing works surprisingly similar. We briefly discuss how to add a custom drag to illustrate the differences but there are not really many.","category":"page"},{"location":"extending/#Custom-drag","page":"Extending SpeedyWeather","title":"Custom drag","text":"","category":"section"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"From the barotropic vorticity equation in Custom forcing: define we omitted the drag term -rzeta which however can be defined in a strikingly similar way. This section is just to outline some differences.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"SpeedyWeather defines AbstractForcing and AbstractDrag, both are only conceptual supertypes, and in fact you could define a forcing as a subtype of AbstractDrag and vice versa. So for a drag, most straight-forwardly you would do","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"struct MyDrag <: AbstractDrag\n # parameters and arrays\nend","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"then define the initialize! function as before, but extend the method drag! instead of forcing!. The only detail that is important to know is that forcing! is called first, then drag! and then the other tendencies. So if you write into vor_tend like so vor_tend[1] = 1, you will overwrite the previous tendency. For the forcing that does not matter as it is the first one to be called, but for the drag you will want to do vor_tend[1] += 1 instead to accumulate the tendency. Otherwise you would undo the forcing term! Note that this conflict would be avoided if the forcing writes into vor_tend but the drag writes into u_tend_grid.","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"In general, these are the fields you can write into for new terms","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"vor_tend in spectral space\ndiv_tend in spectral space (shallow water only)\npres_tend in spectral space (shallow water only)\nu_tend_grid in grid-point space\nv_tend_grid in grid-point space","category":"page"},{"location":"extending/","page":"Extending SpeedyWeather","title":"Extending SpeedyWeather","text":"These space restrictions exist because of the way how SpeedyWeather transforms between spaces to obtain tendencies.","category":"page"},{"location":"spectral_transform/#Spherical-Harmonic-Transform","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The following sections outline the implementation of the spherical harmonic transform (in short spectral transform) between the coefficients of the spherical harmonics (the spectral space) and the grid space which can be any of the Implemented grids as defined by RingGrids. This includes the classical full Gaussian grid, a regular longitude-latitude grid called the full Clenshaw grid (FullClenshawGrid), ECMWF's octahedral Gaussian grid[Malardel2016], and HEALPix grids[Gorski2004]. SpeedyWeather.jl's spectral transform module SpeedyTransforms is grid-flexible and can be used with any of these, see Grids.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"info: SpeedyTransforms is a module too!\nSpeedyTransform is the underlying module that SpeedyWeather imports to transform between spectral and grid-point space, which also implements Derivatives in spherical coordinates. You can use this module independently of SpeedyWeather for spectral transforms, see SpeedyTransforms.","category":"page"},{"location":"spectral_transform/#Inspiration","page":"Spherical Harmonic Transform","title":"Inspiration","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The spectral transform implemented by SpeedyWeather.jl follows largely Justin Willmert's CMB.jl and SphericalHarmonicTransforms.jl package and makes use of AssociatedLegendrePolynomials.jl and FFTW.jl for the Fourier transform. Justin described his work in a Blog series [Willmert2020].","category":"page"},{"location":"spectral_transform/#Spherical-harmonics","page":"Spherical Harmonic Transform","title":"Spherical harmonics","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The spherical harmonics Y_lm of degree l and order m over the longitude phi = (02pi) and colatitudes theta = (-pi2pi2), are","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Y_lm(phi theta) = lambda_l^m(sintheta) e^imphi","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"with lambda_l^m being the pre-normalized associated Legendre polynomials, and e^imphi are the complex exponentials (the Fourier modes). Together they form a set of orthogonal basis functions on the sphere. For an interactive visualisation of the spherical harmonics, see here.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"info: Latitudes versus colatitudes\nThe implementation of the spectral transforms in SpeedyWeather.jl uses colatitudes theta = (0pi) (0 at the north pole) but the dynamical core uses latitudes theta = (-pi2pi2) (pi2 at the north pole). Note: We may also use latitudes in the spherical harmonic transform in the future for consistency. ","category":"page"},{"location":"spectral_transform/#synthesis","page":"Spherical Harmonic Transform","title":"Synthesis (spectral to grid)","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The synthesis (or inverse transform) takes the spectral coefficients a_lm and transforms them to grid-point values f(phitheta) (for the sake of simplicity first regarded as continuous). The synthesis is a linear combination of the spherical harmonics Y_lm with non-zero coefficients.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"f(phitheta) = sum_l=0^infty sum_m=-l^l a_lm Y_lm(phitheta)","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"We obtain an approximation with a finite set of a_lm by truncating the series in both degree l and order m somehow. Most commonly, a triangular truncation is applied, such that all degrees after l = l_max are discarded. Triangular because the retained array of the coefficients a_lm looks like a triangle. Other truncations like rhomboidal have been studied[Daley78] but are rarely used since. Choosing l_max also constrains m_max and determines the (horizontal) spectral resolution. In SpeedyWeather.jl this resolution as chosen as trunc when creating the SpectralGrid.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"For f being a real-valued there is a symmetry","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"a_l-m = (-1)^m a^*_l+m","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"meaning that the coefficients at -m and m are the same, but the sign of the real and imaginary component can be flipped, as denoted with the (-1)^m and the complex conjugate a_lm^*. As we are only dealing with real-valued fields anyway, we therefore never have to store the negative orders -m and end up with a lower triangular matrix of size (l_max+1) times (m_max+1) or technically (T+1)^2 where T is the truncation trunc. One is added here because the degree l and order m use 0-based indexing but sizes (and so is Julia's indexing) are 1-based.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"For correctness we want to mention here that vector quantities require one more degree l due to the recurrence relation in the Meridional derivative. Hence for practical reasons all spectral fields are represented as a lower triangular matrix of size (m_max + 2) times (m_max +1). And the scalar quantities would just not make use of that last degree, and its entries would be simply zero. We will, however, for the following sections ignore this and only discuss it again in Meridional derivative.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Another consequence of the symmetry mentioned above is that the zonal harmonics, meaning a_lm=0 have no imaginary component. Because these harmonics are zonally constant, a non-zero imaginary component would rotate them around the Earth's axis, which, well, doesn't actually change a real-valued field. ","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Following the notation of [Willmert2020] we can therefore write the truncated synthesis as","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"f(phitheta) = sum_l=0^l_max sum_m=0^l (2-delta_m0) a_lm Y_lm(phitheta)","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The (2-delta_m0) factor using the Kronecker delta is used here because of the symmetry we have to count both the m-m order pairs (hence the 2) except for the zonal harmonics which do not have a pair.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Another symmetry arises from the fact that the spherical harmonics are either symmetric or anti-symmetric around the Equator. There is an even/odd combination of degrees and orders so that the sign flips like a checkerboard","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Y_lm(phipi-theta) = (-1)^l+mY_lm(phiphi)","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"This means that one only has to compute the Legendre polynomials for one hemisphere and the other one follows with this equality.","category":"page"},{"location":"spectral_transform/#analysis","page":"Spherical Harmonic Transform","title":"Analysis (grid to spectral)","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Starting in grid-point space we can transform a field f(lambdatheta) into the spectral space of the spherical harmonics by","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"a_lm = int_0^2pi int_0^pi f(phitheta) Y_lm(phitheta) sin theta dtheta dphi","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Note that this notation again uses colatitudes theta, for latitudes the sintheta becomes a costheta and the bounds have to be changed accordingly to (-fracpi2fracpi2). A discretization with N grid points at location (phi_itheta_i), indexed by i can be written as [Willmert2020]","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"hata_lm = sum_i f(phi_itheta_i) Y_lm(phi_itheta_i) sin theta_i Deltatheta Deltaphi","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The hat on a just means that it is an approximation, or an estimate of the true a_lm approx hata_lm. We can essentially make use of the same symmetries as already discussed in Synthesis. Splitting into the Fourier modes e^imphi and the Legendre polynomials lambda_l^m(costheta) (which are defined over -11 so the costheta argument maps them to colatitudes) we have","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"hata_lm = sum_j left sum_i f(phi_itheta_j) e^-imphi_i right lambda_lm(theta_j) sin theta_j Deltatheta Deltaphi","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"So the term in brackets can be separated out as long as the latitude theta_j is constant, which motivates us to restrict the spectral transform to grids with iso-latitude rings, see Grids. Furthermore, this term can be written as a fast Fourier transform, if the phi_i are equally spaced on the latitude ring j. Note that the in-ring index i can depend on the ring index j, so that one can have reduced grids, which have fewer grid points towards the poles, for example. Also the Legendre polynomials only have to be computed for the colatitudes theta_j (and in fact only one hemisphere, due to the north-south symmetry discussed in the Synthesis). It is therefore practical and efficient to design a spectral transform implementation for ring grids, but there is no need to hardcode a specific grid.","category":"page"},{"location":"spectral_transform/#Spectral-packing","page":"Spherical Harmonic Transform","title":"Spectral packing","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Spectral packing is the way how the coefficients a_lm of the spherical harmonics of a given spectral field are stored in an array. SpeedyWeather.jl uses the conventional spectral packing of degree l and order m as illustrated in the following image (Cyp, CC BY-SA 3.0, via Wikimedia Commons)","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Every row represents an order l geq 0, starting from l=0 at the top. Every column represents an order m geq 0, starting from m=0 on the left. The coefficients of these spherical harmonics are directly mapped into a matrix a_lm as ","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":" m \nl a_00 \n a_10 a_11 \n a_20 a_12 a_22 \n a_30 a_13 a_23 a_33","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"which is consistently extended for higher degrees and orders. Consequently, all spectral fields are lower-triangular matrices with complex entries. The upper triangle excluding the diagonal are zero. Note that internally vector fields include an additional degree, such that l_max = m_max + 1 (see Derivatives in spherical coordinates for more information). The harmonics with a_l0 (the first column) are also called zonal harmonics as they are constant with longitude phi. The harmonics with a_ll (the main diagonal) are also called sectoral harmonics as they essentially split the sphere into 2l sectors in longitude phi without a zero-crossing in latitude.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"For correctness it is mentioned here that SpeedyWeather.jl uses a LowerTriangularMatrix type to store the spherical harmonic coefficients. By doing so, the upper triangle is actually not explicitly stored and the data technically unravelled into a vector, but this is hidden as much as possible from the user. For more details see LowerTriangularMatrices.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"info: Array indices\nFor a spectral field a note that due to Julia's 1-based indexing the coefficient a_lm is obtained via a[l+1,m+1]. Alternatively, we may index over 1-based l,m but a comment is usually added for clarification.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Fortran SPEEDY does not use the same spectral packing as SpeedyWeather.jl. The alternative packing lm therein uses l=m and m=l-m as summarized in the following table.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"degree l order m l=m m=l-m\n0 0 0 0\n1 0 0 1\n1 1 1 0\n2 0 0 2\n2 1 1 1\n2 2 2 0\n3 0 0 3\n... ... ... ...","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"This alternative packing uses the top-left triangle of a coefficient matrix, and the degrees and orders from above are stored at the following indices","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":" m \nl a_00 a_10 a_20 a_30\n a_11 a_21 a_31 \n a_22 a_32 \n a_33 ","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"This spectral packing is not used in SpeedyWeather.jl but illustrated here for completeness and comparison with Fortran SPEEDY.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"SpeedyWeather.jl uses triangular truncation such that only spherical harmonics with l leq l_max and m leq m_max are explicitly represented. This is usually described as Tm_max, with l_max = m_max (although in vector quantities require one more degree l in the recursion relation of meridional gradients). For example, T31 is the spectral resolution with l_max = m_max = 31. Note that the degree l and order m are mathematically 0-based, such that the corresponding coefficient matrix is of size 32x32.","category":"page"},{"location":"spectral_transform/#Available-horizontal-resolutions","page":"Spherical Harmonic Transform","title":"Available horizontal resolutions","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Technically, SpeedyWeather.jl supports arbitrarily chosen resolution parameter trunc when creating the SpectralGrid that refers to the highest non-zero degree l_max that is resolved in spectral space. SpeedyWeather.jl will always try to choose an easily-Fourier transformable[FFT] size of the grid, but as we use FFTW.jl there is quite some flexibility without performance sacrifice. However, this has traditionally lead to typical resolutions that we also use for testing we therefore recommend to use. They are as follows with more details below","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"trunc nlon nlat Delta x\n31 (default) 96 48 400 km\n42 128 64 312 km\n63 192 96 216 km\n85 256 128 165 km\n127 384 192 112 km\n170 512 256 85 km\n255 768 384 58 km\n341 1024 512 43 km\n511 1536 768 29 km\n682 2048 1024 22 km\n1024 3072 1536 14 km\n1365 4092 2048 11 km","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Some remarks on this table","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"This assumes the default quadratic truncation, you can always adapt the grid resolution via the dealiasing option, see Matching spectral and grid resolution\nnlat refers to the total number of latitude rings, see Grids. With non-Gaussian grids, nlat will be one one less, e.g. 47 instead of 48 rings.\nnlon is the number of longitude points on the Full Gaussian Grid, for other grids there will be at most these number of points around the Equator.\nDelta x is the horizontal resolution. For a spectral model there are many ways of estimating this[Randall2021]. We use here the square root of the average area a grid cell covers, see Effective grid resolution","category":"page"},{"location":"spectral_transform/#Effective-grid-resolution","page":"Spherical Harmonic Transform","title":"Effective grid resolution","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"There are many ways to estimate the effective grid resolution of spectral models[Randall2021]. Some of them are based on the wavelength a given spectral resolution allows to represent, others on the total number of real variables per area. However, as many atmospheric models do represent a considerable amount of physics on the grid (see Parameterizations) there is also a good argument to include the actual grid resolution into this estimate and not just the spectral resolution. We therefore use the average grid cell area to estimate the resolution","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Delta x = sqrtfrac4pi R^2N","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"with N number of grid points over a sphere with radius R. However, we have to acknowledge that this usually gives higher resolution compared to other methods of estimating the effective resolution, see [Randall2021] for a discussion. You may therefore need to be careful to make claims that, e.g. trunc=85 can resolve the atmospheric dynamics at a scale of 165km.","category":"page"},{"location":"spectral_transform/#Derivatives-in-spherical-coordinates","page":"Spherical Harmonic Transform","title":"Derivatives in spherical coordinates","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Horizontal gradients in spherical coordinates are defined for a scalar field A and the latitudes theta and longitudes lambda as","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"nabla A = left(frac1Rcosthetafracpartial Apartial lambda frac1Rfracpartial Apartial theta right)","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"However, the divergence of a vector field mathbfu = (uv) includes additional cos(theta) scalings","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"nabla cdot mathbfu = frac1Rcosthetafracpartial upartial lambda +\nfrac1Rcosthetafracpartial (v costheta)partial theta","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"and similar for the curl","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"nabla times mathbfu = frac1Rcosthetafracpartial vpartial lambda -\nfrac1Rcosthetafracpartial (u costheta)partial theta","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The radius of the sphere (i.e. Earth) is R. The zonal gradient scales with 1cos(theta) as the longitudes converge towards the poles (note that theta describes latitudes here, definitions using colatitudes replace the cos with a sin.)","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Starting with a spectral field of vorticity zeta and divergence mathcalD one can obtain stream function Psi and velocity potential Phi by inverting the Laplace operator nabla^2:","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Psi = nabla^-2zeta quad Phi = nabla^-2mathcalD","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The velocities uv are then obtained from (uv) = nabla^botPsi + nablaPhi following the definition from above and nabla^bot = (-R^-1partial_theta (Rcostheta)^-1partial_lambda)","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"beginaligned\nu = -frac1Rpartial_thetaPsi + frac1Rcosthetapartial_lambdaPhi \nv = +frac1Rpartial_thetaPhi + frac1Rcosthetapartial_lambdaPsi\nendaligned","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"How the operators nabla nabla times nabla cdot can be implemented with spherical harmonics is presented in the following sections. However, note that the actually implemented operators differ slightly in their scaling with respect to the radius R and the cosine of latitude cos(theta). For further details see Gradient operators which describes those as implemented in the SpeedyTransforms module. Also note that the equations in SpeedyWeather.jl are scaled with the radius R^2 (see Radius scaling) which turns most operators into non-dimensional operators on the unit sphere anyway.","category":"page"},{"location":"spectral_transform/#Zonal-derivative","page":"Spherical Harmonic Transform","title":"Zonal derivative","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The zonal derivative of a scalar field Psi in spectral space is the zonal derivative of all its respective spherical harmonics Psi_lm(phitheta) (now we use phi for longitudes to avoid confusion with the Legendre polynomials lambda_lm)","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"v_lm = frac1R cos(theta) fracpartialpartial phi left( lambda_l^m(costheta) e^imphi right) =\nfracimR cos(theta) lambda_l^m(costheta) e^imphi = fracimR cos(theta) Psi_lm","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"So for every spectral harmonic, cos(theta)v_lm is obtained from Psi_lm via a multiplication with imR. Unscaling the cos(theta)-factor is done after transforming the spectral coefficients v_lm into grid-point space. As discussed in Radius scaling, SpeedyWeather.jl scales the stream function as tildePsi = R^-1Psi such that the division by radius R in the gradients can be omitted. The zonal derivative becomes therefore effectively for each spherical harmonic a scaling with its (imaginary) order im. The spherical harmonics are essentially just a Fourier transform in zonal direction and the derivative a multiplication with the respective wave number m times imaginary i.","category":"page"},{"location":"spectral_transform/#Meridional-derivative","page":"Spherical Harmonic Transform","title":"Meridional derivative","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The meridional derivative of the spherical harmonics is a derivative of the Legendre polynomials for which the following recursion relation applies[Randall2021],[Durran2010],[GFDL],[Orszag70]","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"costheta fracdP_lmdtheta = -lepsilon_l+1mP_l+1m + (l+1)epsilon_lmP_l-1m","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"with recursion factors","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"epsilon_lm = sqrtfracl^2-m^24l^2-1","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"In the following we use the example of obtaining the zonal velocity u from the stream function Psi, which is through the negative meridional gradient. For the meridional derivative itself the leading minus sign has to be omitted. Starting with the spectral expansion","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Psi(lambdatheta) = sum_lmPsi_lmP_lm(sintheta)e^imlambda","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"we multiply with -R^-1costhetapartial_theta to obtain","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"costhetaleft(-frac1Rpartial_thetaPsi right) = -frac1Rsum_lmPsi_lme^imlambdacosthetapartial_theta P_lm","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"at which point the recursion from above can be applied. Collecting terms proportional to P_lm then yields","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"(cos(theta)u)_lm = -frac1R(-(l-1)epsilon_lmPsi_l-1m + (l+2)epsilon_l+1mPsi_l+1m)","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"To obtain the coefficient of each spherical harmonic lm of the meridional gradient of a spectral field, two coefficients at l-1m and l+1m have to be combined. This means that the coefficient of a gradient ((costheta) u)_lm is a linear combination of the coefficients of one higher and one lower degree Psi_l+1mPsi_l-1m. As the coefficient Psi_lm with ml are zero, the sectoral harmonics (l=m) of the gradients are obtained from the first off-diagonal only. However, the l=l_max harmonics of the gradients require the l_max-1 as well as the l_max+1 harmonics. As a consequence vector quantities like velocity components uv require one more degree l than scalar quantities like vorticity[Bourke72]. However, for easier compatibility all spectral fields in SpeedyWeather.jl use one more degree l, but scalar quantities should not make use of it. Equivalently, the last degree l is set to zero before the time integration, which only advances scalar quantities.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"In SpeedyWeather.jl, vector quantities like uv use therefore one more meridional mode than scalar quantities such as vorticity zeta or stream function Psi. The meridional derivative in SpeedyWeather.jl also omits the 1R-scaling as explained for the Zonal derivative and in Radius scaling.","category":"page"},{"location":"spectral_transform/#Divergence-and-curl-in-spherical-harmonics","page":"Spherical Harmonic Transform","title":"Divergence and curl in spherical harmonics","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The meridional gradient as described above can be applied to scalars, such as Psi and Phi in the conversion to velocities (uv) = nabla^botPsi + nablaPhi, however, the operators curl nabla times and divergence nabla cdot in spherical coordinates involve a costheta scaling before the meridional gradient is applied. How to translate this to spectral coefficients has to be derived separately[Randall2021],[Durran2010].","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The spectral transform of vorticity zeta is","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"zeta_lm = frac12piint_-tfracpi2^tfracpi2int_0^2pi zeta(lambdatheta)\nP_lm(sintheta) e^imlambda dlambda costheta dtheta","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Given that Rzeta = cos^-1partial_lambda v - cos^-1partial_theta (u costheta), we therefore have to evaluate a meridional integral of the form","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"int P_lm frac1cos theta partial_theta(u costheta) cos theta dtheta","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"which can be solved through integration by parts. As ucostheta = 0 at theta = pm tfracpi2 only the integral","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"= -int partial_theta P_lm (u costheta) dtheta = -int costheta partial_theta P_lm\n(fracucostheta) costheta dtheta","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"remains. Inserting the recurrence relation from the Meridional derivative turns this into","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"= -int left(-l epsilon_l+1mP_l+1m + (l+1)epsilon_lm P_l-1m right) (fracucostheta)\ncos theta dtheta","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"Now we expand (tfracucostheta) but only the lm harmonic will project ontoP_lm. Let u^* = ucos^-1theta v^* = vcos^-1theta we then have in total","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"beginaligned\nRzeta_lm = imv^*_lm + (l+1)epsilon_lmu^*_l-1m - lepsilon_l+1mu^*_l+1m \nRD_lm = imu^*_lm - (l+1)epsilon_lmv^*_l-1m + lepsilon_l+1mv^*_l+1m \nendaligned","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"And the divergence D is similar, but (uv) to (-vu). We have moved the scaling with the radius R directly into zetaD as further described in Radius scaling.","category":"page"},{"location":"spectral_transform/#Laplacian","page":"Spherical Harmonic Transform","title":"Laplacian","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"The spectral Laplacian is easily applied to the coefficients Psi_lm of a spectral field as the spherical harmonics are eigenfunctions of the Laplace operator nabla^2 in spherical coordinates with eigenvalues -l(l+1) divided by the radius squared R^2, i.e. nabla^2 Psi becomes tfrac-l(l+1)R^2Psi_lm in spectral space. For example, vorticity zeta and stream function Psi are related by zeta = nabla^2Psi in the barotropic vorticity model. Hence, in spectral space this is equivalent for every spectral mode of degree l and order m to","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"zeta_lm = frac-l(l+1)R^2Psi_lm","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"This can be easily inverted to obtain the stream function Psi from vorticity zeta instead. In order to avoid division by zero, we set Psi_00 here, given that the stream function is only defined up to a constant anyway.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"beginaligned\nPsi_lm = fracR^2-l(l+1)zeta_lm quad foralllm 0\nPsi_00 = 0\nendaligned","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"See also Horizontal diffusion and Normalization of diffusion.","category":"page"},{"location":"spectral_transform/#U,V-from-vorticity-and-divergence","page":"Spherical Harmonic Transform","title":"U,V from vorticity and divergence","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"After having discussed the zonal and meridional derivatives with spherical harmonics as well as the Laplace operator, we can derive the conversion from vorticity zeta and divergence D (which are prognostic variables) to U=ucostheta V=vcostheta. Both are linear operations that act either solely on a given harmonic (the zonal gradient and the Laplace operator) or are linear combinations between one lower and one higher degree l (the meridional gradient). It is therefore computationally more efficient to compute UV directly from zetaD instead of calculating stream function and velocity potential first. In total we have","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"beginaligned\nU_lm = -fraciml(l+1)(RD)_lm + fracepsilon_l+1ml+1(Rzeta)_l+1m -\nfracepsilon_lml(Rzeta)_l-1m \nV_lm = -fraciml(l+1)(Rzeta)_lm - fracepsilon_l+1ml+1(RD)_l+1m +\nfracepsilon_lml(RD)_l-1m \nendaligned","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"We have moved the scaling with the radius R directly into zetaD as further described in Radius scaling.","category":"page"},{"location":"spectral_transform/#References","page":"Spherical Harmonic Transform","title":"References","text":"","category":"section"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"[Malardel2016]: Malardel S, Wedi N, Deconinck W, Diamantakis M, Kühnlein C, Mozdzynski G, Hamrud M, Smolarkiewicz P. A new grid for the IFS. ECMWF newsletter. 2016;146(23-28):321. doi: 10.21957/zwdu9u5i","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"[Gorski2004]: Górski, Hivon, Banday, Wandelt, Hansen, Reinecke, Bartelmann, 2004. HEALPix: A FRAMEWORK FOR HIGH-RESOLUTION DISCRETIZATION AND FAST ANALYSIS OF DATA DISTRIBUTED ON THE SPHERE, The Astrophysical Journal. doi:10.1086/427976","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"[Willmert2020]: Justin Willmert, 2020. justinwillmert.comIntroduction to Associated Legendre Polynomials (Legendre.jl Series, Part I)\nCalculating Legendre Polynomials (Legendre.jl Series, Part II)\nPre-normalizing Legendre Polynomials (Legendre.jl Series, Part III)\nMaintaining numerical accuracy in the Legendre recurrences (Legendre.jl Series, Part IV)\nIntroducing Legendre.jl (Legendre.jl Series, Part V)\nNumerical Accuracy of the Spherical Harmonic Recurrence Coefficient (Legendre.jl Series Addendum)\nNotes on Calculating the Spherical Harmonics\nMore Notes on Calculating the Spherical Harmonics: Analysis of maps to harmonic coefficients","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"[Daley78]: Roger Daley & Yvon Bourassa (1978) Rhomboidal versus triangular spherical harmonic truncation: Some verification statistics, Atmosphere-Ocean, 16:2, 187-196, DOI: 10.1080/07055900.1978.9649026","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"[Randall2021]: David Randall, 2021. An Introduction to Numerical Modeling of the Atmosphere, Chapter 22.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"[Durran2010]: Dale Durran, 2010. Numerical Methods for Fluid Dynamics, Springer. In particular section 6.2, 6.4.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"[GFDL]: Geophysical Fluid Dynamics Laboratory, The barotropic vorticity equation.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"[FFT]: Depending on the implementation of the Fast Fourier Transform (Cooley-Tukey algorithm, or or the Bluestein algorithm) easily Fourier-transformable can mean different things: Vectors of the length n that is a power of two, i.e. n = 2^i is certainly easily Fourier-transformable, but for most FFT implementations so are n = 2^i3^j5^k with ijk some positive integers. In fact, FFTW uses O(n log n) algorithms even for prime sizes.","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"[Bourke72]: Bourke, W. An Efficient, One-Level, Primitive-Equation Spectral Model. Mon. Wea. Rev. 100, 683–689 (1972). doi:10.1175/1520-0493(1972)100<0683:AEOPSM>2.3.CO;2","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"[Orszag70]: Orszag, S. A., 1970: Transform Method for the Calculation of Vector-Coupled Sums: Application to the Spectral Form of the Vorticity Equation. J. Atmos. Sci., 27, 890–895, 10.1175/1520-0469(1970)027<0890:TMFTCO>2.0.CO;2. ","category":"page"},{"location":"ringgrids/#RingGrids","page":"Submodule: RingGrids","title":"RingGrids","text":"","category":"section"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"RingGrids defines and exports the following grids:","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"full grids: FullClenshawGrid, FullGaussianGrid, FullHEALPix, and FullOctaHEALPix\nreduced grids: OctahedralGaussianGrid, OctahedralClenshawGrid, OctaHEALPixGrid and HEALPixGrid","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"note: What is a ring?\nWe 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.","category":"page"},{"location":"ringgrids/#Creating-data-on-a-RingGrid","page":"Submodule: RingGrids","title":"Creating data on a RingGrid","text":"","category":"section"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"Data in a Matrix which follows this ring order can be put on a FullGaussianGrid like so","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"using SpeedyWeather.RingGrids\nmap = randn(Float32,8,4)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"grid = FullGaussianGrid(map)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"grid.data","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"Which can be reshaped to reobtain map from above. Alternatively you can Matrix(grid) to do this in one step","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"map == Matrix(FullGaussianGrid(map))","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"nlat_half = 4\ngrid = randn(OctahedralGaussianGrid{Float16},nlat_half)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"and any element type T can be used for OctahedralGaussianGrid{T} and similar for other grid types.","category":"page"},{"location":"ringgrids/#Visualising-RingGrid-data","page":"Submodule: RingGrids","title":"Visualising RingGrid data","text":"","category":"section"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"nlat_half = 24\ngrid = randn(OctahedralGaussianGrid,nlat_half)\nplot(grid)","category":"page"},{"location":"ringgrids/#Indexing-RingGrids","page":"Submodule: RingGrids","title":"Indexing RingGrids","text":"","category":"section"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"grid = randn(OctahedralClenshawGrid,5)\nlatd = get_latd(grid)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"Now we could calculate Coriolis and add it on the grid as follows","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"rotation = 7.29e-5 # angular frequency of Earth's rotation [rad/s]\ncoriolis = 2rotation*sind.(latd) # vector of coriolis parameters per latitude ring\n\nrings = eachring(grid)\nfor (j,ring) in enumerate(rings)\n f = coriolis[j]\n for ij in ring\n grid[ij] += f\n end\nend","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"for ij in eachgridpoint(grid)\n grid[ij]\nend","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"or use eachindex instead.","category":"page"},{"location":"ringgrids/#Interpolation-on-RingGrids","page":"Submodule: RingGrids","title":"Interpolation on RingGrids","text":"","category":"section"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"grid = randn(OctahedralGaussianGrid{Float32},4)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"interpolate(FullGaussianGrid,grid)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"interpolate(FullGaussianGrid,6,grid)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"One can also interpolate onto a given coordinate ˚N, ˚E like so","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"interpolate(30.0,10.0,grid)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"interpolate([30.0,40.0,50.0],[10.0,10.0,10.0],grid)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/#Performance-for-RingGrid-interpolation","page":"Submodule: RingGrids","title":"Performance for RingGrid interpolation","text":"","category":"section"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"output vector\ninput grid\ninterpolator","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"grid_in = rand(HEALPixGrid,4)\ngrid_out = zeros(FullClenshawGrid,6)\ninterp = RingGrids.interpolator(grid_out,grid_in)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"interpolate!(grid_out,grid_in,interp)\ngrid_out","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"grid_out = zeros(FullClenshawGrid{Float16},6);\ninterpolate!(grid_out,grid_in,interp)\ngrid_out","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"grid_in = randn(OctahedralGaussianGrid{Float16},24)\ngrid_out = zeros(FullClenshawGrid{Float16},24)\ninterp = RingGrids.interpolator(Float32,grid_out,grid_in)\ninterpolate!(grid_out,grid_in,interp)\ngrid_out","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"npoints = 10 # number of coordinates to interpolate onto\ninterp = AnvilInterpolator(Float32,HEALPixGrid,24,npoints)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"latds = collect(0.0:5.0:45.0)\nlonds = collect(-10.0:2.0:8.0)\nnothing # hide","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"now we can update the locator inside our interpolator as follows","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"RingGrids.update_locator!(interp,latds,londs)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"With data matching the input from above, a nlat_half=24 HEALPixGrid, and allocate 10-element output vector","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"output_vec = zeros(10)\ngrid_input = rand(HEALPixGrid,24)\nnothing # hide","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"we can use the interpolator as follows","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"interpolate!(output_vec,grid_input,interp)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"which is the approximately the same as doing it directly without creating an interpolator first and updating its locator","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"interpolate(latds,londs,grid_input)","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/#Anvil-interpolator","page":"Submodule: RingGrids","title":"Anvil interpolator","text":"","category":"section"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":" 0..............1 # fraction of distance Δab between a,b\n |< Δab >|\n\n0^ a -------- o - b # anvil-shaped average of a,b,c,d at location x\n.Δy |\n. |\n.v x \n. |\n1 c ------ o ---- d\n\n |< Δcd >|\n 0...............1 # fraction of distance Δcd between c,d\n\n^ fraction of distance Δy between a-b and c-d.","category":"page"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"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.","category":"page"},{"location":"ringgrids/#Function-index","page":"Submodule: RingGrids","title":"Function index","text":"","category":"section"},{"location":"ringgrids/","page":"Submodule: RingGrids","title":"Submodule: RingGrids","text":"Modules = [SpeedyWeather.RingGrids]","category":"page"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractFullGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.AbstractFullGrid","text":"abstract type AbstractFullGrid{T} <: AbstractGrid{T} end\n\nAn 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.AbstractGrid","text":"abstract type AbstractGrid{T} <: AbstractVector{T} end\n\nThe abstract supertype for all spatial grids on the sphere supported by SpeedyWeather.jl. Every new grid has to be of the form\n\nabstract type AbstractGridClass{T} <: AbstractGrid{T} end\nstruct MyNewGrid{T} <: AbstractGridClass{T}\n data::Vector{T} # all grid points unravelled into a vector\n nlat_half::Int # resolution: latitude rings on one hemisphere (Equator incl)\nend\n\nMyNewGrid 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)\n\nFundamental 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\n\nIndexing 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\n\nCoordinates getcolat # vector of colatitudes (radians) getcolatlon # vectors of colatitudes, longitudes (both radians)\n\nSpectral 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\n\nQuadrature 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\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractHEALPixGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.AbstractHEALPixGrid","text":"abstract type AbstractHEALPixGrid{T} <: AbstractGrid{T} end\n\nAn AbstractHEALPixGrid is a horizontal grid similar to the standard HEALPixGrid, but different latitudes can be used, the default HEALPix latitudes or others.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractInterpolator","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.AbstractInterpolator","text":"abstract type AbstractInterpolator{NF,G} end\n\nSupertype for Interpolators. Every Interpolator <: AbstractInterpolator is expected to have two fields,\n\ngeometry, which describes the grid G to interpolate from\nlocator, which locates the indices on G and their weights to interpolate onto a new grid.\n\nNF 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractLocator","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.AbstractLocator","text":"AbstractLocator{NF}\n\nSupertype 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractOctaHEALPixGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.AbstractOctaHEALPixGrid","text":"abstract type AbstractOctaHEALPixGrid{T} <: AbstractGrid{T} end\n\nAn 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractOctahedralGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.AbstractOctahedralGrid","text":"abstract type AbstractOctahedralGrid{T} <: AbstractGrid{T} end\n\nAn 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AnvilLocator","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.AnvilLocator","text":"AnvilLocator{NF<:AbstractFloat} <: AbtractLocator\n\nContains 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AnvilLocator-Union{Tuple{Integer}, Tuple{NF}} where NF<:AbstractFloat","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.AnvilLocator","text":"L = AnvilLocator( ::Type{NF}, # number format used for the interpolation\n npoints::Integer # number of points to interpolate onto\n ) where {NF<:AbstractFloat}\n\nZero 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.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.FullClenshawGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.FullClenshawGrid","text":"G = FullClenshawGrid{T}\n\nA 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.FullGaussianGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.FullGaussianGrid","text":"G = FullGaussianGrid{T}\n\nA 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.FullHEALPixGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.FullHEALPixGrid","text":"G = FullHEALPixGrid{T}\n\nA 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.FullOctaHEALPixGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.FullOctaHEALPixGrid","text":"G = FullOctaHEALPixGrid{T}\n\nA 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.GridGeometry","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.GridGeometry","text":"GridGeometry{G<:AbstractGrid}\n\ncontains general precomputed arrays describing the grid of G.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.GridGeometry-Tuple{Type{<:SpeedyWeather.RingGrids.AbstractGrid}, Integer}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.GridGeometry","text":"G = GridGeometry( Grid::Type{<:AbstractGrid},\n nlat_half::Integer)\n\nPrecomputed 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.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.HEALPixGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.HEALPixGrid","text":"H = HEALPixGrid{T}\n\nA 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.OctaHEALPixGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.OctaHEALPixGrid","text":"H = OctaHEALPixGrid{T}\n\nA 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.OctahedralClenshawGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.OctahedralClenshawGrid","text":"G = OctahedralClenshawGrid{T}\n\nAn 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.OctahedralGaussianGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.OctahedralGaussianGrid","text":"G = OctahedralGaussianGrid{T}\n\nAn 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.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.Matrix!-Tuple{AbstractMatrix, OctaHEALPixGrid}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.Matrix!","text":"Matrix!(M::AbstractMatrix,\n G::OctaHEALPixGrid;\n quadrant_rotation=(0,1,2,3),\n matrix_quadrant=((2,2),(1,2),(1,1),(2,1)),\n )\n\nSorts 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.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.Matrix!-Tuple{AbstractMatrix, OctahedralClenshawGrid}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.Matrix!","text":"Matrix!(M::AbstractMatrix,\n G::OctahedralClenshawGrid;\n quadrant_rotation=(0,1,2,3),\n matrix_quadrant=((2,2),(1,2),(1,1),(2,1)),\n )\n\nSorts 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.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.Matrix!-Union{Tuple{Vararg{Tuple{AbstractMatrix{T}, OctaHEALPixGrid}}}, Tuple{T}} where T","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.Matrix!","text":"Matrix!(MGs::Tuple{AbstractMatrix{T},OctaHEALPixGrid}...;kwargs...)\n\nLike 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.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.Matrix!-Union{Tuple{Vararg{Tuple{AbstractMatrix{T}, OctahedralClenshawGrid}}}, Tuple{T}} where T","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.Matrix!","text":"Matrix!(MGs::Tuple{AbstractMatrix{T},OctahedralClenshawGrid}...;kwargs...)\n\nLike 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.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids._scale_lat!-Union{Tuple{NF}, Tuple{SpeedyWeather.RingGrids.AbstractGrid{NF}, AbstractVector}} where NF","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids._scale_lat!","text":"_scale_lat!(\n A::SpeedyWeather.RingGrids.AbstractGrid{NF},\n v::AbstractVector\n) -> SpeedyWeather.RingGrids.AbstractGrid\n\n\nGeneric latitude scaling applied to A in-place with latitude-like vector v.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.anvil_average-NTuple{7, Any}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.anvil_average","text":"anvil_average(a, b, c, d, Δab, Δcd, Δy) -> Any\n\n\nThe 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:\n\n 0..............1 # fraction of distance Δab between a,b\n |< Δab >|\n\n 0^ a -------- o - b # anvil-shaped average of a,b,c,d at location x\n .Δy |\n . |\n .v x \n . |\n 1 c ------ o ---- d\n\n |< Δcd >|\n 0...............1 # fraction of distance Δcd between c,d\n\n^ fraction of distance Δy between a-b and c-d.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.average_on_poles-Union{Tuple{NF}, Tuple{SpeedyWeather.RingGrids.AbstractGrid{NF}, Vector{<:UnitRange{<:Integer}}}} where NF<:AbstractFloat","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.average_on_poles","text":"average_on_poles(\n A::SpeedyWeather.RingGrids.AbstractGrid{NF<:AbstractFloat},\n rings::Vector{<:UnitRange{<:Integer}}\n) -> Tuple{Any, Any}\n\n\nComputes 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.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.average_on_poles-Union{Tuple{NF}, Tuple{SpeedyWeather.RingGrids.AbstractGrid{NF}, Vector{<:UnitRange{<:Integer}}}} where NF<:Integer","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.average_on_poles","text":"average_on_poles(\n A::SpeedyWeather.RingGrids.AbstractGrid{NF<:Integer},\n rings::Vector{<:UnitRange{<:Integer}}\n) -> Tuple{Any, Any}\n\n\nMethod for A::Abstract{T<:Integer} which rounds the averaged values to return the same number format NF.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.each_index_in_ring-Union{Tuple{Grid}, Tuple{Grid, Integer}} where Grid<:SpeedyWeather.RingGrids.AbstractGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.each_index_in_ring","text":"i = each_index_in_ring(grid,j)\n\nUnitRange i to access data on grid grid on ring j.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.eachgridpoint-Tuple{SpeedyWeather.RingGrids.AbstractGrid}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.eachgridpoint","text":"ijs = eachgridpoint(grid)\n\nUnitRange ijs to access each grid point on grid grid.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.eachring-Tuple{SpeedyWeather.RingGrids.AbstractGrid}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.eachring","text":"eachring(grid::SpeedyWeather.RingGrids.AbstractGrid) -> Any\n\n\nVector{UnitRange} rings to loop over every ring of grid grid and then each grid point per ring. To be used like\n\nrings = eachring(grid)\nfor ring in rings\n for ij in ring\n grid[ij]\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.eachring-Union{Tuple{Grid}, Tuple{Grid, Vararg{Grid}}} where Grid<:SpeedyWeather.RingGrids.AbstractGrid","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.eachring","text":"eachring(\n grid1::SpeedyWeather.RingGrids.AbstractGrid,\n grids::Grid<:SpeedyWeather.RingGrids.AbstractGrid...\n) -> Any\n\n\nSame as eachring(grid) but performs a bounds check to assess that all grids in grids are of same size.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.extrema_in-Tuple{Vector, Real, Real}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.extrema_in","text":"true/false = extrema_in(v::Vector,a::Real,b::Real)\n\nFor every element vᵢ in v does a<=vi<=b hold?\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_nlons-Tuple{Type{<:SpeedyWeather.RingGrids.AbstractGrid}, Integer}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.get_nlons","text":"get_nlons(\n Grid::Type{<:SpeedyWeather.RingGrids.AbstractGrid},\n nlat_half::Integer;\n both_hemispheres\n) -> Any\n\n\nReturns 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.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.grid_cell_average!-Tuple{SpeedyWeather.RingGrids.AbstractGrid, SpeedyWeather.RingGrids.AbstractFullGrid}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.grid_cell_average!","text":"grid_cell_average!(\n output::SpeedyWeather.RingGrids.AbstractGrid,\n input::SpeedyWeather.RingGrids.AbstractFullGrid\n) -> SpeedyWeather.RingGrids.AbstractGrid\n\n\nAverages 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.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.grid_cell_average-Tuple{Type{<:SpeedyWeather.RingGrids.AbstractGrid}, Integer, SpeedyWeather.RingGrids.AbstractFullGrid}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.grid_cell_average","text":"grid_cell_average(\n Grid::Type{<:SpeedyWeather.RingGrids.AbstractGrid},\n nlat_half::Integer,\n input::SpeedyWeather.RingGrids.AbstractFullGrid\n) -> Any\n\n\nAverages 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.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.isdecreasing-Tuple{Vector}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.isdecreasing","text":"true/false = isdecreasing(v::Vector)\n\nCheck whether elements of a vector v are strictly decreasing.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.isincreasing-Tuple{Vector}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.isincreasing","text":"true/false = isincreasing(v::Vector)\n\nCheck whether elements of a vector v are strictly increasing.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.rotate_matrix_indices_180-Tuple{Integer, Integer, Integer}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.rotate_matrix_indices_180","text":"i_new,j_new = rotate_matrix_indices_180(i,j,s)\n\nRotate indices i,j of a square matrix of size s x s by 180˚.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.rotate_matrix_indices_270-Tuple{Integer, Integer, Integer}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.rotate_matrix_indices_270","text":"i_new,j_new = rotate_matrix_indices_270(i,j,s)\n\nRotate indices i,j of a square matrix of size s x s anti-clockwise by 270˚.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.rotate_matrix_indices_90-Tuple{Integer, Integer, Integer}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.rotate_matrix_indices_90","text":"i_new,j_new = rotate_matrix_indices_90(i,j,s)\n\nRotate indices i,j of a square matrix of size s x s anti-clockwise by 90˚.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.whichring-Tuple{Integer, Vector{UnitRange{Int64}}}","page":"Submodule: RingGrids","title":"SpeedyWeather.RingGrids.whichring","text":"whichring(\n ij::Integer,\n rings::Vector{UnitRange{Int64}}\n) -> Int64\n\n\nObtain ring index j from gridpoint ij and Vector{UnitRange} describing rind indices as obtained from eachring(::Grid)\n\n\n\n\n\n","category":"method"},{"location":"#SpeedyWeather.jl-documentation","page":"Home","title":"SpeedyWeather.jl documentation","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Welcome to the documentation for SpeedyWeather.jl a global atmospheric circulation model with simple parametrizations to represent physical processes such as clouds, precipitation and radiation. SpeedyWeather in general is more a library than just a model as it exposes most of its internal functions to the user such that simulations and analysis can be interactively combined. Its user interface is built in a very modular way such that new components can be easily defined and integrated into SpeedyWeather.","category":"page"},{"location":"#Overview","page":"Home","title":"Overview","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"SpeedyWeather.jl is uses a spherical harmonic transform to simulate the general circulation of the atmosphere using a vorticity-divergence formulation, a semi-implicit time integration and simple parameterizations to represent various climate processes: Radiation, clouds, precipitation, surface fluxes, among others.","category":"page"},{"location":"","page":"Home","title":"Home","text":"SpeedyWeather.jl defines ","category":"page"},{"location":"","page":"Home","title":"Home","text":"BarotropicModel for the 2D barotropic vorticity equation\nShallowWaterModel for the 2D shallow water equations\nPrimitiveDryModel for the 3D primitive equations without humidity\nPrimitiveWetModel for the 3D primitive equations with humidity","category":"page"},{"location":"","page":"Home","title":"Home","text":"and solves these equations in spherical coordinates as described in this documentation.","category":"page"},{"location":"#Manual-outline","page":"Home","title":"Manual outline","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"See the following pages of the documentation for more details","category":"page"},{"location":"","page":"Home","title":"Home","text":"Installation\nHow to run SpeedyWeather.jl\nModel setups\nSpherical harmonic transform\nGrids\nBarotropic model\nShallow water model\nPrimitive equation model\nParameterizations\nExtending SpeedyWeather\nNetCDF output","category":"page"},{"location":"","page":"Home","title":"Home","text":"and the submodules","category":"page"},{"location":"","page":"Home","title":"Home","text":"RingGrids\nLowerTriangularMatrices \nSpeedyTransforms","category":"page"},{"location":"","page":"Home","title":"Home","text":"and the original documentation by Molteni and Kucharski.","category":"page"},{"location":"#Developers","page":"Home","title":"Developers","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"The development of SpeedyWeather.jl is lead by Milan Klöwer and current and past contributors include","category":"page"},{"location":"","page":"Home","title":"Home","text":"Tom Kimpson\nAlistair White\nMaximilian Gelbrecht\nDavid Meyer\nDaisuke Hotta\nNavid Constantinou\nSimone Silvestri","category":"page"},{"location":"","page":"Home","title":"Home","text":"Any contributions are always welcome!","category":"page"},{"location":"#Funding","page":"Home","title":"Funding","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"MK received funding by the European Research Council under Horizon 2020 within the ITHACA project, grant agreement number 741112 from 2021-2022. Since 2023 this project is also funded by the National Science Foundation NSF.","category":"page"}] } diff --git a/previews/PR387/setups/index.html b/previews/PR387/setups/index.html index 8814391a0..3464f0c6e 100644 --- a/previews/PR387/setups/index.html +++ b/previews/PR387/setups/index.html @@ -221,4 +221,4 @@ ax.pcolormesh(lon,lat,vor') ax.set_xlabel("longitude") ax.set_ylabel("latitude") -ax.set_title("Surface relative vorticity")

      Jablonowski pyplot

      References

      +ax.set_title("Surface relative vorticity")

      Jablonowski pyplot

      References

      diff --git a/previews/PR387/shallowwater/index.html b/previews/PR387/shallowwater/index.html index 4e5cab8ac..ea89c6f48 100644 --- a/previews/PR387/shallowwater/index.html +++ b/previews/PR387/shallowwater/index.html @@ -34,4 +34,4 @@ (-1)^{n+1}\tilde{\nu}\tilde{\nabla}^{2n}\tilde{\mathcal{D}} \\ \frac{\partial \eta}{\partial \tilde{t}} &= - \tilde{\nabla} \cdot (\mathbf{u}h) + \tilde{F}_\eta. -\end{aligned}\]

      As in the scaled barotropic vorticity equations, one needs to scale the time step, the Coriolis force, the forcing and the diffusion coefficient, but then enjoys the luxury of working with dimensionless gradient operators. As before, SpeedyWeather.jl will scale vorticity and divergence just before the model integration starts and unscale them upon completion and for output. In the semi-implicit time integration we solve an equation that also has to be scaled. It is with radius squared scaling (because it is the tendency for the divergence equation which is also scaled with $R^2$)

      \[R^2 \delta D = R^2\frac{G_\mathcal{D} - \xi g\nabla^2 G_\eta}{1 - \xi^2 H \nabla^2}\]

      As $G_\eta$ is only scaled with $R$ we have

      \[\tilde{\delta D} = \frac{\tilde{G_\mathcal{D}} - \tilde{\xi} g\tilde{\nabla}^2 \tilde{G_\eta}}{1 - \tilde{\xi}^2 H \tilde{\nabla}^2}\]

      The $R^2$ normalizes the Laplace operator in the numerator, but using the scaled $G_\eta$ we also scale $\xi$ (which is convenient, because the time step within is the one we use anyway). The denominator $S$ does not actually change because $\xi^2\nabla^2 = \tilde{\xi}^2\tilde{\nabla}^2$ as $\xi^2$ is scaled with $1/R^2$, but the Laplace operator with $R^2$. So overall we just have to use the scaled time step $\tilde{\Delta t}$ and normalized eigenvalues for $\tilde{\nabla}^2$.

      References

      +\end{aligned}\]

      As in the scaled barotropic vorticity equations, one needs to scale the time step, the Coriolis force, the forcing and the diffusion coefficient, but then enjoys the luxury of working with dimensionless gradient operators. As before, SpeedyWeather.jl will scale vorticity and divergence just before the model integration starts and unscale them upon completion and for output. In the semi-implicit time integration we solve an equation that also has to be scaled. It is with radius squared scaling (because it is the tendency for the divergence equation which is also scaled with $R^2$)

      \[R^2 \delta D = R^2\frac{G_\mathcal{D} - \xi g\nabla^2 G_\eta}{1 - \xi^2 H \nabla^2}\]

      As $G_\eta$ is only scaled with $R$ we have

      \[\tilde{\delta D} = \frac{\tilde{G_\mathcal{D}} - \tilde{\xi} g\tilde{\nabla}^2 \tilde{G_\eta}}{1 - \tilde{\xi}^2 H \tilde{\nabla}^2}\]

      The $R^2$ normalizes the Laplace operator in the numerator, but using the scaled $G_\eta$ we also scale $\xi$ (which is convenient, because the time step within is the one we use anyway). The denominator $S$ does not actually change because $\xi^2\nabla^2 = \tilde{\xi}^2\tilde{\nabla}^2$ as $\xi^2$ is scaled with $1/R^2$, but the Laplace operator with $R^2$. So overall we just have to use the scaled time step $\tilde{\Delta t}$ and normalized eigenvalues for $\tilde{\nabla}^2$.

      References

      diff --git a/previews/PR387/spectral_transform/index.html b/previews/PR387/spectral_transform/index.html index f41285664..32a7e8a27 100644 --- a/previews/PR387/spectral_transform/index.html +++ b/previews/PR387/spectral_transform/index.html @@ -19,4 +19,4 @@ \frac{\epsilon_{l,m}}{l}(R\zeta)_{l-1,m} \\ V_{l,m} &= -\frac{im}{l(l+1)}(R\zeta)_{l,m} - \frac{\epsilon_{l+1,m}}{l+1}(RD)_{l+1,m} + \frac{\epsilon_{l,m}}{l}(RD)_{l-1,m} \\ -\end{aligned}\]

      We have moved the scaling with the radius $R$ directly into $\zeta,D$ as further described in Radius scaling.

      References

      +\end{aligned}\]

      We have moved the scaling with the radius $R$ directly into $\zeta,D$ as further described in Radius scaling.

      References

      diff --git a/previews/PR387/speedytransforms/index.html b/previews/PR387/speedytransforms/index.html index 1e16b21e5..59c448d8e 100644 --- a/previews/PR387/speedytransforms/index.html +++ b/previews/PR387/speedytransforms/index.html @@ -263,9 +263,9 @@ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄ -90 ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ └──┘ └────────────────────────────────────────────────────────────┘ -600 - 0 ˚E 360

      Strikingly similar! The remaining differences are the ageostrophic motions but also note that the mean is off. This is because geostrophy only use/defines the gradient of $\eta$ not the absolute values itself. Our geostrophic $\eta_g$ has by construction a mean of zero (that is how we define the inverse Laplace operator) but the actual $\eta$ is some 1400m higher.

      Functions and type index

      SpeedyWeather.SpeedyTransforms.SpectralTransformType
      S = SpectralTransform{NF<:AbstractFloat}(...)

      SpectralTransform struct that contains all parameters and preallocated arrays for the spectral transform.

      source
      SpeedyWeather.SpeedyTransforms.SpectralTransformMethod
      S = SpectralTransform(  alms::AbstractMatrix{Complex{NF}};
      +        0                           ˚E                           360       

      Strikingly similar! The remaining differences are the ageostrophic motions but also note that the mean is off. This is because geostrophy only use/defines the gradient of $\eta$ not the absolute values itself. Our geostrophic $\eta_g$ has by construction a mean of zero (that is how we define the inverse Laplace operator) but the actual $\eta$ is some 1400m higher.

      Functions and type index

      SpeedyWeather.SpeedyTransforms.SpectralTransformMethod
      S = SpectralTransform(  alms::AbstractMatrix{Complex{NF}};
                               recompute_legendre::Bool=true,
      -                        Grid::Type{<:AbstractGrid}=DEFAULT_GRID)

      Generator function for a SpectralTransform struct based on the size of the spectral coefficients alms and the grid Grid. Recomputes the Legendre polynomials by default.

      source
      SpeedyWeather.SpeedyTransforms.SpectralTransformMethod
      SpectralTransform(
      +                        Grid::Type{<:AbstractGrid}=DEFAULT_GRID)

      Generator function for a SpectralTransform struct based on the size of the spectral coefficients alms and the grid Grid. Recomputes the Legendre polynomials by default.

      source
      SpeedyWeather.SpeedyTransforms.SpectralTransformMethod
      SpectralTransform(
           ::Type{NF},
           Grid::Type{<:SpeedyWeather.RingGrids.AbstractGrid},
           lmax::Int64,
      @@ -274,30 +274,30 @@
           legendre_shortcut,
           dealiasing
       ) -> SpectralTransform
      -

      Generator function for a SpectralTransform struct. With NF the number format, Grid the grid type <:AbstractGrid and spectral truncation lmax,mmax this function sets up necessary constants for the spetral transform. Also plans the Fourier transforms, retrieves the colatitudes, and preallocates the Legendre polynomials (if recompute_legendre == false) and quadrature weights.

      source
      SpeedyWeather.SpeedyTransforms.SpectralTransformMethod
      S = SpectralTransform(  map::AbstractGrid;
      -                        recompute_legendre::Bool=true)

      Generator function for a SpectralTransform struct based on the size and grid type of gridded field map. Recomputes the Legendre polynomials by default.

      source
      SpeedyWeather.RingGrids.get_nlat_halfFunction
      get_nlat_half(trunc::Integer) -> Any
      +

      Generator function for a SpectralTransform struct. With NF the number format, Grid the grid type <:AbstractGrid and spectral truncation lmax,mmax this function sets up necessary constants for the spetral transform. Also plans the Fourier transforms, retrieves the colatitudes, and preallocates the Legendre polynomials (if recompute_legendre == false) and quadrature weights.

      source
      SpeedyWeather.SpeedyTransforms.SpectralTransformMethod
      S = SpectralTransform(  map::AbstractGrid;
      +                        recompute_legendre::Bool=true)

      Generator function for a SpectralTransform struct based on the size and grid type of gridded field map. Recomputes the Legendre polynomials by default.

      source
      SpeedyWeather.RingGrids.get_nlat_halfFunction
      get_nlat_half(trunc::Integer) -> Any
       get_nlat_half(trunc::Integer, dealiasing::Real) -> Any
      -

      For the spectral truncation trunc (e.g. 31 for T31) return the grid resolution parameter nlat_half (number of latitude rings on one hemisphere including the Equator) following a dealiasing parameter (default 2) to match spectral and grid resolution.

      source
      SpeedyWeather.SpeedyTransforms.UV_from_vor!Method
      UV_from_vor!(
      +

      For the spectral truncation trunc (e.g. 31 for T31) return the grid resolution parameter nlat_half (number of latitude rings on one hemisphere including the Equator) following a dealiasing parameter (default 2) to match spectral and grid resolution.

      source
      SpeedyWeather.SpeedyTransforms.UV_from_vor!Method
      UV_from_vor!(
           U::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},
           V::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},
           vor::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},
           S::SpectralTransform{NF<:AbstractFloat}
       )
      -

      Get U,V (=(u,v)*coslat) from vorticity ζ spectral space (divergence D=0) Two operations are combined into a single linear operation. First, invert the spherical Laplace ∇² operator to get stream function from vorticity. Then compute zonal and meridional gradients to get U,V.

      source
      SpeedyWeather.SpeedyTransforms.UV_from_vordiv!Method
      UV_from_vordiv!(
      +

      Get U,V (=(u,v)*coslat) from vorticity ζ spectral space (divergence D=0) Two operations are combined into a single linear operation. First, invert the spherical Laplace ∇² operator to get stream function from vorticity. Then compute zonal and meridional gradients to get U,V.

      source
      SpeedyWeather.SpeedyTransforms.UV_from_vordiv!Method
      UV_from_vordiv!(
           U::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},
           V::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},
           vor::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},
           div::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},
           S::SpectralTransform{NF<:AbstractFloat}
       ) -> Any
      -

      Get U,V (=(u,v)*coslat) from vorticity ζ and divergence D in spectral space. Two operations are combined into a single linear operation. First, invert the spherical Laplace ∇² operator to get stream function from vorticity and velocity potential from divergence. Then compute zonal and meridional gradients to get U,V.

      source
      SpeedyWeather.SpeedyTransforms._divergence!Method
      _divergence!(
      +

      Get U,V (=(u,v)*coslat) from vorticity ζ and divergence D in spectral space. Two operations are combined into a single linear operation. First, invert the spherical Laplace ∇² operator to get stream function from vorticity and velocity potential from divergence. Then compute zonal and meridional gradients to get U,V.

      source
      SpeedyWeather.SpeedyTransforms._divergence!Method
      _divergence!(
           kernel,
           div::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},
           u::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},
           v::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},
           S::SpectralTransform{NF<:AbstractFloat}
       )
      -

      Generic divergence function of vector u,v that writes into the output into div. Generic as it uses the kernel kernel such that curl, div, add or flipsign options are provided through kernel, but otherwise a single function is used.

      source
      SpeedyWeather.SpeedyTransforms.curl!Method
      curl!(
      +

      Generic divergence function of vector u,v that writes into the output into div. Generic as it uses the kernel kernel such that curl, div, add or flipsign options are provided through kernel, but otherwise a single function is used.

      source
      SpeedyWeather.SpeedyTransforms.curl!Method
      curl!(
           curl::LowerTriangularMatrix,
           u::LowerTriangularMatrix,
           v::LowerTriangularMatrix,
      @@ -305,7 +305,7 @@
           flipsign,
           add
       )
      -

      Curl of a vector u,v written into curl, curl = ∇×(u,v). u,v are expected to have a 1/coslat-scaling included, then curl is not scaled. flipsign option calculates -∇×(u,v) instead. add option calculates curl += ∇×(u,v) instead. flipsign and add can be combined. This functions only creates the kernel and calls the generic divergence function _divergence! subsequently with flipped u,v -> v,u for the curl.

      source
      SpeedyWeather.SpeedyTransforms.curlMethod
      curl(
      +

      Curl of a vector u,v written into curl, curl = ∇×(u,v). u,v are expected to have a 1/coslat-scaling included, then curl is not scaled. flipsign option calculates -∇×(u,v) instead. add option calculates curl += ∇×(u,v) instead. flipsign and add can be combined. This functions only creates the kernel and calls the generic divergence function _divergence! subsequently with flipped u,v -> v,u for the curl.

      source
      SpeedyWeather.SpeedyTransforms.curlMethod
      curl(
           u::LowerTriangularMatrix,
           v::LowerTriangularMatrix
       ) -> Any
      @@ -314,11 +314,11 @@
       u = spectral(u_grid)
       v = spectral(v_grid)
       vor = curl(u,v)
      -vor_grid = gridded(div)
      source
      SpeedyWeather.SpeedyTransforms.curlMethod
      curl(
           u::SpeedyWeather.RingGrids.AbstractGrid,
           v::SpeedyWeather.RingGrids.AbstractGrid
       ) -> Any
      -

      Curl (∇×) of two vector components u,v on a grid. Applies 1/coslat scaling, transforms to spectral space and returns the spectral curl, which is scaled with the radius of the sphere. Divide by radius for unscaling.

      source
      SpeedyWeather.SpeedyTransforms.divergence!Method
      divergence!(
      +

      Curl (∇×) of two vector components u,v on a grid. Applies 1/coslat scaling, transforms to spectral space and returns the spectral curl, which is scaled with the radius of the sphere. Divide by radius for unscaling.

      source
      SpeedyWeather.SpeedyTransforms.divergence!Method
      divergence!(
           div::LowerTriangularMatrix,
           u::LowerTriangularMatrix,
           v::LowerTriangularMatrix,
      @@ -326,7 +326,7 @@
           flipsign,
           add
       )
      -

      Divergence of a vector u,v written into div, div = ∇⋅(u,v). u,v are expected to have a 1/coslat-scaling included, then div is not scaled. flipsign option calculates -∇⋅(u,v) instead. add option calculates div += ∇⋅(u,v) instead. flipsign and add can be combined. This functions only creates the kernel and calls the generic divergence function _divergence! subsequently.

      source
      SpeedyWeather.SpeedyTransforms.divergenceMethod
      divergence(
      +

      Divergence of a vector u,v written into div, div = ∇⋅(u,v). u,v are expected to have a 1/coslat-scaling included, then div is not scaled. flipsign option calculates -∇⋅(u,v) instead. add option calculates div += ∇⋅(u,v) instead. flipsign and add can be combined. This functions only creates the kernel and calls the generic divergence function _divergence! subsequently.

      source
      SpeedyWeather.SpeedyTransforms.divergenceMethod
      divergence(
           u::LowerTriangularMatrix,
           v::LowerTriangularMatrix
       ) -> Any
      @@ -335,69 +335,69 @@
       u = spectral(u_grid,one_more_degree=true)
       v = spectral(v_grid,one_more_degree=true)
       div = divergence(u,v)
      -div_grid = gridded(div)

      Both div and div_grid are scaled with the radius.

      source
      SpeedyWeather.SpeedyTransforms.divergenceMethod
      divergence(
           u::SpeedyWeather.RingGrids.AbstractGrid,
           v::SpeedyWeather.RingGrids.AbstractGrid
       ) -> Any
      -

      Divergence (∇⋅) of two vector components u,v on a grid. Applies 1/coslat scaling, transforms to spectral space and returns the spectral divergence, which is scaled with the radius of the sphere. Divide by radius for unscaling.

      source
      SpeedyWeather.SpeedyTransforms.get_recursion_factorsMethod
      get_recursion_factors(  ::Type{NF}, # number format NF
      +

      Divergence (∇⋅) of two vector components u,v on a grid. Applies 1/coslat scaling, transforms to spectral space and returns the spectral divergence, which is scaled with the radius of the sphere. Divide by radius for unscaling.

      source
      SpeedyWeather.SpeedyTransforms.get_recursion_factorsMethod
      get_recursion_factors(  ::Type{NF}, # number format NF
                               lmax::Int,  # max degree l of spherical harmonics (0-based here)
                               mmax::Int   # max order m of spherical harmonics
      -                        ) where {NF<:AbstractFloat}

      Returns a matrix of recursion factors ϵ up to degree lmax and order mmax of number format NF.

      source
      SpeedyWeather.SpeedyTransforms.get_truncationFunction
      get_truncation(nlat_half::Integer) -> Any
       get_truncation(nlat_half::Integer, dealiasing::Real) -> Any
      -

      For the grid resolution parameter nlat_half (e.g. 24 for a 48-ring FullGaussianGrid) return the spectral truncation trunc (max degree of spherical harmonics) following a dealiasing parameter (default 2) to match spectral and grid resolution.

      source
      SpeedyWeather.SpeedyTransforms.gridded!Method
      gridded!(   map::AbstractGrid,
      +

      For the grid resolution parameter nlat_half (e.g. 24 for a 48-ring FullGaussianGrid) return the spectral truncation trunc (max degree of spherical harmonics) following a dealiasing parameter (default 2) to match spectral and grid resolution.

      source
      SpeedyWeather.SpeedyTransforms.gridded!Method
      gridded!(   map::AbstractGrid,
                   alms::LowerTriangularMatrix,
      -            S::SpectralTransform)

      Spectral transform (spectral to grid) of the spherical harmonic coefficients alms to a gridded field map. The spectral transform is number format-flexible as long as the parametric types of map, alms, S are identical. The spectral transform is grid-flexible as long as the typeof(map)<:AbstractGrid. Uses the precalculated arrays, FFT plans and other constants in the SpectralTransform struct S.

      source
      SpeedyWeather.SpeedyTransforms.griddedMethod
      gridded(
      +            S::SpectralTransform)

      Spectral transform (spectral to grid) of the spherical harmonic coefficients alms to a gridded field map. The spectral transform is number format-flexible as long as the parametric types of map, alms, S are identical. The spectral transform is grid-flexible as long as the typeof(map)<:AbstractGrid. Uses the precalculated arrays, FFT plans and other constants in the SpectralTransform struct S.

      source
      SpeedyWeather.SpeedyTransforms.griddedMethod
      gridded(
           alms::AbstractArray{T<:Complex{NF}, 2};
           recompute_legendre,
           Grid,
           kwargs...
       ) -> Any
      -

      Spectral transform (spectral to grid space) from spherical coefficients alms to a newly allocated gridded field map. Based on the size of alms the grid type grid, the spatial resolution is retrieved based on the truncation defined for grid. SpectralTransform struct S is allocated to execute gridded(alms,S).

      source
      SpeedyWeather.SpeedyTransforms.griddedMethod
      gridded(
      +

      Spectral transform (spectral to grid space) from spherical coefficients alms to a newly allocated gridded field map. Based on the size of alms the grid type grid, the spatial resolution is retrieved based on the truncation defined for grid. SpectralTransform struct S is allocated to execute gridded(alms,S).

      source
      SpeedyWeather.SpeedyTransforms.griddedMethod
      gridded(
           alms::AbstractMatrix,
           S::SpectralTransform{NF};
           kwargs...
       ) -> Any
      -

      Spectral transform (spectral to grid space) from spherical coefficients alms to a newly allocated gridded field map with precalculated properties based on the SpectralTransform struct S. alms is converted to a LowerTriangularMatrix to execute the in-place gridded!.

      source
      SpeedyWeather.SpeedyTransforms.roundup_fftMethod
      m = roundup_fft(n::Int;
      -                small_primes::Vector{Int}=[2,3,5])

      Returns an integer m >= n with only small prime factors 2, 3 (default, others can be specified with the keyword argument small_primes) to obtain an efficiently fourier-transformable number of longitudes, m = 2^i * 3^j * 5^k >= n, with i,j,k >=0.

      source
      SpeedyWeather.SpeedyTransforms.spectral!Method
      spectral!(  alms::LowerTriangularMatrix,
      +

      Spectral transform (spectral to grid space) from spherical coefficients alms to a newly allocated gridded field map with precalculated properties based on the SpectralTransform struct S. alms is converted to a LowerTriangularMatrix to execute the in-place gridded!.

      source
      SpeedyWeather.SpeedyTransforms.roundup_fftMethod
      m = roundup_fft(n::Int;
      +                small_primes::Vector{Int}=[2,3,5])

      Returns an integer m >= n with only small prime factors 2, 3 (default, others can be specified with the keyword argument small_primes) to obtain an efficiently fourier-transformable number of longitudes, m = 2^i * 3^j * 5^k >= n, with i,j,k >=0.

      source
      SpeedyWeather.SpeedyTransforms.spectral!Method
      spectral!(  alms::LowerTriangularMatrix,
                   map::AbstractGrid,
      -            S::SpectralTransform)

      Spectral transform (grid to spectral space) from the gridded field map on a grid<:AbstractGrid to a LowerTriangularMatrix of spherical harmonic coefficients alms. Uses FFT in the zonal direction, and a Legendre Transform in the meridional direction exploiting symmetries. The spectral transform is number format-flexible as long as the parametric types of map, alms, S are identical. The spectral transform is grid-flexible as long as the typeof(map)<:AbstractGrid. Uses the precalculated arrays, FFT plans and other constants in the SpectralTransform struct S.

      source
      SpeedyWeather.SpeedyTransforms.spectralMethod
      spectral(
      +            S::SpectralTransform)

      Spectral transform (grid to spectral space) from the gridded field map on a grid<:AbstractGrid to a LowerTriangularMatrix of spherical harmonic coefficients alms. Uses FFT in the zonal direction, and a Legendre Transform in the meridional direction exploiting symmetries. The spectral transform is number format-flexible as long as the parametric types of map, alms, S are identical. The spectral transform is grid-flexible as long as the typeof(map)<:AbstractGrid. Uses the precalculated arrays, FFT plans and other constants in the SpectralTransform struct S.

      source
      SpeedyWeather.SpeedyTransforms.spectralMethod
      spectral(
           map::AbstractMatrix;
           Grid,
           kwargs...
       ) -> LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat
      -

      Converts map to grid(map) to execute spectral(map::AbstractGrid;kwargs...).

      source
      SpeedyWeather.SpeedyTransforms.spectralMethod
      spectral(
           map::SpeedyWeather.RingGrids.AbstractGrid,
           S::SpectralTransform{NF}
       ) -> LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat
      -

      Spectral transform (grid to spectral) map to grid(map) to execute spectral(map::AbstractGrid;kwargs...).

      source
      SpeedyWeather.SpeedyTransforms.spectralMethod
      spectral(
           map::SpeedyWeather.RingGrids.AbstractGrid{NF};
           recompute_legendre,
           one_more_degree
       ) -> LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat
      -

      Converts map to Grid(map) to execute spectral(map::AbstractGrid;kwargs...).

      source
      SpeedyWeather.SpeedyTransforms.spectral_interpolationMethod
      alms_interp = spectral_interpolation(   ::Type{NF},
                                               alms::LowerTriangularMatrix,
                                               ltrunc::Integer,
                                               mtrunc::Integer
      -                                        ) where NF

      Returns a spectral coefficient matrix alms_interp that is alms padded with zeros to interpolate in spectral space. If trunc is smaller or equal to the implicit truncation in alms obtained from its size than spectral_truncation is automatically called instead, returning alms_trunc, a coefficient matrix that is smaller than alms, implicitly setting higher degrees and orders to zero.

      source
      SpeedyWeather.SpeedyTransforms.spectral_smoothing!Method
      spectral_smoothing!(A::LowerTriangularMatrix,c;power=1)

      Smooth the spectral field A following A = (1-(1-c)∇²ⁿ) with power n of a normalised Laplacian so that the highest degree lmax is dampened by multiplication with c. Anti-diffusion for c>1.

      source
      SpeedyWeather.SpeedyTransforms.spectral_smoothingMethod
      A_smooth = spectral_smoothing(A::LowerTriangularMatrix,c;power=1)

      Smooth the spectral field A following A_smooth = (1-c*∇²ⁿ)A with power n of a normalised Laplacian so that the highest degree lmax is dampened by multiplication with c. Anti-diffusion for c<0.

      source
      SpeedyWeather.SpeedyTransforms.spectral_truncation!Method
      spectral_truncation!(alms::AbstractMatrix,ltrunc::Integer,mtrunc::Integer)

      Truncate spectral coefficients alms in-place by setting (a) the upper right triangle to zero and (b) all coefficients for which the degree l is larger than the truncation ltrunc or order m larger than the truncaction mtrunc.

      source
      SpeedyWeather.SpeedyTransforms.spectral_truncation!Method
      spectral_truncation!(alms::LowerTriangularMatrix,ltrunc::Integer,mtrunc::Integer)

      Truncate spectral coefficients alms in-place by setting all coefficients for which the degree l is larger than the truncation ltrunc or order m larger than the truncaction mtrunc. Similar to spectral_truncation!(::AbstractMatrix, ...) but skips the upper triangle which is zero by design for LowerTriangularMatrix.

      source
      SpeedyWeather.SpeedyTransforms.spectral_truncationMethod
      alms_trunc = spectral_truncation(alms,trunc)

      Returns a spectral coefficient matrix alms_trunc that is truncated from alms to the size (trunc+1)². alms_trunc only contains those coefficient of alms for which m,l ≤ trunc, and l ≥ m are zero anyway. If trunc is larger than the implicit truncation in alms obtained from its size than spectral_interpolation is automatically called instead, returning alms_interp, a coefficient matrix that is larger than alms with padded zero coefficients.

      source
      SpeedyWeather.SpeedyTransforms.ϵlmMethod
      ϵ = ϵ(l,m)

      Recursion factors ϵ as a function of degree l and order m (0-based) of the spherical harmonics. ϵ(l,m) = sqrt((l^2-m^2)/(4*l^2-1)) with default number format Float64.

      source
      SpeedyWeather.SpeedyTransforms.ϵlmMethod
      ϵ = ϵ(NF,l,m)

      Recursion factors ϵ as a function of degree l and order m (0-based) of the spherical harmonics. ϵ(l,m) = sqrt((l^2-m^2)/(4*l^2-1)) and then converted to number format NF.

      source
      SpeedyWeather.SpeedyTransforms.∇!Method
      ∇!(
      +                                        ) where NF

      Returns a spectral coefficient matrix alms_interp that is alms padded with zeros to interpolate in spectral space. If trunc is smaller or equal to the implicit truncation in alms obtained from its size than spectral_truncation is automatically called instead, returning alms_trunc, a coefficient matrix that is smaller than alms, implicitly setting higher degrees and orders to zero.

      source
      SpeedyWeather.SpeedyTransforms.spectral_smoothing!Method
      spectral_smoothing!(A::LowerTriangularMatrix,c;power=1)

      Smooth the spectral field A following A = (1-(1-c)∇²ⁿ) with power n of a normalised Laplacian so that the highest degree lmax is dampened by multiplication with c. Anti-diffusion for c>1.

      source
      SpeedyWeather.SpeedyTransforms.spectral_smoothingMethod
      A_smooth = spectral_smoothing(A::LowerTriangularMatrix,c;power=1)

      Smooth the spectral field A following A_smooth = (1-c*∇²ⁿ)A with power n of a normalised Laplacian so that the highest degree lmax is dampened by multiplication with c. Anti-diffusion for c<0.

      source
      SpeedyWeather.SpeedyTransforms.spectral_truncation!Method
      spectral_truncation!(alms::AbstractMatrix,ltrunc::Integer,mtrunc::Integer)

      Truncate spectral coefficients alms in-place by setting (a) the upper right triangle to zero and (b) all coefficients for which the degree l is larger than the truncation ltrunc or order m larger than the truncaction mtrunc.

      source
      SpeedyWeather.SpeedyTransforms.spectral_truncation!Method
      spectral_truncation!(alms::LowerTriangularMatrix,ltrunc::Integer,mtrunc::Integer)

      Truncate spectral coefficients alms in-place by setting all coefficients for which the degree l is larger than the truncation ltrunc or order m larger than the truncaction mtrunc. Similar to spectral_truncation!(::AbstractMatrix, ...) but skips the upper triangle which is zero by design for LowerTriangularMatrix.

      source
      SpeedyWeather.SpeedyTransforms.spectral_truncationMethod
      alms_trunc = spectral_truncation(alms,trunc)

      Returns a spectral coefficient matrix alms_trunc that is truncated from alms to the size (trunc+1)². alms_trunc only contains those coefficient of alms for which m,l ≤ trunc, and l ≥ m are zero anyway. If trunc is larger than the implicit truncation in alms obtained from its size than spectral_interpolation is automatically called instead, returning alms_interp, a coefficient matrix that is larger than alms with padded zero coefficients.

      source
      SpeedyWeather.SpeedyTransforms.ϵlmMethod
      ϵ = ϵ(l,m)

      Recursion factors ϵ as a function of degree l and order m (0-based) of the spherical harmonics. ϵ(l,m) = sqrt((l^2-m^2)/(4*l^2-1)) with default number format Float64.

      source
      SpeedyWeather.SpeedyTransforms.ϵlmMethod
      ϵ = ϵ(NF,l,m)

      Recursion factors ϵ as a function of degree l and order m (0-based) of the spherical harmonics. ϵ(l,m) = sqrt((l^2-m^2)/(4*l^2-1)) and then converted to number format NF.

      source
      SpeedyWeather.SpeedyTransforms.∇!Method
      ∇!(
           dpdx::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},
           dpdy::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},
           p::LowerTriangularMatrix{Complex{NF<:AbstractFloat}},
           S::SpectralTransform{NF<:AbstractFloat}
       ) -> Tuple{LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat, LowerTriangularMatrix{Complex{NF}} where NF<:AbstractFloat}
      -

      Applies the gradient operator ∇ applied to input p and stores the result in dpdx (zonal derivative) and dpdy (meridional derivative). The gradient operator acts on the unit sphere and therefore omits the radius scaling

      source
      SpeedyWeather.SpeedyTransforms.∇²!Method
      ∇²!(    ∇²alms::LowerTriangularMatrix,
      +

      Applies the gradient operator ∇ applied to input p and stores the result in dpdx (zonal derivative) and dpdy (meridional derivative). The gradient operator acts on the unit sphere and therefore omits the radius scaling

      source
      SpeedyWeather.SpeedyTransforms.∇²!Method
      ∇²!(    ∇²alms::LowerTriangularMatrix,
               alms::LowerTriangularMatrix,
               S::SpectralTransform;
               add::Bool=false,
               flipsign::Bool=false,
      -        inverse::Bool=false)

      Laplace operator ∇² applied to the spectral coefficients alms in spherical coordinates. The radius R is omitted in the eigenvalues which are precomputed in S. ∇²! is the in-place version which directly stores the output in the first argument ∇²alms.

      Keyword arguments

      • add=true adds the ∇²(alms) to the output
      • flipsign=true computes -∇²(alms) instead
      • inverse=true computes ∇⁻²(alms) instead

      Default is add=false, flipsign=false, inverse=false. These options can be combined.

      source
      SpeedyWeather.SpeedyTransforms.∇²Method
      ∇²(alms::LowerTriangularMatrix, S::SpectralTransform) -> Any
      -

      Laplace operator ∇² applied to input alms, using precomputed eigenvalues from S. The Laplace operator acts on the unit sphere and therefore omits the 1/radius^2 scaling

      source
      SpeedyWeather.SpeedyTransforms.∇²Method
      ∇²(alms::LowerTriangularMatrix) -> Any
      -

      Returns the Laplace operator ∇² applied to input alms. The Laplace operator acts on the unit sphere and therefore omits the 1/radius^2 scaling

      source
      SpeedyWeather.SpeedyTransforms.∇⁻²!Method
      ∇⁻²!(   ∇⁻²alms::LowerTriangularMatrix,
      +        inverse::Bool=false)

      Laplace operator ∇² applied to the spectral coefficients alms in spherical coordinates. The radius R is omitted in the eigenvalues which are precomputed in S. ∇²! is the in-place version which directly stores the output in the first argument ∇²alms.

      Keyword arguments

      • add=true adds the ∇²(alms) to the output
      • flipsign=true computes -∇²(alms) instead
      • inverse=true computes ∇⁻²(alms) instead

      Default is add=false, flipsign=false, inverse=false. These options can be combined.

      source
      SpeedyWeather.SpeedyTransforms.∇²Method
      ∇²(alms::LowerTriangularMatrix, S::SpectralTransform) -> Any
      +

      Laplace operator ∇² applied to input alms, using precomputed eigenvalues from S. The Laplace operator acts on the unit sphere and therefore omits the 1/radius^2 scaling

      source
      SpeedyWeather.SpeedyTransforms.∇²Method
      ∇²(alms::LowerTriangularMatrix) -> Any
      +

      Returns the Laplace operator ∇² applied to input alms. The Laplace operator acts on the unit sphere and therefore omits the 1/radius^2 scaling

      source
      SpeedyWeather.SpeedyTransforms.∇⁻²!Method
      ∇⁻²!(   ∇⁻²alms::LowerTriangularMatrix,
               alms::LowerTriangularMatrix,
               S::SpectralTransform;
               add::Bool=false,
      -        flipsign::Bool=false)

      Calls ∇²!(∇⁻²alms, alms, S; add, flipsign, inverse=true).

      source
      SpeedyWeather.SpeedyTransforms.∇⁻²Method
      ∇⁻²(
           ∇²alms::LowerTriangularMatrix,
           S::SpectralTransform
       ) -> Any
      -

      InverseLaplace operator ∇⁻² applied to input alms, using precomputed eigenvalues from S. The Laplace operator acts on the unit sphere and therefore omits the radius^2 scaling

      source
      SpeedyWeather.SpeedyTransforms.∇⁻²Method
      ∇⁻²(∇²alms::LowerTriangularMatrix) -> Any
      -

      Returns the inverse Laplace operator ∇⁻² applied to input alms. The Laplace operator acts on the unit sphere and therefore omits the radius^2 scaling

      source
      +

      InverseLaplace operator ∇⁻² applied to input alms, using precomputed eigenvalues from S. The Laplace operator acts on the unit sphere and therefore omits the radius^2 scaling

      source
      SpeedyWeather.SpeedyTransforms.∇⁻²Method
      ∇⁻²(∇²alms::LowerTriangularMatrix) -> Any
      +

      Returns the inverse Laplace operator ∇⁻² applied to input alms. The Laplace operator acts on the unit sphere and therefore omits the radius^2 scaling

      source