From 9b1ae081eeb17afc91f146b2ff97a83e6c3afde6 Mon Sep 17 00:00:00 2001 From: "Documenter.jl" Date: Tue, 17 Dec 2024 14:13:00 +0000 Subject: [PATCH] build based on 8f46448 --- previews/PR645/.documenter-siteinfo.json | 2 +- previews/PR645/analysis/index.html | 2 +- previews/PR645/barotropic/index.html | 2 +- previews/PR645/callbacks/index.html | 20 +- previews/PR645/convection/index.html | 2 +- .../PR645/custom_netcdf_output/index.html | 2 +- previews/PR645/examples_2D/index.html | 2 +- previews/PR645/examples_3D/index.html | 2 +- previews/PR645/extensions/index.html | 2 +- previews/PR645/forcing_drag/index.html | 2 +- previews/PR645/functions/index.html | 478 +++++++++--------- previews/PR645/gradients/index.html | 2 +- previews/PR645/grids/index.html | 2 +- previews/PR645/how_to_run_speedy/index.html | 2 +- previews/PR645/index.html | 2 +- previews/PR645/initial_conditions/index.html | 2 +- previews/PR645/installation/index.html | 2 +- previews/PR645/land_sea_mask/index.html | 2 +- .../PR645/large_scale_condensation/index.html | 2 +- .../PR645/lowertriangularmatrices/index.html | 40 +- previews/PR645/ocean/index.html | 2 +- previews/PR645/orography/index.html | 2 +- previews/PR645/output/index.html | 2 +- previews/PR645/parameterizations/index.html | 2 +- previews/PR645/particles/index.html | 2 +- previews/PR645/primitiveequation/index.html | 2 +- previews/PR645/radiation/index.html | 2 +- previews/PR645/ringgrids/index.html | 134 ++--- previews/PR645/run_0001/parameters.txt | 2 +- previews/PR645/run_0001/progress.txt | 4 +- previews/PR645/run_0002/parameters.txt | 2 +- previews/PR645/run_0002/progress.txt | 4 +- previews/PR645/run_0003/parameters.txt | 2 +- previews/PR645/run_0003/progress.txt | 6 +- previews/PR645/run_0004/parameters.txt | 2 +- previews/PR645/run_0004/progress.txt | 10 +- previews/PR645/run_0005/parameters.txt | 2 +- previews/PR645/run_0005/progress.txt | 2 +- previews/PR645/run_test/parameters.txt | 2 +- previews/PR645/run_test/progress.txt | 4 +- previews/PR645/search_index.js | 2 +- previews/PR645/shallowwater/index.html | 2 +- previews/PR645/spectral_transform/index.html | 2 +- previews/PR645/speedytransforms/index.html | 120 ++--- previews/PR645/stochastic_physics/index.html | 2 +- previews/PR645/structure/index.html | 2 +- previews/PR645/surface_fluxes/index.html | 2 +- previews/PR645/vertical_diffusion/index.html | 2 +- 48 files changed, 448 insertions(+), 448 deletions(-) diff --git a/previews/PR645/.documenter-siteinfo.json b/previews/PR645/.documenter-siteinfo.json index 56a5eec93..dfdbc7c58 100644 --- a/previews/PR645/.documenter-siteinfo.json +++ b/previews/PR645/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.11.2","generation_timestamp":"2024-12-17T14:05:54","documenter_version":"1.8.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.11.2","generation_timestamp":"2024-12-17T14:12:45","documenter_version":"1.8.0"}} \ No newline at end of file diff --git a/previews/PR645/analysis/index.html b/previews/PR645/analysis/index.html index 204085afb..31c9f0957 100644 --- a/previews/PR645/analysis/index.html +++ b/previews/PR645/analysis/index.html @@ -267,4 +267,4 @@ lines!(axs[3,2], days, Q) axs[3,2].title = "Potential enstrophy" axs[3,2].xlabel = "time [day]" -fig

Global diagnostics

+fig

Global diagnostics

diff --git a/previews/PR645/barotropic/index.html b/previews/PR645/barotropic/index.html index c74d84ef9..232c33d8e 100644 --- a/previews/PR645/barotropic/index.html +++ b/previews/PR645/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/PR645/callbacks/index.html b/previews/PR645/callbacks/index.html index b6fc989cf..5a956057b 100644 --- a/previews/PR645/callbacks/index.html +++ b/previews/PR645/callbacks/index.html @@ -53,14 +53,14 @@ end

The function signature for callback! is the same as for initialize!. You may access anything from progn, diagn or model, although for a purely diagnostic callback this should be read-only. While you could change other model components like the land sea mask in model.land_sea_mask or orography etc. then you interfere with the simulation which is more advanced and will be discussed in Intrusive callbacks below.

Lastly, we extend the finalize! function which is called once after the last time step. This could be used, for example, to save the maximum_surface_wind_speed vector to file or in case you want to find the highest wind speed across all time steps. But in many cases you may not need to do anything, in which case you just just let it return nothing.

SpeedyWeather.finalize!(::StormChaser, args...) = nothing
Always extend `initialize!`, `callback!` and `finalize!`

For a custom callback you need to extend all three, initialize!, callback! and finalize!, even if your callback doesn't need it. Just return nothing in that case. Otherwise a MethodError will occur. While we could have defined all callbacks by default to do nothing on each of these, this may give you the false impression that your callback is already defined correctly, although it's not.

Adding a callback

Every model has a field callbacks::Dict{Symbol, AbstractCallback} such that the callbacks keyword can be used to create a model with a dictionary of callbacks. Callbacks are identified with a Symbol key inside such a dictionary. We have a convenient CallbackDict generator function which can be used like Dict but the key-value pairs have to be of type Symbol-AbstractCallback. Let us illustrate this with the dummy callback NoCallback (which is a callback that returns nothing on initialize!, callback! and finalize!)

callbacks = CallbackDict()                                  # empty dictionary
 callbacks = CallbackDict(:my_callback => NoCallback())      # key => callback
Dict{Symbol, SpeedyWeather.AbstractCallback} with 1 entry:
   :my_callback => NoCallback <: AbstractCallback…

If you don't provide a key a random key will be assigned

callbacks = CallbackDict(NoCallback())
Dict{Symbol, SpeedyWeather.AbstractCallback} with 1 entry:
-  :callback_ufll => NoCallback <: AbstractCallback…

and you can add (or delete) additional callbacks

add!(callbacks, NoCallback())                   # this will also pick a random key
+  :callback_ujYv => NoCallback <: AbstractCallback…

and you can add (or delete) additional callbacks

add!(callbacks, NoCallback())                   # this will also pick a random key
 add!(callbacks, :my_callback => NoCallback())   # use key :my_callback
 delete!(callbacks, :my_callback)                # remove by key
 callbacks
Dict{Symbol, SpeedyWeather.AbstractCallback} with 2 entries:
-  :callback_ufll => NoCallback <: AbstractCallback…
-  :callback_GKHy => NoCallback <: AbstractCallback…

And you can chain them too

add!(callbacks, NoCallback(), NoCallback())                     # random keys
-add!(callbacks, :key1 => NoCallback(), :key2 => NoCallback())   # keys provided
[ Info: NoCallback callback added with key callback_lzMP
-[ Info: NoCallback callback added with key callback_qSjD

Meaning that callbacks can be added before and after model construction

spectral_grid = SpectralGrid()
+  :callback_ujYv => NoCallback <: AbstractCallback…
+  :callback_XkUl => NoCallback <: AbstractCallback…

And you can chain them too

add!(callbacks, NoCallback(), NoCallback())                     # random keys
+add!(callbacks, :key1 => NoCallback(), :key2 => NoCallback())   # keys provided
[ Info: NoCallback callback added with key callback_BU7c
+[ Info: NoCallback callback added with key callback_DKI2

Meaning that callbacks can be added before and after model construction

spectral_grid = SpectralGrid()
 callbacks = CallbackDict(:callback_added_before => NoCallback())
 model = PrimitiveWetModel(spectral_grid; callbacks)
 add!(model.callbacks, :callback_added_afterwards => NoCallback())
@@ -153,12 +153,12 @@
 
 # start simulation 7 days earlier
 simulation = initialize!(model, time = DateTime(2000,1,2,12))
-run!(simulation, period=Day(10))
[ Info: Main.MyScheduledCallback callback added with key callback_a15s
+run!(simulation, period=Day(10))
[ Info: Main.MyScheduledCallback callback added with key callback_cnGT
 [ Info: North pole has a temperature of 226.52678 on 2000-01-09T12:00:00.

So the callback gives us the temperature at the North Pole exactly when scheduled. We could have also stored this temperature, or conditionally changed parameters inside the model. There are plenty of ways how to use the scheduling, either by event, or in contrast, we could also schedule for once a day. As illustrated in the following

north_pole_temp_daily = MyScheduledCallback(Schedule(every=Day(1)))
 add!(model.callbacks, north_pole_temp_daily)
 
 # resume simulation, start time is now 2000-1-12 noon
-run!(simulation, period=Day(5))
[ Info: Main.MyScheduledCallback callback added with key callback_NMEG
+run!(simulation, period=Day(5))
[ Info: Main.MyScheduledCallback callback added with key callback_JgyL
 ┌ Warning: Empty schedule.
 @ SpeedyWeather ~/work/SpeedyWeather.jl/SpeedyWeather.jl/src/output/schedule.jl:77
 [ Info: North pole has a temperature of 233.03633 on 2000-01-13T12:00:00.
@@ -169,10 +169,10 @@
 add!(model.callbacks, odd_schedule)
 
 # resume simulation for 4 hours
-run!(simulation, period=Hour(4))
[ Info: Main.MyScheduledCallback callback added with key callback_FIdB
+run!(simulation, period=Hour(4))
[ Info: Main.MyScheduledCallback callback added with key callback_NOTt
+[ Info: Scheduler adjusted from every 1 hour, 10 minutes to every 1 hour, 20 minutes to match timestep.
 ┌ Warning: Empty schedule.
 @ SpeedyWeather ~/work/SpeedyWeather.jl/SpeedyWeather.jl/src/output/schedule.jl:77
-[ Info: Scheduler adjusted from every 1 hour, 10 minutes to every 1 hour, 20 minutes to match timestep.
 ┌ Warning: Empty schedule.
 @ SpeedyWeather ~/work/SpeedyWeather.jl/SpeedyWeather.jl/src/output/schedule.jl:77
 [ Info: North pole has a temperature of 255.18764 on 2000-01-17T13:20:00.
@@ -187,4 +187,4 @@
 ├ williams_filter::Float32 = 0.53
 ├ Δt_millisec::Dates.Millisecond = 2400000 milliseconds
 ├ Δt_sec::Float32 = 2400.0
-└ Δt::Float32 = 0.00037670694

Or converted into minutes (the time step internally is at millisecond accuracy)

Minute(model.time_stepping.Δt_millisec)
40 minutes

which illustrates why the adjustment of our callback frequency was necessary.

+└ Δt::Float32 = 0.00037670694

Or converted into minutes (the time step internally is at millisecond accuracy)

Minute(model.time_stepping.Δt_millisec)
40 minutes

which illustrates why the adjustment of our callback frequency was necessary.

diff --git a/previews/PR645/convection/index.html b/previews/PR645/convection/index.html index 489e92258..c50ba25bb 100644 --- a/previews/PR645/convection/index.html +++ b/previews/PR645/convection/index.html @@ -22,4 +22,4 @@ \end{aligned}\]

Corrected relaxation

After the reference profiles have been corrected in Deep convection and Shallow convection we actually calculate tendencies from

\[\begin{aligned} \delta q &= - \frac{q - q_{ref, 2}}{\tau_{SBM}} \\ \delta T &= - \frac{T - T_{ref, 2}}{\tau_{SBM}} -\end{aligned}\]

with $\tau_{SBM} = 2h$ as default.

Convective precipitation

The convective precipitation $P$ results then from the vertical integration of the $\delta q$ tendencies, similar to Large-scale precipitation.

\[P = -\int \frac{\Delta t}{g \rho} \delta q dp\]

In the shallow convection case $P=0$ due to the correction even though in the first guess relaxation $P<0$ was possible, but for deep convection $P>0$ by definition.

Dry convection

In the primitive equation model with humidity the Betts-Miller convection scheme as described above is defined. Without humidity, a dry version reduces to the Shallow convection case. The two different shallow convection schemes in Frierson 2007[Frierson2007], the "shallower" shallow convection scheme and the "qref" (as implemented here in Shallow convection) in that case also reduce to the same formulation. The dry Betts-Miller convection scheme is the default in the primitive equation model without humidity.

References

+\end{aligned}\]

with $\tau_{SBM} = 2h$ as default.

Convective precipitation

The convective precipitation $P$ results then from the vertical integration of the $\delta q$ tendencies, similar to Large-scale precipitation.

\[P = -\int \frac{\Delta t}{g \rho} \delta q dp\]

In the shallow convection case $P=0$ due to the correction even though in the first guess relaxation $P<0$ was possible, but for deep convection $P>0$ by definition.

Dry convection

In the primitive equation model with humidity the Betts-Miller convection scheme as described above is defined. Without humidity, a dry version reduces to the Shallow convection case. The two different shallow convection schemes in Frierson 2007[Frierson2007], the "shallower" shallow convection scheme and the "qref" (as implemented here in Shallow convection) in that case also reduce to the same formulation. The dry Betts-Miller convection scheme is the default in the primitive equation model without humidity.

References

diff --git a/previews/PR645/custom_netcdf_output/index.html b/previews/PR645/custom_netcdf_output/index.html index dc584c8d9..31b4c1a19 100644 --- a/previews/PR645/custom_netcdf_output/index.html +++ b/previews/PR645/custom_netcdf_output/index.html @@ -56,4 +56,4 @@

Fantastic, it's all there. We wrap this back into a FullGaussianGrid but ignore the mask (there are no masked values) in the netCDF file which causes a Union{Missing, Float32} element type by reading out the raw data with .var. And visualise the vertical velocity in sigma coordinates (remember this is actually $\partial \sigma / \partial t$) of the last time step (index end) stored on layer $k=4$ (counted from the top)

w = FullGaussianGrid(ds["w"].var[:, :, :, :], input_as=Matrix)
 
 using CairoMakie
-heatmap(w[:, 4, end], title="vertical velocity dσ/dt at k=4")

Sigma tendency

This is now the vertical velocity between layer $k=4$ and $k=5$. You can check that the vertical velocity on layer $k=8$ is actually zero (because that is the boundary condition at the surface) and so would be the velocity between $k=0$ and $k=1$ at the top of the atmosphere, which however is not explicitly stored. The vertical velocity is strongest on the wind and leeward side of mountains which is reassuring and all the analysis we want to do here for now.

+heatmap(w[:, 4, end], title="vertical velocity dσ/dt at k=4")

Sigma tendency

This is now the vertical velocity between layer $k=4$ and $k=5$. You can check that the vertical velocity on layer $k=8$ is actually zero (because that is the boundary condition at the surface) and so would be the velocity between $k=0$ and $k=1$ at the top of the atmosphere, which however is not explicitly stored. The vertical velocity is strongest on the wind and leeward side of mountains which is reassuring and all the analysis we want to do here for now.

diff --git a/previews/PR645/examples_2D/index.html b/previews/PR645/examples_2D/index.html index 8f0b77a5d..1ddef8b7b 100644 --- a/previews/PR645/examples_2D/index.html +++ b/previews/PR645/examples_2D/index.html @@ -253,4 +253,4 @@ η = simulation.diagnostic_variables.grid.pres_grid h = @. η + H - Hb # @. to broadcast grid + scalar - grid -heatmap(h, title="Dynamic layer thickness h", colormap=:oslo)

Gravity waves pyplot

Mountains like the Himalayas or the Andes are quite obvious because the atmospheric layer is much thinner there. The pressure gradient is relative to $z=0$ so in a fluid at rest the mountains would just "reach into" the fluid, thinning the layer the higher the mountain. As the atmosphere here is not at rest the layer thickness is not perfectly (anti-)correlated with orography but almost so.

References

+heatmap(h, title="Dynamic layer thickness h", colormap=:oslo)

Gravity waves pyplot

Mountains like the Himalayas or the Andes are quite obvious because the atmospheric layer is much thinner there. The pressure gradient is relative to $z=0$ so in a fluid at rest the mountains would just "reach into" the fluid, thinning the layer the higher the mountain. As the atmosphere here is not at rest the layer thickness is not perfectly (anti-)correlated with orography but almost so.

References

diff --git a/previews/PR645/examples_3D/index.html b/previews/PR645/examples_3D/index.html index 2fd9298a7..d36571936 100644 --- a/previews/PR645/examples_3D/index.html +++ b/previews/PR645/examples_3D/index.html @@ -98,4 +98,4 @@ # visualise, precip_* arrays are flat copies, no need to read them out again! m2mm_hr = (1000*Hour(1)/Hour(6)) # convert from [m] to [mm/hr] heatmap(m2mm_hr*precip_large_scale, title="Large-scale precipiation [mm/hr]", colormap=:dense) -heatmap(m2mm_hr*precip_convection, title="Convective precipiation [mm/hr]", colormap=:dense)

Large-scale precipitation Convective precipitation

As the precipitation fields are accumulated meters over the integration period we divide by 6 hours to get a precipitation rate $[m/s]$ but then multiply with 1 hour and 1000 to get the typical precipitation unit of $[mm/hr]$.

References

+heatmap(m2mm_hr*precip_convection, title="Convective precipiation [mm/hr]", colormap=:dense)

Large-scale precipitation Convective precipitation

As the precipitation fields are accumulated meters over the integration period we divide by 6 hours to get a precipitation rate $[m/s]$ but then multiply with 1 hour and 1000 to get the typical precipitation unit of $[mm/hr]$.

References

diff --git a/previews/PR645/extensions/index.html b/previews/PR645/extensions/index.html index 1ddb44672..0eace3655 100644 --- a/previews/PR645/extensions/index.html +++ b/previews/PR645/extensions/index.html @@ -57,4 +57,4 @@ SpeedyWeather.AbstractSurfaceWind SpeedyWeather.AbstractTemperatureRelaxation SpeedyWeather.AbstractVegetation - SpeedyWeather.AbstractVerticalDiffusion

but these are discussed in more detail in Parameterizations. For a more concrete example of how to define a new forcing for the 2D models, see Custom forcing and drag.

+ SpeedyWeather.AbstractVerticalDiffusion

but these are discussed in more detail in Parameterizations. For a more concrete example of how to define a new forcing for the 2D models, see Custom forcing and drag.

diff --git a/previews/PR645/forcing_drag/index.html b/previews/PR645/forcing_drag/index.html index ff9af24c0..199c693f0 100644 --- a/previews/PR645/forcing_drag/index.html +++ b/previews/PR645/forcing_drag/index.html @@ -127,4 +127,4 @@ vor = simulation.diagnostic_variables.grid.vor_grid[:, 1] heatmap(vor, title="Stochastically stirred vorticity")

Stochastic stirring

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 and drag 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 <: SpeedyWeather.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/PR645/functions/index.html b/previews/PR645/functions/index.html index eb53e24f2..bdd70957d 100644 --- a/previews/PR645/functions/index.html +++ b/previews/PR645/functions/index.html @@ -1,5 +1,5 @@ -Function and type index · SpeedyWeather.jl

Function and type index

Core.TypeMethod

Generator function pulling the resolution information from spectral_grid.

source
Core.TypeMethod

Generator function pulling the resolution information from spectral_grid for all Orography <: AbstractOrography.

source
SpeedyWeather.AbstractLandSeaMaskType

Abstract super type for land-sea masks. Custom land-sea masks have to be defined as

CustomMask{NF<:AbstractFloat, Grid<:AbstractGrid{NF}} <: AbstractLandSeaMask{NF, Grid}

and need to have at least a field called mask::Grid that uses a Grid as defined by the spectral grid object, so of correct size and with the number format NF. All AbstractLandSeaMask have a convenient generator function to be used like mask = CustomMask(spectral_grid, option=argument), but you may add your own or customize by defining CustomMask(args...) which should return an instance of type CustomMask{NF, Grid} with parameters matching the spectral grid. Then the initialize function has to be extended for that new mask

initialize!(mask::CustomMask, model::PrimitiveEquation)

which generally is used to tweak the mask.mask grid as you like, using any other options you have included in CustomMask as fields or anything else (preferrably read-only, because this is only to initialize the land-sea mask, nothing else) from model. You can for example read something from file, set some values manually, or use coordinates from model.geometry.

The land-sea mask grid is expected to have values between [0, 1] as we use a fractional mask, allowing for grid points being, e.g. quarter land and three quarters sea for 0.25 with 0 (=sea) and 1 (=land). The surface fluxes will weight proportionally the fluxes e.g. from sea and land surface temperatures. Note however, that the land-sea mask can declare grid points being (at least partially) ocean even though the sea surface temperatures aren't defined (=NaN) in that grid point. In that case, not flux is applied.

source
SpeedyWeather.AbstractOceanType

Abstract super type for ocean models, which control the sea surface temperature and sea ice concentration as boundary conditions to a SpeedyWeather simulation. A new ocean model has to be defined as

CustomOceanModel <: AbstractOcean

and can have parameters like CustomOceanModel{T} and fields. They need to extend the following functions

function initialize!(ocean_model::CustomOceanModel, model::PrimitiveEquation)
+Function and type index · SpeedyWeather.jl

Function and type index

Core.TypeMethod

Generator function pulling the resolution information from spectral_grid.

source
Core.TypeMethod

Generator function pulling the resolution information from spectral_grid for all Orography <: AbstractOrography.

source
SpeedyWeather.AbstractLandSeaMaskType

Abstract super type for land-sea masks. Custom land-sea masks have to be defined as

CustomMask{NF<:AbstractFloat, Grid<:AbstractGrid{NF}} <: AbstractLandSeaMask{NF, Grid}

and need to have at least a field called mask::Grid that uses a Grid as defined by the spectral grid object, so of correct size and with the number format NF. All AbstractLandSeaMask have a convenient generator function to be used like mask = CustomMask(spectral_grid, option=argument), but you may add your own or customize by defining CustomMask(args...) which should return an instance of type CustomMask{NF, Grid} with parameters matching the spectral grid. Then the initialize function has to be extended for that new mask

initialize!(mask::CustomMask, model::PrimitiveEquation)

which generally is used to tweak the mask.mask grid as you like, using any other options you have included in CustomMask as fields or anything else (preferrably read-only, because this is only to initialize the land-sea mask, nothing else) from model. You can for example read something from file, set some values manually, or use coordinates from model.geometry.

The land-sea mask grid is expected to have values between [0, 1] as we use a fractional mask, allowing for grid points being, e.g. quarter land and three quarters sea for 0.25 with 0 (=sea) and 1 (=land). The surface fluxes will weight proportionally the fluxes e.g. from sea and land surface temperatures. Note however, that the land-sea mask can declare grid points being (at least partially) ocean even though the sea surface temperatures aren't defined (=NaN) in that grid point. In that case, not flux is applied.

source
SpeedyWeather.AbstractOceanType

Abstract super type for ocean models, which control the sea surface temperature and sea ice concentration as boundary conditions to a SpeedyWeather simulation. A new ocean model has to be defined as

CustomOceanModel <: AbstractOcean

and can have parameters like CustomOceanModel{T} and fields. They need to extend the following functions

function initialize!(ocean_model::CustomOceanModel, model::PrimitiveEquation)
     # your code here to initialize the ocean model itself
     # you can use other fields from model, e.g. model.geometry
 end
@@ -24,42 +24,42 @@
 )
     # your code here to change the progn.ocean.sea_surface_temperature and/or
     # progn.ocean.sea_ice_concentration on any timestep
-end

Temperatures in ocean.seasurfacetemperature have units of Kelvin, or NaN for no ocean. Note that neither sea surface temperature, land-sea mask or orography have to agree. It is possible to have an ocean on top of a mountain. For an ocean grid-cell that is (partially) masked by the land-sea mask, its value will be (fractionally) ignored in the calculation of surface fluxes (potentially leading to a zero flux depending on land surface temperatures). For an ocean grid-cell that is NaN but not masked by the land-sea mask, its value is always ignored.

source
SpeedyWeather.AquaPlanetType

AquaPlanet sea surface temperatures that are constant in time and longitude, but vary in latitude following a coslat². To be created like

ocean = AquaPlanet(spectral_grid, temp_equator=302, temp_poles=273)

Fields and options are

  • temp_equator::Any: [OPTION] Temperature on the Equator [K]

  • temp_poles::Any: [OPTION] Temperature at the poles [K]

source
SpeedyWeather.AquaPlanetMaskType

Land-sea mask with zero = sea everywhere.

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

The BarotropicModel contains all model components needed for the simulation of the barotropic vorticity equations. To be constructed like

model = BarotropicModel(spectral_grid; kwargs...)

with spectral_grid::SpectralGrid used to initalize all non-default components passed on as keyword arguments, e.g. planet=Earth(spectral_grid). Fields, representing model components, are

  • spectral_grid::SpectralGrid

  • device_setup::Any

  • geometry::Any

  • planet::Any

  • atmosphere::Any

  • coriolis::Any

  • forcing::Any

  • drag::Any

  • particle_advection::Any

  • initial_conditions::Any

  • random_process::Any

  • time_stepping::Any

  • spectral_transform::Any

  • implicit::Any

  • horizontal_diffusion::Any

  • output::Any

  • callbacks::Dict{Symbol, SpeedyWeather.AbstractCallback}

  • feedback::Any

source
SpeedyWeather.BulkRichardsonDragType

Boundary layer drag coefficient from the bulk Richardson number, following Frierson, 2006, Journal of the Atmospheric Sciences.

  • κ::Any: von Kármán constant [1]

  • z₀::Any: roughness length [m]

  • Ri_c::Any: Critical Richardson number for stable mixing cutoff [1]

  • drag_max::Base.RefValue: Maximum drag coefficient, κ²/log(zₐ/z₀) for zₐ from reference temperature

source
SpeedyWeather.ClausiusClapeyronType

Parameters for computing saturation vapour pressure of water using the Clausis-Clapeyron equation,

e(T) = e₀ * exp( -Lᵥ/Rᵥ * (1/T - 1/T₀)),

where T is in Kelvin, Lᵥ the the latent heat of condensation and Rᵥ the gas constant of water vapour

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

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

  • Lᵥ::AbstractFloat: Latent heat of condensation/vaporization of water [J/kg]

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

  • R_vapour::AbstractFloat: Gas constant of water vapour [J/kg/K]

  • R_dry::AbstractFloat: Gas constant of dry air [J/kg/K]

  • Lᵥ_Rᵥ::AbstractFloat: Latent heat of condensation divided by gas constant of water vapour [K]

  • T₀⁻¹::AbstractFloat: Inverse of T₀, one over 0°C in Kelvin

  • mol_ratio::AbstractFloat: Ratio of molecular masses [1] of water vapour over dry air (=Rdry/Rvapour).

source
SpeedyWeather.ClausiusClapeyronMethod

Functor: Saturation water vapour pressure as a function of temperature using the Clausius-Clapeyron equation,

e(T) = e₀ * exp( -Lᵥ/Rᵥ * (1/T - 1/T₀)),

where T is in Kelvin, Lᵥ the the latent heat of vaporization and Rᵥ the gas constant of water vapour, T₀ is 0˚C in Kelvin.

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::DateTime: current model time

  • start::DateTime: start time of simulation

  • period::Second: period to integrate for, set in set_period!(::Clock, ::Dates.Period)

  • timestep_counter::Int64: Counting all time steps during simulation

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

  • Δt::Dates.Millisecond: Time step

.

source
SpeedyWeather.ClockMethod
Clock(
+end

Temperatures in ocean.seasurfacetemperature have units of Kelvin, or NaN for no ocean. Note that neither sea surface temperature, land-sea mask or orography have to agree. It is possible to have an ocean on top of a mountain. For an ocean grid-cell that is (partially) masked by the land-sea mask, its value will be (fractionally) ignored in the calculation of surface fluxes (potentially leading to a zero flux depending on land surface temperatures). For an ocean grid-cell that is NaN but not masked by the land-sea mask, its value is always ignored.

source
SpeedyWeather.AquaPlanetType

AquaPlanet sea surface temperatures that are constant in time and longitude, but vary in latitude following a coslat². To be created like

ocean = AquaPlanet(spectral_grid, temp_equator=302, temp_poles=273)

Fields and options are

  • temp_equator::Any: [OPTION] Temperature on the Equator [K]

  • temp_poles::Any: [OPTION] Temperature at the poles [K]

source
SpeedyWeather.AquaPlanetMaskType

Land-sea mask with zero = sea everywhere.

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

The BarotropicModel contains all model components needed for the simulation of the barotropic vorticity equations. To be constructed like

model = BarotropicModel(spectral_grid; kwargs...)

with spectral_grid::SpectralGrid used to initalize all non-default components passed on as keyword arguments, e.g. planet=Earth(spectral_grid). Fields, representing model components, are

  • spectral_grid::SpectralGrid

  • device_setup::Any

  • geometry::Any

  • planet::Any

  • atmosphere::Any

  • coriolis::Any

  • forcing::Any

  • drag::Any

  • particle_advection::Any

  • initial_conditions::Any

  • random_process::Any

  • time_stepping::Any

  • spectral_transform::Any

  • implicit::Any

  • horizontal_diffusion::Any

  • output::Any

  • callbacks::Dict{Symbol, SpeedyWeather.AbstractCallback}

  • feedback::Any

source
SpeedyWeather.BulkRichardsonDragType

Boundary layer drag coefficient from the bulk Richardson number, following Frierson, 2006, Journal of the Atmospheric Sciences.

  • κ::Any: von Kármán constant [1]

  • z₀::Any: roughness length [m]

  • Ri_c::Any: Critical Richardson number for stable mixing cutoff [1]

  • drag_max::Base.RefValue: Maximum drag coefficient, κ²/log(zₐ/z₀) for zₐ from reference temperature

source
SpeedyWeather.ClausiusClapeyronType

Parameters for computing saturation vapour pressure of water using the Clausis-Clapeyron equation,

e(T) = e₀ * exp( -Lᵥ/Rᵥ * (1/T - 1/T₀)),

where T is in Kelvin, Lᵥ the the latent heat of condensation and Rᵥ the gas constant of water vapour

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

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

  • Lᵥ::AbstractFloat: Latent heat of condensation/vaporization of water [J/kg]

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

  • R_vapour::AbstractFloat: Gas constant of water vapour [J/kg/K]

  • R_dry::AbstractFloat: Gas constant of dry air [J/kg/K]

  • Lᵥ_Rᵥ::AbstractFloat: Latent heat of condensation divided by gas constant of water vapour [K]

  • T₀⁻¹::AbstractFloat: Inverse of T₀, one over 0°C in Kelvin

  • mol_ratio::AbstractFloat: Ratio of molecular masses [1] of water vapour over dry air (=Rdry/Rvapour).

source
SpeedyWeather.ClausiusClapeyronMethod

Functor: Saturation water vapour pressure as a function of temperature using the Clausius-Clapeyron equation,

e(T) = e₀ * exp( -Lᵥ/Rᵥ * (1/T - 1/T₀)),

where T is in Kelvin, Lᵥ the the latent heat of vaporization and Rᵥ the gas constant of water vapour, T₀ is 0˚C in Kelvin.

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::DateTime: current model time

  • start::DateTime: start time of simulation

  • period::Second: period to integrate for, set in set_period!(::Clock, ::Dates.Period)

  • timestep_counter::Int64: Counting all time steps during simulation

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

  • Δt::Dates.Millisecond: Time step

.

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

Create and initialize a clock from time_stepping

source
SpeedyWeather.CloudTopOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

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. Most column vectors have nlayers entries, from [1] at the top to [end] at the lowermost model level at the planetary boundary layer.

  • nlayers::Int64

  • nbands_shortwave::Int64

  • nbands_longwave::Int64

  • ij::Int64

  • jring::Int64

  • lond::Any

  • latd::Any

  • land_fraction::Any

  • orography::Any

  • u::Any

  • v::Any

  • temp::Any

  • humid::Any

  • ln_pres::Any

  • pres::Any

  • u_tend::Any

  • v_tend::Any

  • temp_tend::Any

  • humid_tend::Any

  • flux_u_upward::Any

  • flux_u_downward::Any

  • flux_v_upward::Any

  • flux_v_downward::Any

  • flux_temp_upward::Any

  • flux_temp_downward::Any

  • flux_humid_upward::Any

  • flux_humid_downward::Any

  • random_value::Any

  • boundary_layer_depth::Int64

  • boundary_layer_drag::Any

  • surface_geopotential::Any

  • surface_u::Any

  • surface_v::Any

  • surface_temp::Any

  • surface_humid::Any

  • surface_wind_speed::Any

  • skin_temperature_sea::Any

  • skin_temperature_land::Any

  • soil_moisture_availability::Any

  • surface_air_density::Any

  • sat_humid::Any

  • dry_static_energy::Any

  • temp_virt::Any

  • geopot::Any

  • cloud_top::Int64

  • precip_convection::Any

  • precip_large_scale::Any

  • cos_zenith::Any

  • albedo::Any

  • outgoing_longwave_radiation::Any

  • outgoing_shortwave_radiation::Any

  • optical_depth_shortwave::Any

  • optical_depth_longwave::Any

  • a::Any

  • b::Any

  • c::Any

  • d::Any

source
SpeedyWeather.ConstantOceanClimatologyType

Constant ocean climatology that reads monthly sea surface temperature fields from file, and interpolates them only for the initial conditions in time to be stored in the prognostic variables. It is therefore an ocean from climatology but without a seasonal cycle that is constant in time. To be created like

ocean = SeasonalOceanClimatology(spectral_grid)

and the ocean time is set with initialize!(model, time=time). Fields and options are

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

  • file::String: [OPTION] filename of sea surface temperatures

  • varname::String: [OPTION] Variable name in netcdf file

  • file_Grid::Type{<:AbstractGrid}: [OPTION] Grid the sea surface temperature file comes on

  • missing_value::Float64: [OPTION] The missing value in the data respresenting land

source
SpeedyWeather.ConvectiveHeatingType

Convective heating as defined by Lee and Kim, 2003, JAS implemented as convection parameterization. Fields are

  • nlat::Int64

  • time_scale::Second: [OPTION] Qmax heating strength as 1K/timescale

  • p₀::Any: [OPTION] Pressure of maximum heating [hPa]

  • σₚ::Any: [OPTION] Vertical extent of heating [hPa]

  • θ₀::Any: [OPTION] Latitude of heating [˚N]

  • σθ::Any: [OPTION] Latitudinal width of heating [˚]

  • lat_mask::Vector

source
SpeedyWeather.ConvectivePrecipitationOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

  • rate::SpeedyWeather.AbstractOutputVariable

source
SpeedyWeather.ConvectivePrecipitationRateOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.DeviceSetupType

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

  • device::SpeedyWeather.AbstractDevice: ::AbstractDevice, device the model is running on.

  • device_KA::Any: ::KernelAbstractions.Device, device for use with KernelAbstractions

  • n::Int64: workgroup size

source
SpeedyWeather.DiagnosticVariablesType

All diagnostic variables.

  • trunc::Int64: Spectral resolution: Max degree of spherical harmonics (0-based)

  • nlat_half::Int64: Grid resoltion: Number of latitude rings on one hemisphere (Equator incl.)

  • nlayers::Int64: Number of vertical layers

  • nparticles::Int64: Number of particles for particle advection

  • tendencies::Tendencies: Tendencies (spectral and grid) of the prognostic variables

  • grid::GridVariables: Gridded prognostic variables

  • dynamics::DynamicsVariables: Intermediate variables for the dynamical core

  • physics::PhysicsVariables: Global fields returned from physics parameterizations

  • particles::ParticleVariables{NF, ArrayType, ParticleVector, VectorType, Grid} where {NF, ArrayType, Grid, ParticleVector, VectorType}: Intermediate variables for the particle advection

  • column::ColumnVariables: Vertical column for the physics parameterizations

  • temp_average::Any: Average temperature of every horizontal layer [K]

  • scale::Base.RefValue: Scale applied to vorticity and divergence

source
SpeedyWeather.CloudTopOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

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. Most column vectors have nlayers entries, from [1] at the top to [end] at the lowermost model level at the planetary boundary layer.

  • nlayers::Int64

  • nbands_shortwave::Int64

  • nbands_longwave::Int64

  • ij::Int64

  • jring::Int64

  • lond::Any

  • latd::Any

  • land_fraction::Any

  • orography::Any

  • u::Any

  • v::Any

  • temp::Any

  • humid::Any

  • ln_pres::Any

  • pres::Any

  • u_tend::Any

  • v_tend::Any

  • temp_tend::Any

  • humid_tend::Any

  • flux_u_upward::Any

  • flux_u_downward::Any

  • flux_v_upward::Any

  • flux_v_downward::Any

  • flux_temp_upward::Any

  • flux_temp_downward::Any

  • flux_humid_upward::Any

  • flux_humid_downward::Any

  • random_value::Any

  • boundary_layer_depth::Int64

  • boundary_layer_drag::Any

  • surface_geopotential::Any

  • surface_u::Any

  • surface_v::Any

  • surface_temp::Any

  • surface_humid::Any

  • surface_wind_speed::Any

  • skin_temperature_sea::Any

  • skin_temperature_land::Any

  • soil_moisture_availability::Any

  • surface_air_density::Any

  • sat_humid::Any

  • dry_static_energy::Any

  • temp_virt::Any

  • geopot::Any

  • cloud_top::Int64

  • precip_convection::Any

  • precip_large_scale::Any

  • cos_zenith::Any

  • albedo::Any

  • outgoing_longwave_radiation::Any

  • outgoing_shortwave_radiation::Any

  • optical_depth_shortwave::Any

  • optical_depth_longwave::Any

  • a::Any

  • b::Any

  • c::Any

  • d::Any

source
SpeedyWeather.ConstantOceanClimatologyType

Constant ocean climatology that reads monthly sea surface temperature fields from file, and interpolates them only for the initial conditions in time to be stored in the prognostic variables. It is therefore an ocean from climatology but without a seasonal cycle that is constant in time. To be created like

ocean = SeasonalOceanClimatology(spectral_grid)

and the ocean time is set with initialize!(model, time=time). Fields and options are

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

  • file::String: [OPTION] filename of sea surface temperatures

  • varname::String: [OPTION] Variable name in netcdf file

  • file_Grid::Type{<:AbstractGrid}: [OPTION] Grid the sea surface temperature file comes on

  • missing_value::Float64: [OPTION] The missing value in the data respresenting land

source
SpeedyWeather.ConvectiveHeatingType

Convective heating as defined by Lee and Kim, 2003, JAS implemented as convection parameterization. Fields are

  • nlat::Int64

  • time_scale::Second: [OPTION] Qmax heating strength as 1K/timescale

  • p₀::Any: [OPTION] Pressure of maximum heating [hPa]

  • σₚ::Any: [OPTION] Vertical extent of heating [hPa]

  • θ₀::Any: [OPTION] Latitude of heating [˚N]

  • σθ::Any: [OPTION] Latitudinal width of heating [˚]

  • lat_mask::Vector

source
SpeedyWeather.ConvectivePrecipitationOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

  • rate::SpeedyWeather.AbstractOutputVariable

source
SpeedyWeather.ConvectivePrecipitationRateOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.DeviceSetupType

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

  • device::SpeedyWeather.AbstractDevice: ::AbstractDevice, device the model is running on.

  • device_KA::Any: ::KernelAbstractions.Device, device for use with KernelAbstractions

  • n::Int64: workgroup size

source
SpeedyWeather.DiagnosticVariablesType

All diagnostic variables.

  • trunc::Int64: Spectral resolution: Max degree of spherical harmonics (0-based)

  • nlat_half::Int64: Grid resoltion: Number of latitude rings on one hemisphere (Equator incl.)

  • nlayers::Int64: Number of vertical layers

  • nparticles::Int64: Number of particles for particle advection

  • tendencies::Tendencies: Tendencies (spectral and grid) of the prognostic variables

  • grid::GridVariables: Gridded prognostic variables

  • dynamics::DynamicsVariables: Intermediate variables for the dynamical core

  • physics::PhysicsVariables: Global fields returned from physics parameterizations

  • particles::ParticleVariables{NF, ArrayType, ParticleVector, VectorType, Grid} where {NF, ArrayType, Grid, ParticleVector, VectorType}: Intermediate variables for the particle advection

  • column::ColumnVariables: Vertical column for the physics parameterizations

  • temp_average::Any: Average temperature of every horizontal layer [K]

  • scale::Base.RefValue: Scale applied to vorticity and divergence

source
SpeedyWeather.DivergenceOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.DryBettsMillerType

The simplified Betts-Miller convection scheme from Frierson, 2007, https://doi.org/10.1175/JAS3935.1 but with humidity set to zero. Fields and options are

  • nlayers::Int64: number of vertical layers/levels

  • time_scale::Second: [OPTION] Relaxation time for profile adjustment

  • surface_temp::Any: [OPTION] Surface perturbation of temp to calculate the dry adiabat

source
SpeedyWeather.DynamicsVariablesType

Intermediate quantities for the dynamics of a given layer.

  • trunc::Int64

  • nlat_half::Int64

  • nlayers::Int64

  • a::Any: Multi-purpose a, 3D work array to be reused in various places

  • b::Any: Multi-purpose b, 3D work array to be reused in various places

  • a_grid::Any: Multi-purpose a, 3D work array to be reused in various places

  • b_grid::Any: Multi-purpose b, 3D work array to be reused in various places

  • a_2D::Any: Multi-purpose a, work array to be reused in various places

  • b_2D::Any: Multi-purpose b, work array to be reused in various places

  • a_2D_grid::Any: Multi-purpose a, work array to be reused in various places

  • b_2D_grid::Any: Multi-purpose b, work array to be reused in various places

  • uv∇lnp::Any: Pressure flux (uₖ, vₖ)⋅∇ln(pₛ)

  • uv∇lnp_sum_above::Any: Sum of Δσₖ-weighted uv∇lnp above

  • div_sum_above::Any: Sum of div_weighted from top to k

  • temp_virt::Any: Virtual temperature [K], spectral for geopotential

  • geopot::Any: Geopotential [m²/s²] on full layers

  • σ_tend::Any: Vertical velocity (dσ/dt), on half levels k+1/2 below, pointing to the surface (σ=1)

  • ∇lnp_x::Any: Zonal gradient of log surf pressure

  • ∇lnp_y::Any: Meridional gradient of log surf pressure

  • u_mean_grid::Any: Vertical average of zonal velocity [m/s]

  • v_mean_grid::Any: Vertical average of meridional velocity [m/s]

  • div_mean_grid::Any: Vertical average of divergence [1/s], grid

  • div_mean::Any: Vertical average of divergence [1/s], spectral

source
SpeedyWeather.DivergenceOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.DryBettsMillerType

The simplified Betts-Miller convection scheme from Frierson, 2007, https://doi.org/10.1175/JAS3935.1 but with humidity set to zero. Fields and options are

  • nlayers::Int64: number of vertical layers/levels

  • time_scale::Second: [OPTION] Relaxation time for profile adjustment

  • surface_temp::Any: [OPTION] Surface perturbation of temp to calculate the dry adiabat

source
SpeedyWeather.DynamicsVariablesType

Intermediate quantities for the dynamics of a given layer.

  • trunc::Int64

  • nlat_half::Int64

  • nlayers::Int64

  • a::Any: Multi-purpose a, 3D work array to be reused in various places

  • b::Any: Multi-purpose b, 3D work array to be reused in various places

  • a_grid::Any: Multi-purpose a, 3D work array to be reused in various places

  • b_grid::Any: Multi-purpose b, 3D work array to be reused in various places

  • a_2D::Any: Multi-purpose a, work array to be reused in various places

  • b_2D::Any: Multi-purpose b, work array to be reused in various places

  • a_2D_grid::Any: Multi-purpose a, work array to be reused in various places

  • b_2D_grid::Any: Multi-purpose b, work array to be reused in various places

  • uv∇lnp::Any: Pressure flux (uₖ, vₖ)⋅∇ln(pₛ)

  • uv∇lnp_sum_above::Any: Sum of Δσₖ-weighted uv∇lnp above

  • div_sum_above::Any: Sum of div_weighted from top to k

  • temp_virt::Any: Virtual temperature [K], spectral for geopotential

  • geopot::Any: Geopotential [m²/s²] on full layers

  • σ_tend::Any: Vertical velocity (dσ/dt), on half levels k+1/2 below, pointing to the surface (σ=1)

  • ∇lnp_x::Any: Zonal gradient of log surf pressure

  • ∇lnp_y::Any: Meridional gradient of log surf pressure

  • u_mean_grid::Any: Vertical average of zonal velocity [m/s]

  • v_mean_grid::Any: Vertical average of meridional velocity [m/s]

  • div_mean_grid::Any: Vertical average of divergence [1/s], grid

  • div_mean::Any: Vertical average of divergence [1/s], spectral

source
SpeedyWeather.DynamicsVariablesMethod
DynamicsVariables(
     SG::SpectralGrid
 ) -> DynamicsVariables{<:AbstractFloat, <:AbstractArray, <:AbstractArray, <:AbstractArray, <:AbstractArray, <:AbstractArray}
-

Generator function.

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::AbstractFloat: angular frequency of Earth's rotation [rad/s]

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

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

  • length_of_day::Second: Seconds in a daily rotation

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

  • length_of_year::Second: Seconds in an orbit around the sun

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

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

  • solar_constant::AbstractFloat: Total solar irradiance at the distance of 1 AU [W/m²]

source
SpeedyWeather.EarthAtmosphereType

Create a struct EarthAtmosphere <: AbstractAtmosphere, with the following physical/chemical characteristics. Keyword arguments are

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

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

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

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

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

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

  • mol_ratio::AbstractFloat: Ratio of gas constants: dry air / water vapour, often called ε [1]

  • μ_virt_temp::AbstractFloat: Virtual temperature Tᵥ calculation, Tᵥ = T(1 + μ*q), humidity q, absolute tempereature T

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

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

  • latent_heat_condensation::AbstractFloat: latent heat of condensation [J/kg] for consistency with specific humidity [kg/kg]

  • latent_heat_sublimation::AbstractFloat: latent heat of sublimation [J/kg]

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

  • pres_ref::AbstractFloat: surface reference pressure [Pa]

  • temp_ref::AbstractFloat: surface reference temperature [K]

  • moist_lapse_rate::AbstractFloat: reference moist-adiabatic temperature lapse rate [K/m]

  • dry_lapse_rate::AbstractFloat: reference dry-adiabatic temperature lapse rate [K/m]

  • layer_thickness::AbstractFloat: layer thickness for the shallow water model [m]

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{<: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_fraction::Float64: fraction of highest wavenumbers to smooth

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

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

source
SpeedyWeather.FeedbackType

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

  • verbose::Bool: print feedback to REPL?, default is isinteractive(), true in interactive REPL mode

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

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

  • id::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

  • 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

  • nlayers::Int64: number of vertical levels

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

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

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

  • lat::Vector{NF} where NF<:AbstractFloat: array of latitudes in radians (π...-π)

  • 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 (0˚...360˚) for each grid point in ring order

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

  • lons::Vector{NF} where NF<:AbstractFloat: longitude (0...2π) for each grid point in ring order

  • lats::Vector{NF} where NF<:AbstractFloat: latitude (-π/2...π/2) 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

  • full_to_half_interpolation::Vector{NF} where NF<:AbstractFloat: Full to half levels interpolation

source
SpeedyWeather.GeometryMethod
Geometry(SG::SpectralGrid) -> Geometry
-

Generator function for Geometry struct based on spectral_grid.

source
SpeedyWeather.GridVariablesType

Transformed prognostic variables (and u, v, temp_virt) into grid-point space.

  • nlat_half::Int64

  • nlayers::Int64

  • vor_grid::Any: Relative vorticity of the horizontal wind [1/s]

  • div_grid::Any: Divergence of the horizontal wind [1/s]

  • temp_grid::Any: Absolute temperature [K]

  • temp_virt_grid::Any: Virtual tempereature [K]

  • humid_grid::Any: Specific_humidity [kg/kg]

  • u_grid::Any: Zonal velocity [m/s]

  • v_grid::Any: Meridional velocity [m/s]

  • pres_grid::Any: Logarithm of surface pressure [Pa]

  • random_pattern::Any: Random pattern controlled by random process [1]

  • temp_grid_prev::Any: Absolute temperature [K] at previous time step

  • humid_grid_prev::Any: Specific humidity [kg/kg] at previous time step

  • u_grid_prev::Any: Zonal velocity [m/s] at previous time step

  • v_grid_prev::Any: Meridional velocity [m/s] at previous time step

  • pres_grid_prev::Any: Logarithm of surface pressure [Pa] at previous time step

.

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::AbstractFloat: angular frequency of Earth's rotation [rad/s]

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

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

  • length_of_day::Second: Seconds in a daily rotation

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

  • length_of_year::Second: Seconds in an orbit around the sun

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

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

  • solar_constant::AbstractFloat: Total solar irradiance at the distance of 1 AU [W/m²]

source
SpeedyWeather.EarthAtmosphereType

Create a struct EarthAtmosphere <: AbstractAtmosphere, with the following physical/chemical characteristics. Keyword arguments are

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

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

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

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

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

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

  • mol_ratio::AbstractFloat: Ratio of gas constants: dry air / water vapour, often called ε [1]

  • μ_virt_temp::AbstractFloat: Virtual temperature Tᵥ calculation, Tᵥ = T(1 + μ*q), humidity q, absolute tempereature T

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

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

  • latent_heat_condensation::AbstractFloat: latent heat of condensation [J/kg] for consistency with specific humidity [kg/kg]

  • latent_heat_sublimation::AbstractFloat: latent heat of sublimation [J/kg]

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

  • pres_ref::AbstractFloat: surface reference pressure [Pa]

  • temp_ref::AbstractFloat: surface reference temperature [K]

  • moist_lapse_rate::AbstractFloat: reference moist-adiabatic temperature lapse rate [K/m]

  • dry_lapse_rate::AbstractFloat: reference dry-adiabatic temperature lapse rate [K/m]

  • layer_thickness::AbstractFloat: layer thickness for the shallow water model [m]

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{<: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_fraction::Float64: fraction of highest wavenumbers to smooth

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

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

source
SpeedyWeather.FeedbackType

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

  • verbose::Bool: print feedback to REPL?, default is isinteractive(), true in interactive REPL mode

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

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

  • id::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

  • 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

  • nlayers::Int64: number of vertical levels

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

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

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

  • lat::Vector{NF} where NF<:AbstractFloat: array of latitudes in radians (π...-π)

  • 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 (0˚...360˚) for each grid point in ring order

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

  • lons::Vector{NF} where NF<:AbstractFloat: longitude (0...2π) for each grid point in ring order

  • lats::Vector{NF} where NF<:AbstractFloat: latitude (-π/2...π/2) 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

  • full_to_half_interpolation::Vector{NF} where NF<:AbstractFloat: Full to half levels interpolation

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

Generator function for Geometry struct based on spectral_grid.

source
SpeedyWeather.GridVariablesType

Transformed prognostic variables (and u, v, temp_virt) into grid-point space.

  • nlat_half::Int64

  • nlayers::Int64

  • vor_grid::Any: Relative vorticity of the horizontal wind [1/s]

  • div_grid::Any: Divergence of the horizontal wind [1/s]

  • temp_grid::Any: Absolute temperature [K]

  • temp_virt_grid::Any: Virtual tempereature [K]

  • humid_grid::Any: Specific_humidity [kg/kg]

  • u_grid::Any: Zonal velocity [m/s]

  • v_grid::Any: Meridional velocity [m/s]

  • pres_grid::Any: Logarithm of surface pressure [Pa]

  • random_pattern::Any: Random pattern controlled by random process [1]

  • temp_grid_prev::Any: Absolute temperature [K] at previous time step

  • humid_grid_prev::Any: Specific humidity [kg/kg] at previous time step

  • u_grid_prev::Any: Zonal velocity [m/s] at previous time step

  • v_grid_prev::Any: Meridional velocity [m/s] at previous time step

  • pres_grid_prev::Any: Logarithm of surface pressure [Pa] at previous time step

.

source
SpeedyWeather.GridVariablesMethod
GridVariables(
     SG::SpectralGrid
 ) -> GridVariables{<:AbstractFloat, <:AbstractArray, <:AbstractArray, <:AbstractArray}
-

Generator function.

source
SpeedyWeather.HeldSuarezType

Temperature relaxation from Held and Suarez, 1996 BAMS

  • nlat::Int64: number of latitude rings

  • nlayers::Int64: number of vertical levels

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

  • relax_time_slow::Second: time scale for slow global relaxation

  • relax_time_fast::Second: time scale for faster tropical surface relaxation

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

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

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

  • Δθz::AbstractFloat: 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...) -> HeldSuarez
-

create a HeldSuarez temperature relaxation with arrays allocated given spectral_grid

source
SpeedyWeather.HumidityOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.HyperDiffusionType

Horizontal hyper diffusion of vor, div, temp, humid; implicitly in spectral space with a power of the Laplacian (default = 4) and the strength controlled by time_scale (default = 1 hour). For vorticity and divergence, by default, the time_scale (=1/strength of diffusion) is reduced with increasing resolution through resolution_scaling and the power is linearly decreased in the vertical above the tapering_σ sigma level to power_stratosphere (default 2).

For the BarotropicModel and ShallowWaterModel no tapering or scaling is applied. Fields and options are

  • trunc::Int64: spectral resolution

  • nlayers::Int64: number of vertical levels

  • power::Any: [OPTION] power of Laplacian

  • time_scale::Second: [OPTION] diffusion time scale

  • time_scale_div::Second: [OPTION] diffusion time scale for temperature and humidity

  • resolution_scaling::Any: [OPTION] stronger diffusion with resolution? 0: constant with trunc, 1: (inverse) linear with trunc, etc

  • power_stratosphere::Any: [OPTION] different power for tropopause/stratosphere

  • tapering_σ::Any: [OPTION] linearly scale towards power_stratosphere above this σ

  • expl::Any

  • impl::Any

  • expl_div::Any

  • impl_div::Any

source
SpeedyWeather.HeldSuarezType

Temperature relaxation from Held and Suarez, 1996 BAMS

  • nlat::Int64: number of latitude rings

  • nlayers::Int64: number of vertical levels

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

  • relax_time_slow::Second: time scale for slow global relaxation

  • relax_time_fast::Second: time scale for faster tropical surface relaxation

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

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

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

  • Δθz::AbstractFloat: 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...) -> HeldSuarez
+

create a HeldSuarez temperature relaxation with arrays allocated given spectral_grid

source
SpeedyWeather.HumidityOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.HyperDiffusionType

Horizontal hyper diffusion of vor, div, temp, humid; implicitly in spectral space with a power of the Laplacian (default = 4) and the strength controlled by time_scale (default = 1 hour). For vorticity and divergence, by default, the time_scale (=1/strength of diffusion) is reduced with increasing resolution through resolution_scaling and the power is linearly decreased in the vertical above the tapering_σ sigma level to power_stratosphere (default 2).

For the BarotropicModel and ShallowWaterModel no tapering or scaling is applied. Fields and options are

  • trunc::Int64: spectral resolution

  • nlayers::Int64: number of vertical levels

  • power::Any: [OPTION] power of Laplacian

  • time_scale::Second: [OPTION] diffusion time scale

  • time_scale_div::Second: [OPTION] diffusion time scale for temperature and humidity

  • resolution_scaling::Any: [OPTION] stronger diffusion with resolution? 0: constant with trunc, 1: (inverse) linear with trunc, etc

  • power_stratosphere::Any: [OPTION] different power for tropopause/stratosphere

  • tapering_σ::Any: [OPTION] linearly scale towards power_stratosphere above this σ

  • expl::Any

  • impl::Any

  • expl_div::Any

  • impl_div::Any

source
SpeedyWeather.HyperDiffusionMethod
HyperDiffusion(
     spectral_grid::SpectralGrid;
     kwargs...
 ) -> HyperDiffusion{<:AbstractFloat}
-

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

source
SpeedyWeather.ImplicitCondensationType

Large scale condensation as with implicit precipitation.

  • relative_humidity_threshold::AbstractFloat: Relative humidity threshold [1 = 100%] to trigger condensation

  • time_scale::AbstractFloat: Time scale in multiples of time step Δt, the larger the less immediate

source
SpeedyWeather.ImplicitPrimitiveEquationType

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

  • nlayers::Int64: number of vertical layers

  • α::AbstractFloat: 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 on first time step

  • ξ::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.ImplicitCondensationType

Large scale condensation as with implicit precipitation.

  • relative_humidity_threshold::AbstractFloat: Relative humidity threshold [1 = 100%] to trigger condensation

  • time_scale::AbstractFloat: Time scale in multiples of time step Δt, the larger the less immediate

source
SpeedyWeather.ImplicitPrimitiveEquationType

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

  • nlayers::Int64: number of vertical layers

  • α::AbstractFloat: 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 on first time step

  • ξ::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

  • α::AbstractFloat: [OPTION] coefficient for semi-implicit computations to filter gravity waves, 0.5 <= α <= 1

  • 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

  • α::AbstractFloat: [OPTION] coefficient for semi-implicit computations to filter gravity waves, 0.5 <= α <= 1

  • 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.InterfaceDisplacementOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.InterfaceDisplacementOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

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

create a JablonowskiRelaxation temperature relaxation with arrays allocated given spectral_grid

source
SpeedyWeather.JablonowskiTemperatureType

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

  • σ_tropopause::Float64: Sigma coordinates of the tropopause [1]

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

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

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

source
SpeedyWeather.JeevanjeeRadiationType

Temperature flux longwave radiation from Jeevanjee and Romps, 2018, following Seeley and Wordsworth, 2023, eq (1)

dF/dT = α*(T_t - T)

with F the upward temperature flux between two layers with temperature difference dT, α = 0.025 W/m²/K² and T_t = 200K a prescribed tropopause temperature. Flux into the lowermost layer is 0. Flux out of uppermost layer also 0, but dT/dt = (T_t - T)/τ is added to relax the uppermost layer towards the tropopause temperature T_t with time scale τ = 24h (Seeley and Wordsworth, 2023 use 6h, which is unstable a low resolutions here). Fields are

  • α::Any: Radiative forcing constant (W/m²/K²)

  • temp_tropopause::Any: Tropopause temperature [K]

  • time_scale::Second: Tropopause relaxation time scale to temp_tropopause

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

  • nlayers::Int64: Number of vertical layers

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

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

  • sigma::Any: sigma level [1], vertical location of jet

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

  • time_scale::Second: time scale [days]

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

  • tapering::Vector: precomputed vertical tapering

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{<:AbstractGrid}: Grid the land-sea mask file comes on

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

source
SpeedyWeather.LargeScalePrecipitationOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

  • rate::SpeedyWeather.AbstractOutputVariable

source
SpeedyWeather.LargeScalePrecipitationRateOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.LeapfrogType

Leapfrog time stepping defined by the following fields

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

  • nsteps::Int64: Number of timesteps stored simultaneously in prognostic variables

  • Δt_at_T31::Second: Time step in minutes for T31, scale linearly to trunc

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

  • adjust_with_output::Bool: Adjust ΔtatT31 with the outputdt to reach outputdt exactly in integer time steps

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

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

  • Δt_millisec::Dates.Millisecond: time step Δt [ms] at specified resolution

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

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

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

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

  • nlayers::Int64

  • σb::AbstractFloat

  • time_scale::Second

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

source
SpeedyWeather.LinearDragMethod
LinearDrag(SG::SpectralGrid; kwargs...) -> LinearDrag
-

Generator function using nlayers from SG::SpectralGrid

source
SpeedyWeather.MeridionalVelocityOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.JablonowskiTemperatureType

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

  • σ_tropopause::Float64: Sigma coordinates of the tropopause [1]

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

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

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

source
SpeedyWeather.JeevanjeeRadiationType

Temperature flux longwave radiation from Jeevanjee and Romps, 2018, following Seeley and Wordsworth, 2023, eq (1)

dF/dT = α*(T_t - T)

with F the upward temperature flux between two layers with temperature difference dT, α = 0.025 W/m²/K² and T_t = 200K a prescribed tropopause temperature. Flux into the lowermost layer is 0. Flux out of uppermost layer also 0, but dT/dt = (T_t - T)/τ is added to relax the uppermost layer towards the tropopause temperature T_t with time scale τ = 24h (Seeley and Wordsworth, 2023 use 6h, which is unstable a low resolutions here). Fields are

  • α::Any: Radiative forcing constant (W/m²/K²)

  • temp_tropopause::Any: Tropopause temperature [K]

  • time_scale::Second: Tropopause relaxation time scale to temp_tropopause

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

  • nlayers::Int64: Number of vertical layers

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

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

  • sigma::Any: sigma level [1], vertical location of jet

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

  • time_scale::Second: time scale [days]

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

  • tapering::Vector: precomputed vertical tapering

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{<:AbstractGrid}: Grid the land-sea mask file comes on

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

source
SpeedyWeather.LargeScalePrecipitationOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

  • rate::SpeedyWeather.AbstractOutputVariable

source
SpeedyWeather.LargeScalePrecipitationRateOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.LeapfrogType

Leapfrog time stepping defined by the following fields

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

  • nsteps::Int64: Number of timesteps stored simultaneously in prognostic variables

  • Δt_at_T31::Second: Time step in minutes for T31, scale linearly to trunc

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

  • adjust_with_output::Bool: Adjust ΔtatT31 with the outputdt to reach outputdt exactly in integer time steps

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

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

  • Δt_millisec::Dates.Millisecond: time step Δt [ms] at specified resolution

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

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

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

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

  • nlayers::Int64

  • σb::AbstractFloat

  • time_scale::Second

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

source
SpeedyWeather.LinearDragMethod
LinearDrag(SG::SpectralGrid; kwargs...) -> LinearDrag
+

Generator function using nlayers from SG::SpectralGrid

source
SpeedyWeather.MeridionalVelocityOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.NetCDFOutputType
NetCDFOutput(
     S::SpectralGrid;
     ...
 ) -> NetCDFOutput{_A, _B, Interpolator} where {_A, _B, Interpolator<:(AnvilInterpolator{Float32})}
@@ -72,200 +72,200 @@
     output_dt,
     kwargs...
 ) -> NetCDFOutput{_A, _B, Interpolator} where {_A, _B, Interpolator<:(AnvilInterpolator{Float32})}
-

Constructor for NetCDFOutput based on S::SpectralGrid and optionally the Model type (e.g. ShallowWater, PrimitiveWet) as second positional argument. The output grid is optionally determined by keyword arguments output_Grid (its type, full grid required), nlat_half (resolution) and output_NF (number format). By default, uses the full grid equivalent of the grid and resolution used in SpectralGrid S.

source
SpeedyWeather.NetCDFOutputType

Output writer for a netCDF file with (re-)gridded variables. Interpolates non-rectangular grids. Fields are

  • active::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::DateTime

  • output_dt::Second: [OPTION] output frequency, time step

  • variables::Dict{Symbol, SpeedyWeather.AbstractOutputVariable}: [OPTION] dictionary of variables to output, e.g. u, v, vor, div, pres, temp, humid

  • output_every_n_steps::Int64

  • timestep_counter::Int64

  • output_counter::Int64

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

  • interpolator::Any

  • grid2D::Any

  • grid3D::Any

source
SpeedyWeather.NoOrographyType

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

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

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

source
SpeedyWeather.OrographyOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.OutgoingLongwaveRadiationOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.OutgoingShortwaveRadiationOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.ParticleType

Particle with location lon (longitude), lat (latitude) and σ (vertical coordinate). Longitude is assumed to be in [0,360˚E), latitude in [-90˚,90˚N] and σ in [0,1] but not strictly enforced at creation, see mod(::Particle) and ismod(::Particle). A particle is either active or inactive, determined by the Boolean in it's 2nd type parameter. By default, a particle is active, of number format DEFAULT_NF and at 0˚N, 0˚E, σ=0.

  • lon::AbstractFloat: longitude in [0,360˚]

  • lat::AbstractFloat: latitude [-90˚,90˚]

  • σ::AbstractFloat: vertical sigma coordinate [0 (top) to 1 (surface)]

source
SpeedyWeather.ParticleTrackerType

A ParticleTracker is implemented as a callback to output the trajectories of particles from particle advection. To be added like

add!(model.callbacks, ParticleTracker(spectral_grid; kwargs...))

Output done via netCDF. Fields and options are

  • schedule::Schedule: [OPTION] when to schedule particle tracking

  • file_name::String: [OPTION] File name for netCDF file

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

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

  • keepbits::Int64: [OPTION] mantissa bits to keep, (14, 15, 16) means at least (2km, 1km, 500m) accurate locations

  • nparticles::Int64: Number of particles to track

  • netcdf_file::Union{Nothing, NCDatasets.NCDataset}: The netcdf file to be written into, will be created at initialization

  • lon::Vector

  • lat::Vector

  • σ::Vector

source
SpeedyWeather.ParticleVariablesType

Diagnostic variables for the particle advection

  • nparticles::Int64: Number of particles

  • nlat_half::Int64: Number of latitudes on one hemisphere (Eq. incld.), resolution parameter of Grid

  • locations::Any: Work array: particle locations

  • u::Any: Work array: velocity u

  • v::Any: Work array: velocity v

  • σ_tend::Any: Work array: velocity w = dσ/dt

  • interpolator::AnvilInterpolator{NF, Grid} where {NF, Grid}: Interpolator to interpolate velocity fields onto particle positions

source
SpeedyWeather.ParticleVariablesMethod
ParticleVariables(
+

Constructor for NetCDFOutput based on S::SpectralGrid and optionally the Model type (e.g. ShallowWater, PrimitiveWet) as second positional argument. The output grid is optionally determined by keyword arguments output_Grid (its type, full grid required), nlat_half (resolution) and output_NF (number format). By default, uses the full grid equivalent of the grid and resolution used in SpectralGrid S.

source
SpeedyWeather.NetCDFOutputType

Output writer for a netCDF file with (re-)gridded variables. Interpolates non-rectangular grids. Fields are

  • active::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::DateTime

  • output_dt::Second: [OPTION] output frequency, time step

  • variables::Dict{Symbol, SpeedyWeather.AbstractOutputVariable}: [OPTION] dictionary of variables to output, e.g. u, v, vor, div, pres, temp, humid

  • output_every_n_steps::Int64

  • timestep_counter::Int64

  • output_counter::Int64

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

  • interpolator::Any

  • grid2D::Any

  • grid3D::Any

source
SpeedyWeather.NoOrographyType

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

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

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

source
SpeedyWeather.OrographyOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.OutgoingLongwaveRadiationOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.OutgoingShortwaveRadiationOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.ParticleType

Particle with location lon (longitude), lat (latitude) and σ (vertical coordinate). Longitude is assumed to be in [0,360˚E), latitude in [-90˚,90˚N] and σ in [0,1] but not strictly enforced at creation, see mod(::Particle) and ismod(::Particle). A particle is either active or inactive, determined by the Boolean in it's 2nd type parameter. By default, a particle is active, of number format DEFAULT_NF and at 0˚N, 0˚E, σ=0.

  • lon::AbstractFloat: longitude in [0,360˚]

  • lat::AbstractFloat: latitude [-90˚,90˚]

  • σ::AbstractFloat: vertical sigma coordinate [0 (top) to 1 (surface)]

source
SpeedyWeather.ParticleTrackerType

A ParticleTracker is implemented as a callback to output the trajectories of particles from particle advection. To be added like

add!(model.callbacks, ParticleTracker(spectral_grid; kwargs...))

Output done via netCDF. Fields and options are

  • schedule::Schedule: [OPTION] when to schedule particle tracking

  • file_name::String: [OPTION] File name for netCDF file

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

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

  • keepbits::Int64: [OPTION] mantissa bits to keep, (14, 15, 16) means at least (2km, 1km, 500m) accurate locations

  • nparticles::Int64: Number of particles to track

  • netcdf_file::Union{Nothing, NCDatasets.NCDataset}: The netcdf file to be written into, will be created at initialization

  • lon::Vector

  • lat::Vector

  • σ::Vector

source
SpeedyWeather.ParticleVariablesType

Diagnostic variables for the particle advection

  • nparticles::Int64: Number of particles

  • nlat_half::Int64: Number of latitudes on one hemisphere (Eq. incld.), resolution parameter of Grid

  • locations::Any: Work array: particle locations

  • u::Any: Work array: velocity u

  • v::Any: Work array: velocity v

  • σ_tend::Any: Work array: velocity w = dσ/dt

  • interpolator::AnvilInterpolator{NF, Grid} where {NF, Grid}: Interpolator to interpolate velocity fields onto particle positions

source
SpeedyWeather.ParticleVariablesMethod
ParticleVariables(
     SG::SpectralGrid
 ) -> ParticleVariables{var"#s252", var"#s251", var"#s146", _A, <:AbstractGrid} where {var"#s252"<:AbstractFloat, var"#s251"<:AbstractArray, var"#s146"<:AbstractArray, _A}
-

Generator function.

source
SpeedyWeather.PhysicsVariablesType

Diagnostic variables of the physical parameterizations.

  • nlat_half::Int64

  • precip_large_scale::Any: Accumulated large-scale precipitation [m]

  • precip_convection::Any: Accumulated large-scale precipitation [m]

  • cloud_top::Any: Cloud top [m]

  • soil_moisture_availability::Any: Availability of soil moisture to evaporation [1]

  • surface_flux_heat::Any: Surface flux of heat [W/m^2]

  • surface_flux_humid::Any: Surface flux of humidity [?]

  • outgoing_shortwave_radiation::Any: Outgoing shortwave radiation [W/m^2]

  • outgoing_longwave_radiation::Any: Outgoing longwave radiation [W/m^2]

  • cos_zenith::Any: Cosine of solar zenith angle [1]

source
SpeedyWeather.PhysicsVariablesType

Diagnostic variables of the physical parameterizations.

  • nlat_half::Int64

  • precip_large_scale::Any: Accumulated large-scale precipitation [m]

  • precip_convection::Any: Accumulated large-scale precipitation [m]

  • cloud_top::Any: Cloud top [m]

  • soil_moisture_availability::Any: Availability of soil moisture to evaporation [1]

  • surface_flux_heat::Any: Surface flux of heat [W/m^2]

  • surface_flux_humid::Any: Surface flux of humidity [?]

  • outgoing_shortwave_radiation::Any: Outgoing shortwave radiation [W/m^2]

  • outgoing_longwave_radiation::Any: Outgoing longwave radiation [W/m^2]

  • cos_zenith::Any: Cosine of solar zenith angle [1]

source
SpeedyWeather.PrimitiveDryModelType

The PrimitiveDryModel contains all model components (themselves structs) needed for the simulation of the primitive equations without humidity. To be constructed like

model = PrimitiveDryModel(spectral_grid; kwargs...)

with spectral_grid::SpectralGrid used to initalize all non-default components passed on as keyword arguments, e.g. planet=Earth(spectral_grid). Fields, representing model components, are

  • spectral_grid::SpectralGrid

  • device_setup::Any

  • dynamics::Bool

  • geometry::Any

  • planet::Any

  • atmosphere::Any

  • coriolis::Any

  • geopotential::Any

  • adiabatic_conversion::Any

  • particle_advection::Any

  • initial_conditions::Any

  • random_process::Any

  • forcing::Any

  • drag::Any

  • orography::Any

  • land_sea_mask::Any

  • ocean::Any

  • land::Any

  • solar_zenith::Any

  • albedo::Any

  • physics::Bool

  • boundary_layer_drag::Any

  • temperature_relaxation::Any

  • vertical_diffusion::Any

  • surface_thermodynamics::Any

  • surface_wind::Any

  • surface_heat_flux::Any

  • convection::Any

  • optical_depth::Any

  • shortwave_radiation::Any

  • longwave_radiation::Any

  • stochastic_physics::Any

  • time_stepping::Any

  • spectral_transform::Any

  • implicit::Any

  • horizontal_diffusion::Any

  • vertical_advection::Any

  • output::Any

  • callbacks::Dict{Symbol, SpeedyWeather.AbstractCallback}

  • feedback::Any

source
SpeedyWeather.PrimitiveWetModelType

The PrimitiveWetModel contains all model components (themselves structs) needed for the simulation of the primitive equations with humidity. To be constructed like

model = PrimitiveWetModel(spectral_grid; kwargs...)

with spectral_grid::SpectralGrid used to initalize all non-default components passed on as keyword arguments, e.g. planet=Earth(spectral_grid). Fields, representing model components, are

  • spectral_grid::SpectralGrid

  • device_setup::Any

  • dynamics::Bool

  • geometry::Any

  • planet::Any

  • atmosphere::Any

  • coriolis::Any

  • geopotential::Any

  • adiabatic_conversion::Any

  • particle_advection::Any

  • initial_conditions::Any

  • random_process::Any

  • forcing::Any

  • drag::Any

  • orography::Any

  • land_sea_mask::Any

  • ocean::Any

  • land::Any

  • solar_zenith::Any

  • albedo::Any

  • soil::Any

  • vegetation::Any

  • physics::Bool

  • clausius_clapeyron::Any

  • boundary_layer_drag::Any

  • temperature_relaxation::Any

  • vertical_diffusion::Any

  • surface_thermodynamics::Any

  • surface_wind::Any

  • surface_heat_flux::Any

  • surface_evaporation::Any

  • large_scale_condensation::Any

  • convection::Any

  • optical_depth::Any

  • shortwave_radiation::Any

  • longwave_radiation::Any

  • stochastic_physics::Any

  • time_stepping::Any

  • spectral_transform::Any

  • implicit::Any

  • horizontal_diffusion::Any

  • vertical_advection::Any

  • hole_filling::Any

  • output::Any

  • callbacks::Dict{Symbol, SpeedyWeather.AbstractCallback}

  • feedback::Any

source
SpeedyWeather.PrimitiveDryModelType

The PrimitiveDryModel contains all model components (themselves structs) needed for the simulation of the primitive equations without humidity. To be constructed like

model = PrimitiveDryModel(spectral_grid; kwargs...)

with spectral_grid::SpectralGrid used to initalize all non-default components passed on as keyword arguments, e.g. planet=Earth(spectral_grid). Fields, representing model components, are

  • spectral_grid::SpectralGrid

  • device_setup::Any

  • dynamics::Bool

  • geometry::Any

  • planet::Any

  • atmosphere::Any

  • coriolis::Any

  • geopotential::Any

  • adiabatic_conversion::Any

  • particle_advection::Any

  • initial_conditions::Any

  • random_process::Any

  • forcing::Any

  • drag::Any

  • orography::Any

  • land_sea_mask::Any

  • ocean::Any

  • land::Any

  • solar_zenith::Any

  • albedo::Any

  • physics::Bool

  • boundary_layer_drag::Any

  • temperature_relaxation::Any

  • vertical_diffusion::Any

  • surface_thermodynamics::Any

  • surface_wind::Any

  • surface_heat_flux::Any

  • convection::Any

  • optical_depth::Any

  • shortwave_radiation::Any

  • longwave_radiation::Any

  • stochastic_physics::Any

  • time_stepping::Any

  • spectral_transform::Any

  • implicit::Any

  • horizontal_diffusion::Any

  • vertical_advection::Any

  • output::Any

  • callbacks::Dict{Symbol, SpeedyWeather.AbstractCallback}

  • feedback::Any

source
SpeedyWeather.PrimitiveWetModelType

The PrimitiveWetModel contains all model components (themselves structs) needed for the simulation of the primitive equations with humidity. To be constructed like

model = PrimitiveWetModel(spectral_grid; kwargs...)

with spectral_grid::SpectralGrid used to initalize all non-default components passed on as keyword arguments, e.g. planet=Earth(spectral_grid). Fields, representing model components, are

  • spectral_grid::SpectralGrid

  • device_setup::Any

  • dynamics::Bool

  • geometry::Any

  • planet::Any

  • atmosphere::Any

  • coriolis::Any

  • geopotential::Any

  • adiabatic_conversion::Any

  • particle_advection::Any

  • initial_conditions::Any

  • random_process::Any

  • forcing::Any

  • drag::Any

  • orography::Any

  • land_sea_mask::Any

  • ocean::Any

  • land::Any

  • solar_zenith::Any

  • albedo::Any

  • soil::Any

  • vegetation::Any

  • physics::Bool

  • clausius_clapeyron::Any

  • boundary_layer_drag::Any

  • temperature_relaxation::Any

  • vertical_diffusion::Any

  • surface_thermodynamics::Any

  • surface_wind::Any

  • surface_heat_flux::Any

  • surface_evaporation::Any

  • large_scale_condensation::Any

  • convection::Any

  • optical_depth::Any

  • shortwave_radiation::Any

  • longwave_radiation::Any

  • stochastic_physics::Any

  • time_stepping::Any

  • spectral_transform::Any

  • implicit::Any

  • horizontal_diffusion::Any

  • vertical_advection::Any

  • hole_filling::Any

  • output::Any

  • callbacks::Dict{Symbol, SpeedyWeather.AbstractCallback}

  • feedback::Any

source
SpeedyWeather.PrognosticVariablesMethod
PrognosticVariables(
     SG::SpectralGrid,
     model::AbstractModel
 ) -> PrognosticVariables{var"#s252", var"#s251", _A, <:AbstractArray, <:AbstractArray, <:AbstractArray, <:AbstractArray} where {var"#s252"<:AbstractFloat, var"#s251"<:AbstractArray, _A}
-

Generator function.

source
SpeedyWeather.PrognosticVariablesMethod
PrognosticVariables(
     SG::SpectralGrid;
     nsteps
 ) -> PrognosticVariables{var"#s252", var"#s251", _A, <:AbstractArray, <:AbstractArray, <:AbstractArray, <:AbstractArray} where {var"#s252"<:AbstractFloat, var"#s251"<:AbstractArray, _A}
-

Generator function.

source
SpeedyWeather.RandomPatternOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

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

Rossby-Haurwitz wave initial conditions as in Williamson et al. 1992, J Computational Physics with an additional cut-off amplitude c to filter out tiny harmonics in the vorticity field. Parameters are

  • m::Int64

  • ω::Float64

  • K::Float64

  • c::Float64

source
SpeedyWeather.ScheduleType

A schedule for callbacks, to execute them periodically after a given period has passed (first timestep excluded) or on/after specific times (initial time excluded). For two consecutive time steps i, i+1, an event is scheduled at i+1 when it occurs in (i,i+1]. Similarly, a periodic schedule of period p will be executed at start+p, but p is rounded to match the multiple of the model timestep. Periodic schedules and event schedules can be combined, executing at both. A Schedule is supposed to be added into callbacks as fields

Base.@kwdef struct MyCallback
+

Generator function.

source
SpeedyWeather.RandomPatternOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

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

Rossby-Haurwitz wave initial conditions as in Williamson et al. 1992, J Computational Physics with an additional cut-off amplitude c to filter out tiny harmonics in the vorticity field. Parameters are

  • m::Int64

  • ω::Float64

  • K::Float64

  • c::Float64

source
SpeedyWeather.ScheduleType

A schedule for callbacks, to execute them periodically after a given period has passed (first timestep excluded) or on/after specific times (initial time excluded). For two consecutive time steps i, i+1, an event is scheduled at i+1 when it occurs in (i,i+1]. Similarly, a periodic schedule of period p will be executed at start+p, but p is rounded to match the multiple of the model timestep. Periodic schedules and event schedules can be combined, executing at both. A Schedule is supposed to be added into callbacks as fields

Base.@kwdef struct MyCallback
     schedule::Schedule = Schedule(every=Day(1))
     other_fields
-end

see also initialize!(::Schedule,::Clock) and isscheduled(::Schedule,::Clock). Fields

  • every::Second: [OPTION] Execute every time period, first timestep excluded. Default=never.

  • times::Vector{DateTime}: [OPTION] Events scheduled at times

  • schedule::BitVector: Actual schedule, true=execute this timestep, false=don't

  • steps::Int64: Number of scheduled executions

  • counter::Int64: Count up the number of executions

source
SpeedyWeather.ScheduleMethod
Schedule(times::DateTime...) -> Schedule
-

A Schedule based on DateTime arguments, For two consecutive time steps i, i+1, an event is scheduled at i+1 when it occurs in (i,i+1].

source
SpeedyWeather.SeaSurfaceTemperatureOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.SeasonalOceanClimatologyType

Seasonal ocean climatology that reads monthly sea surface temperature fields from file, and interpolates them in time regularly (default every 3 days) to be stored in the prognostic variables. Fields and options are

  • nlat_half::Int64: number of latitudes on one hemisphere, Equator included

  • path::String: [OPTION] Path to the folder containing the sea surface temperatures, pkg path default

  • file::String: [OPTION] Filename of sea surface temperatures

  • varname::String: [OPTION] Variable name in netcdf file

  • file_Grid::Type{<:AbstractGrid}: [OPTION] Grid the sea surface temperature file comes on

  • missing_value::Any: [OPTION] The missing value in the data respresenting land

  • monthly_temperature::Vector{Grid} where {NF, Grid<:AbstractGrid{NF}}: Monthly sea surface temperatures [K], interpolated onto Grid

source
SpeedyWeather.ShallowWaterModelType

The ShallowWaterModel contains all model components needed for the simulation of the shallow water equations. To be constructed like

model = ShallowWaterModel(spectral_grid; kwargs...)

with spectral_grid::SpectralGrid used to initalize all non-default components passed on as keyword arguments, e.g. planet=Earth(spectral_grid). Fields, representing model components, are

  • spectral_grid::SpectralGrid

  • device_setup::Any

  • geometry::Any

  • planet::Any

  • atmosphere::Any

  • coriolis::Any

  • orography::Any

  • forcing::Any

  • drag::Any

  • particle_advection::Any

  • initial_conditions::Any

  • random_process::Any

  • time_stepping::Any

  • spectral_transform::Any

  • implicit::Any

  • horizontal_diffusion::Any

  • output::Any

  • callbacks::Dict{Symbol, SpeedyWeather.AbstractCallback}

  • feedback::Any

source
SpeedyWeather.SimplifiedBettsMillerType

The simplified Betts-Miller convection scheme from Frierson, 2007, https://doi.org/10.1175/JAS3935.1. This implements the qref-formulation in their paper. Fields and options are

  • nlayers::Int64: number of vertical layers

  • time_scale::Second: [OPTION] Relaxation time for profile adjustment

  • relative_humidity::Any: [OPTION] Relative humidity for reference profile

  • surface_temp_humid::Any: [OPTION] Surface perturbation of temp, humid to calculate the moist pseudo adiabat

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::AbstractModel: all parameters, constant at runtime

source
SpeedyWeather.SinSolarDeclinationType

Coefficients to calculate the solar declination angle δ [radians] based on a simple sine function, with Earth's axial tilt as amplitude, equinox as phase shift.

  • axial_tilt::Any

  • equinox::DateTime

  • length_of_year::Second

  • length_of_day::Second

source
SpeedyWeather.SinSolarDeclinationMethod

SinSolarDeclination functor, computing the solar declination angle of angular fraction of year g [radians] using the coefficients of the SinSolarDeclination struct.

source
SpeedyWeather.SolarDeclinationType

Coefficients to calculate the solar declination angle δ from

δ = 0.006918    - 0.399912*cos(g)  + 0.070257*sin(g)
+end

see also initialize!(::Schedule,::Clock) and isscheduled(::Schedule,::Clock). Fields

  • every::Second: [OPTION] Execute every time period, first timestep excluded. Default=never.

  • times::Vector{DateTime}: [OPTION] Events scheduled at times

  • schedule::BitVector: Actual schedule, true=execute this timestep, false=don't

  • steps::Int64: Number of scheduled executions

  • counter::Int64: Count up the number of executions

source
SpeedyWeather.ScheduleMethod
Schedule(times::DateTime...) -> Schedule
+

A Schedule based on DateTime arguments, For two consecutive time steps i, i+1, an event is scheduled at i+1 when it occurs in (i,i+1].

source
SpeedyWeather.SeaSurfaceTemperatureOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.SeasonalOceanClimatologyType

Seasonal ocean climatology that reads monthly sea surface temperature fields from file, and interpolates them in time regularly (default every 3 days) to be stored in the prognostic variables. Fields and options are

  • nlat_half::Int64: number of latitudes on one hemisphere, Equator included

  • path::String: [OPTION] Path to the folder containing the sea surface temperatures, pkg path default

  • file::String: [OPTION] Filename of sea surface temperatures

  • varname::String: [OPTION] Variable name in netcdf file

  • file_Grid::Type{<:AbstractGrid}: [OPTION] Grid the sea surface temperature file comes on

  • missing_value::Any: [OPTION] The missing value in the data respresenting land

  • monthly_temperature::Vector{Grid} where {NF, Grid<:AbstractGrid{NF}}: Monthly sea surface temperatures [K], interpolated onto Grid

source
SpeedyWeather.ShallowWaterModelType

The ShallowWaterModel contains all model components needed for the simulation of the shallow water equations. To be constructed like

model = ShallowWaterModel(spectral_grid; kwargs...)

with spectral_grid::SpectralGrid used to initalize all non-default components passed on as keyword arguments, e.g. planet=Earth(spectral_grid). Fields, representing model components, are

  • spectral_grid::SpectralGrid

  • device_setup::Any

  • geometry::Any

  • planet::Any

  • atmosphere::Any

  • coriolis::Any

  • orography::Any

  • forcing::Any

  • drag::Any

  • particle_advection::Any

  • initial_conditions::Any

  • random_process::Any

  • time_stepping::Any

  • spectral_transform::Any

  • implicit::Any

  • horizontal_diffusion::Any

  • output::Any

  • callbacks::Dict{Symbol, SpeedyWeather.AbstractCallback}

  • feedback::Any

source
SpeedyWeather.SimplifiedBettsMillerType

The simplified Betts-Miller convection scheme from Frierson, 2007, https://doi.org/10.1175/JAS3935.1. This implements the qref-formulation in their paper. Fields and options are

  • nlayers::Int64: number of vertical layers

  • time_scale::Second: [OPTION] Relaxation time for profile adjustment

  • relative_humidity::Any: [OPTION] Relative humidity for reference profile

  • surface_temp_humid::Any: [OPTION] Surface perturbation of temp, humid to calculate the moist pseudo adiabat

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::AbstractModel: all parameters, constant at runtime

source
SpeedyWeather.SinSolarDeclinationType

Coefficients to calculate the solar declination angle δ [radians] based on a simple sine function, with Earth's axial tilt as amplitude, equinox as phase shift.

  • axial_tilt::Any

  • equinox::DateTime

  • length_of_year::Second

  • length_of_day::Second

source
SpeedyWeather.SinSolarDeclinationMethod

SinSolarDeclination functor, computing the solar declination angle of angular fraction of year g [radians] using the coefficients of the SinSolarDeclination struct.

source
SpeedyWeather.SolarDeclinationType

Coefficients to calculate the solar declination angle δ from

δ = 0.006918    - 0.399912*cos(g)  + 0.070257*sin(g)
                 - 0.006758*cos(2g) + 0.000907*sin(2g)
-                - 0.002697*cos(3g) + 0.001480*sin(3g)

with g the angular fraction of the year in radians. Following Spencer 1971, Fourier series representation of the position of the sun. Search 2(5):172.

  • a::AbstractFloat

  • s1::AbstractFloat

  • c1::AbstractFloat

  • s2::AbstractFloat

  • c2::AbstractFloat

  • s3::AbstractFloat

  • c3::AbstractFloat

source
SpeedyWeather.SolarDeclinationMethod

SolarDeclination functor, computing the solar declination angle of angular fraction of year g [radians] using the coefficients of the SolarDeclination struct.

source
SpeedyWeather.SolarTimeCorrectionType

Coefficients for the solar time correction (also called Equation of time) which adjusts the solar hour to an oscillation of sunrise/set by about +-16min throughout the year.

source
SpeedyWeather.SolarZenithType

Solar zenith angle varying with daily and seasonal cycle.

  • length_of_day::Second

  • length_of_year::Second

  • equation_of_time::Bool

  • seasonal_cycle::Bool

  • solar_declination::SpeedyWeather.SinSolarDeclination{NF} where NF<:AbstractFloat

  • time_correction::SpeedyWeather.SolarTimeCorrection

  • initial_time::Base.RefValue{DateTime}

source
SpeedyWeather.SolarZenithSeasonType

Solar zenith angle varying with seasonal cycle only.

  • length_of_day::Second

  • length_of_year::Second

  • seasonal_cycle::Bool

  • solar_declination::SpeedyWeather.SinSolarDeclination{NF} where NF<:AbstractFloat

  • initial_time::Base.RefValue{DateTime}

source
SpeedyWeather.SpectralAR1ProcessType

First-order auto-regressive random process (AR1) in spectral space, evolving wavenumbers with respectice time_scales and standard_deviations independently. Transformed after every time step to grid space with a clamp applied to limit extrema. For reproducability seed can be provided and an independent random_number_generator is used that is reseeded on every initialize!. Fields are

  • trunc::Int64

  • time_scale::Second: [OPTION] Time scale of the AR1 process

  • wavenumber::Int64: [OPTION] Wavenumber of the AR1 process

  • standard_deviation::Any: [OPTION] Standard deviation of the AR1 process

  • clamp::Tuple{NF, NF} where NF: [OPTION] Range to clamp values into after every transform into grid space

  • seed::Int64: [OPTION] Random number generator seed, 0=randomly seed from Julia's GLOBAL_RNG

  • random_number_generator::Random.Xoshiro: Independent random number generator for this random process

  • autoregressive_factor::Base.RefValue: Precomputed auto-regressive factor [1], function of time scale and model time step

  • noise_factors::Any: Precomputed noise factors [1] for every total wavenumber l

source
SpeedyWeather.SpectralFilterType

Spectral filter for horizontal diffusion. Fields are

  • trunc::Int64: spectral resolution

  • nlayers::Int64: number of vertical levels

  • shift::Any: [OPTION] shift diffusion to higher (positive shift) or lower (neg) wavenumbers, relative to trunc

  • scale::Any: [OPTION] Scale-selectiveness, steepness of the sigmoid, higher is more selective

  • time_scale::Second: [OPTION] diffusion time scale

  • time_scale_div::Second: [OPTION] stronger diffusion time scale for divergence

  • resolution_scaling::Any: [OPTION] resolution scaling to shorten time_scale with trunc

  • power::Any: [OPTION] power of the tanh function

  • power_div::Any: [OPTION] power of the tanh function for divergence

  • expl::Any

  • impl::Any

  • expl_div::Any

  • impl_div::Any

source
SpeedyWeather.SpectralFilterMethod
SpectralFilter(
+                - 0.002697*cos(3g) + 0.001480*sin(3g)

with g the angular fraction of the year in radians. Following Spencer 1971, Fourier series representation of the position of the sun. Search 2(5):172.

  • a::AbstractFloat

  • s1::AbstractFloat

  • c1::AbstractFloat

  • s2::AbstractFloat

  • c2::AbstractFloat

  • s3::AbstractFloat

  • c3::AbstractFloat

source
SpeedyWeather.SolarDeclinationMethod

SolarDeclination functor, computing the solar declination angle of angular fraction of year g [radians] using the coefficients of the SolarDeclination struct.

source
SpeedyWeather.SolarTimeCorrectionType

Coefficients for the solar time correction (also called Equation of time) which adjusts the solar hour to an oscillation of sunrise/set by about +-16min throughout the year.

source
SpeedyWeather.SolarZenithType

Solar zenith angle varying with daily and seasonal cycle.

  • length_of_day::Second

  • length_of_year::Second

  • equation_of_time::Bool

  • seasonal_cycle::Bool

  • solar_declination::SpeedyWeather.SinSolarDeclination{NF} where NF<:AbstractFloat

  • time_correction::SpeedyWeather.SolarTimeCorrection

  • initial_time::Base.RefValue{DateTime}

source
SpeedyWeather.SolarZenithSeasonType

Solar zenith angle varying with seasonal cycle only.

  • length_of_day::Second

  • length_of_year::Second

  • seasonal_cycle::Bool

  • solar_declination::SpeedyWeather.SinSolarDeclination{NF} where NF<:AbstractFloat

  • initial_time::Base.RefValue{DateTime}

source
SpeedyWeather.SpectralAR1ProcessType

First-order auto-regressive random process (AR1) in spectral space, evolving wavenumbers with respectice time_scales and standard_deviations independently. Transformed after every time step to grid space with a clamp applied to limit extrema. For reproducability seed can be provided and an independent random_number_generator is used that is reseeded on every initialize!. Fields are

  • trunc::Int64

  • time_scale::Second: [OPTION] Time scale of the AR1 process

  • wavenumber::Int64: [OPTION] Wavenumber of the AR1 process

  • standard_deviation::Any: [OPTION] Standard deviation of the AR1 process

  • clamp::Tuple{NF, NF} where NF: [OPTION] Range to clamp values into after every transform into grid space

  • seed::Int64: [OPTION] Random number generator seed, 0=randomly seed from Julia's GLOBAL_RNG

  • random_number_generator::Random.Xoshiro: Independent random number generator for this random process

  • autoregressive_factor::Base.RefValue: Precomputed auto-regressive factor [1], function of time scale and model time step

  • noise_factors::Any: Precomputed noise factors [1] for every total wavenumber l

source
SpeedyWeather.SpectralFilterType

Spectral filter for horizontal diffusion. Fields are

  • trunc::Int64: spectral resolution

  • nlayers::Int64: number of vertical levels

  • shift::Any: [OPTION] shift diffusion to higher (positive shift) or lower (neg) wavenumbers, relative to trunc

  • scale::Any: [OPTION] Scale-selectiveness, steepness of the sigmoid, higher is more selective

  • time_scale::Second: [OPTION] diffusion time scale

  • time_scale_div::Second: [OPTION] stronger diffusion time scale for divergence

  • resolution_scaling::Any: [OPTION] resolution scaling to shorten time_scale with trunc

  • power::Any: [OPTION] power of the tanh function

  • power_div::Any: [OPTION] power of the tanh function for divergence

  • expl::Any

  • impl::Any

  • expl_div::Any

  • impl_div::Any

source
SpeedyWeather.SpectralFilterMethod
SpectralFilter(
     spectral_grid::SpectralGrid;
     kwargs...
 ) -> SpectralFilter{<:AbstractFloat}
-

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

source
SpeedyWeather.SpectralGridType

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

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

  • device::SpeedyWeather.AbstractDevice: [OPTION] device archictecture to run on

  • ArrayType::Type{<:AbstractArray}: [OPTION] array type to use for all variables

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

  • Grid::Type{<:AbstractGrid}: [OPTION] horizontal grid used for calculations in grid-point space

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

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

  • nparticles::Int64: [OPTION] number of particles for particle advection [1]

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

  • nlat::Int64: number of latitude rings on both hemispheres

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

  • nlayers::Int64: [OPTION] number of vertical levels

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

  • VectorType::Type{<:AbstractVector}

  • MatrixType::Type{<:AbstractMatrix}

  • SpectralVariable2D::Type{<:AbstractArray}

  • SpectralVariable3D::Type{<:AbstractArray}

  • SpectralVariable4D::Type{<:AbstractArray}

  • GridVariable2D::Type{<:AbstractArray}

  • GridVariable3D::Type{<:AbstractArray}

  • GridVariable4D::Type{<:AbstractArray}

  • ParticleVector::Type{<:AbstractArray}

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

source
SpeedyWeather.SpectralGridType

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

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

  • device::SpeedyWeather.AbstractDevice: [OPTION] device archictecture to run on

  • ArrayType::Type{<:AbstractArray}: [OPTION] array type to use for all variables

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

  • Grid::Type{<:AbstractGrid}: [OPTION] horizontal grid used for calculations in grid-point space

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

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

  • nparticles::Int64: [OPTION] number of particles for particle advection [1]

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

  • nlat::Int64: number of latitude rings on both hemispheres

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

  • nlayers::Int64: [OPTION] number of vertical levels

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

  • VectorType::Type{<:AbstractVector}

  • MatrixType::Type{<:AbstractMatrix}

  • SpectralVariable2D::Type{<:AbstractArray}

  • SpectralVariable3D::Type{<:AbstractArray}

  • SpectralVariable4D::Type{<:AbstractArray}

  • GridVariable2D::Type{<:AbstractArray}

  • GridVariable3D::Type{<:AbstractArray}

  • GridVariable4D::Type{<:AbstractArray}

  • ParticleVector::Type{<:AbstractArray}

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

source
SpeedyWeather.SpeedyTransforms.SpectralTransformMethod
SpectralTransform(
     spectral_grid::SpectralGrid;
     one_more_degree,
     kwargs...
 ) -> SpectralTransform{NF, _A, _B, _C, _D, _E, LowerTriangularArray{NF1, 1, _A1}, LowerTriangularArray{NF2, 2, _A2}} where {NF<:AbstractFloat, _A, _B, _C, _D, _E, NF1<:AbstractFloat, _A1, NF2<:AbstractFloat, _A2}
-

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

Surface evaporation following a bulk formula with wind from model.surface_wind

  • use_boundary_layer_drag::Bool: Use column.boundarylayerdrag coefficient

  • moisture_exchange_land::AbstractFloat: Otherwise, use the following drag coefficient for evaporation over land

  • moisture_exchange_sea::AbstractFloat: Or drag coefficient for evaporation over sea

source
SpeedyWeather.SurfaceFluxHeatOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.SurfaceFluxHumidOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.SurfacePressureOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.TemperatureOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.TendenciesType

Tendencies of the prognostic variables in spectral and grid-point space

  • trunc::Int64

  • nlat_half::Int64

  • nlayers::Int64

  • vor_tend::Any: Vorticity of horizontal wind field [1/s]

  • div_tend::Any: Divergence of horizontal wind field [1/s]

  • temp_tend::Any: Absolute temperature [K]

  • humid_tend::Any: Specific humidity [kg/kg]

  • u_tend::Any: Zonal velocity [m/s]

  • v_tend::Any: Meridional velocity [m/s]

  • pres_tend::Any: Logarithm of surface pressure [Pa]

  • u_tend_grid::Any: Zonal velocity [m/s], grid

  • v_tend_grid::Any: Meridinoal velocity [m/s], grid

  • temp_tend_grid::Any: Absolute temperature [K], grid

  • humid_tend_grid::Any: Specific humidity [kg/kg], grid

  • pres_tend_grid::Any: Logarith of surface pressure [Pa], grid

source
SpeedyWeather.TendenciesMethod
Tendencies(
+

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

Surface evaporation following a bulk formula with wind from model.surface_wind

  • use_boundary_layer_drag::Bool: Use column.boundarylayerdrag coefficient

  • moisture_exchange_land::AbstractFloat: Otherwise, use the following drag coefficient for evaporation over land

  • moisture_exchange_sea::AbstractFloat: Or drag coefficient for evaporation over sea

source
SpeedyWeather.SurfaceFluxHeatOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.SurfaceFluxHumidOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.SurfacePressureOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.TemperatureOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

source
SpeedyWeather.TendenciesType

Tendencies of the prognostic variables in spectral and grid-point space

  • trunc::Int64

  • nlat_half::Int64

  • nlayers::Int64

  • vor_tend::Any: Vorticity of horizontal wind field [1/s]

  • div_tend::Any: Divergence of horizontal wind field [1/s]

  • temp_tend::Any: Absolute temperature [K]

  • humid_tend::Any: Specific humidity [kg/kg]

  • u_tend::Any: Zonal velocity [m/s]

  • v_tend::Any: Meridional velocity [m/s]

  • pres_tend::Any: Logarithm of surface pressure [Pa]

  • u_tend_grid::Any: Zonal velocity [m/s], grid

  • v_tend_grid::Any: Meridinoal velocity [m/s], grid

  • temp_tend_grid::Any: Absolute temperature [K], grid

  • humid_tend_grid::Any: Specific humidity [kg/kg], grid

  • pres_tend_grid::Any: Logarith of surface pressure [Pa], grid

source
SpeedyWeather.TendenciesMethod
Tendencies(
     SG::SpectralGrid
 ) -> Tendencies{<:AbstractFloat, <:AbstractArray, <:AbstractArray, <:AbstractArray, <:AbstractArray, <:AbstractArray}
-

Generator function.

source
SpeedyWeather.TetensEquationType

Parameters for computing saturation vapour pressure of water using the Tetens' equation,

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

where T is in Kelvin and i = 1, 2 for saturation above and below freezing, respectively. From Tetens (1930), and Murray (1967) for below freezing.

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

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

  • T₁::AbstractFloat: Tetens denominator (water) [˚C]

  • T₂::AbstractFloat: Tetens denominator following Murray (1967, below freezing) [˚C]

  • C₁::AbstractFloat: Tetens numerator scaling [1], above freezing

  • C₂::AbstractFloat: Tetens numerator scaling [1], below freezing

source
SpeedyWeather.TetensEquationMethod

Functor: Saturation water vapour pressure as a function of temperature using the Tetens equation,

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

where T is in Kelvin and i = 1, 2 for saturation above and below freezing, respectively.

source
SpeedyWeather.UniformCoolingType

Uniform cooling following Paulius and Garner, 2006. JAS. https://doi.org/10.1175/JAS3705.1 imposing a default temperature tendency of -1.5K/day (=1K/16hours for a time_scale of 16 hours) on every level except for the stratosphere (diagnosed as temp < temp_min) where a relaxation term with time_scale_stratosphere towards temp_stratosphere is applied.

dT/dt = -1.5K/day for T > 207.5K else (200K-T) / 5 days

Fields are

  • time_scale::Second: [OPTION] time scale of cooling, default = -1.5K/day = -1K/16hrs

  • temp_min::Any: [OPTION] temperature [K] below which stratospheric relaxation is applied

  • temp_stratosphere::Any: [OPTION] target temperature [K] of stratospheric relaxation

  • time_scale_stratosphere::Second: [OPTION] time scale of stratospheric relaxation

source
SpeedyWeather.VorticityOutputType

Defines netCDF output of vorticity. Fields are

  • name::String: [Required] short name of variable (unique) used in netCDF file and key for dictionary

  • unit::String: [Required] unit of variable

  • long_name::String: [Required] long name of variable used in netCDF file

  • dims_xyzt::NTuple{4, Bool}: [Required] NetCDF dimensions the variable uses, lon, lat, layer, time

  • missing_value::Float64: [Optional] missing value for the variable, if not specified uses NaN

  • compression_level::Int64: [Optional] compression level of the lossless compressor, 1=lowest/fastest, 9=highest/slowest, 3=default

  • shuffle::Bool: [Optional] bitshuffle the data for compression, false = default

  • keepbits::Int64: [Optional] number of mantissa bits to keep for compression (default: 15)

Custom variable output defined similarly with required fields marked, optional fields otherwise use variable-independent defaults. Initialize with VorticityOutput() and non-default fields can always be passed on as keyword arguments, e.g. VorticityOutput(long_name="relative vorticity", compression_level=0).

source
SpeedyWeather.ZonalJetType

A struct that contains all parameters for the Galewsky et al, 2004 zonal jet intitial conditions for the ShallowWaterModel. 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::AbstractGrid{NF} where NF<:AbstractFloat: height [m] on grid-point space.

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

source
SpeedyWeather.ZonalVelocityOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

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]

source
SpeedyWeather.TetensEquationType

Parameters for computing saturation vapour pressure of water using the Tetens' equation,

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

where T is in Kelvin and i = 1, 2 for saturation above and below freezing, respectively. From Tetens (1930), and Murray (1967) for below freezing.

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

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

  • T₁::AbstractFloat: Tetens denominator (water) [˚C]

  • T₂::AbstractFloat: Tetens denominator following Murray (1967, below freezing) [˚C]

  • C₁::AbstractFloat: Tetens numerator scaling [1], above freezing

  • C₂::AbstractFloat: Tetens numerator scaling [1], below freezing

source
SpeedyWeather.TetensEquationMethod

Functor: Saturation water vapour pressure as a function of temperature using the Tetens equation,

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

where T is in Kelvin and i = 1, 2 for saturation above and below freezing, respectively.

source
SpeedyWeather.UniformCoolingType

Uniform cooling following Paulius and Garner, 2006. JAS. https://doi.org/10.1175/JAS3705.1 imposing a default temperature tendency of -1.5K/day (=1K/16hours for a time_scale of 16 hours) on every level except for the stratosphere (diagnosed as temp < temp_min) where a relaxation term with time_scale_stratosphere towards temp_stratosphere is applied.

dT/dt = -1.5K/day for T > 207.5K else (200K-T) / 5 days

Fields are

  • time_scale::Second: [OPTION] time scale of cooling, default = -1.5K/day = -1K/16hrs

  • temp_min::Any: [OPTION] temperature [K] below which stratospheric relaxation is applied

  • temp_stratosphere::Any: [OPTION] target temperature [K] of stratospheric relaxation

  • time_scale_stratosphere::Second: [OPTION] time scale of stratospheric relaxation

source
SpeedyWeather.VorticityOutputType

Defines netCDF output of vorticity. Fields are

  • name::String: [Required] short name of variable (unique) used in netCDF file and key for dictionary

  • unit::String: [Required] unit of variable

  • long_name::String: [Required] long name of variable used in netCDF file

  • dims_xyzt::NTuple{4, Bool}: [Required] NetCDF dimensions the variable uses, lon, lat, layer, time

  • missing_value::Float64: [Optional] missing value for the variable, if not specified uses NaN

  • compression_level::Int64: [Optional] compression level of the lossless compressor, 1=lowest/fastest, 9=highest/slowest, 3=default

  • shuffle::Bool: [Optional] bitshuffle the data for compression, false = default

  • keepbits::Int64: [Optional] number of mantissa bits to keep for compression (default: 15)

Custom variable output defined similarly with required fields marked, optional fields otherwise use variable-independent defaults. Initialize with VorticityOutput() and non-default fields can always be passed on as keyword arguments, e.g. VorticityOutput(long_name="relative vorticity", compression_level=0).

source
SpeedyWeather.ZonalJetType

A struct that contains all parameters for the Galewsky et al, 2004 zonal jet intitial conditions for the ShallowWaterModel. 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::AbstractGrid{NF} where NF<:AbstractFloat: height [m] on grid-point space.

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

source
SpeedyWeather.ZonalVelocityOutputType

Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are

  • name::String

  • unit::String

  • long_name::String

  • dims_xyzt::NTuple{4, Bool}

  • missing_value::Float64

  • compression_level::Int64

  • shuffle::Bool

  • keepbits::Int64

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]

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

Copies entries of progn_old into progn_new.

source
Base.delete!Method
delete!(
     output::NetCDFOutput,
     keys::Union{String, Symbol}...
 ) -> NetCDFOutput
-

Delete output variables from output by their (short name) (Symbol or String), corresponding to the keys in the dictionary.

source
Base.fill!Method
fill!(
+

Delete output variables from output by their (short name) (Symbol or String), corresponding to the keys in the dictionary.

source
Base.fill!Method
fill!(
     tendencies::Tendencies,
     x,
     _::Type{<:Barotropic}
 ) -> Tendencies
-

Set the tendencies for the barotropic model to x.

source
Base.fill!Method
fill!(
+

Set the tendencies for the barotropic model to x.

source
Base.fill!Method
fill!(
     tendencies::Tendencies,
     x,
     _::Type{<:PrimitiveDry}
 ) -> Tendencies
-

Set the tendencies for the primitive dry model to x.

source
Base.fill!Method
fill!(
+

Set the tendencies for the primitive dry model to x.

source
Base.fill!Method
fill!(
     tendencies::Tendencies,
     x,
     _::Type{<:PrimitiveWet}
 ) -> Tendencies
-

Set the tendencies for the primitive wet model to x.

source
Base.fill!Method
fill!(
+

Set the tendencies for the primitive wet model to x.

source
Base.fill!Method
fill!(
     tendencies::Tendencies,
     x,
     _::Type{<:ShallowWater}
 ) -> Tendencies
-

Set the tendencies for the shallow-water model to x.

source
Base.modMethod
mod(p::Particle) -> Any
-

Modulo operator for particle locations to map them back into [0,360˚E) and [-90˚,90˚N], in the horizontal and to clamp vertical σ coordinates into [0,1].

source
Base.modMethod
mod(p::Particle) -> Any
+

Modulo operator for particle locations to map them back into [0,360˚E) and [-90˚,90˚N], in the horizontal and to clamp vertical σ coordinates into [0,1].

source
SpeedyWeather.CallbackDictMethod
CallbackDict(
     pairs::Pair{Symbol, <:SpeedyWeather.AbstractCallback}...
 ) -> Dict{Symbol, SpeedyWeather.AbstractCallback}
-

Create Callback dictionary like normal dictionaries.

source
SpeedyWeather.DeviceArrayMethod
DeviceArray(_::CPU, x) -> Any
-

Adapts x to an Array when device::CPU is used. Define for CPU for compatibility with adapt to CuArrays etc. Uses adapt, thus also can return SubArrays etc.

source
SpeedyWeather.DeviceArrayMethod
DeviceArray(_::CPU, x) -> Any
+

Adapts x to an Array when device::CPU is used. Define for CPU for compatibility with adapt to CuArrays etc. Uses adapt, thus also can return SubArrays etc.

source
SpeedyWeather.SpeedyTransforms.transform!Method
transform!(
     diagn::DiagnosticVariables,
     progn::PrognosticVariables,
     lf::Integer,
     model::Barotropic;
     kwargs...
 )
-

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.transform!Method
transform!(
+

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.transform!Method
transform!(
     diagn::DiagnosticVariables,
     progn::PrognosticVariables,
     lf::Integer,
     random_process::NoRandomProcess,
     spectral_transform::SpectralTransform
 )
-

NoRandomProcess does not need to transform any random pattern from spectral to grid space.

source
SpeedyWeather.SpeedyTransforms.transform!Method
transform!(
     diagn::DiagnosticVariables,
     progn::PrognosticVariables,
     lf::Integer,
     model::PrimitiveEquation;
     initialize
 )
-

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.transform!Method
transform!(
+

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.transform!Method
transform!(
     diagn::DiagnosticVariables,
     progn::PrognosticVariables,
     lf::Integer,
     model::ShallowWater;
     kwargs...
 )
-

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.SpeedyTransforms.transform!Method
transform!(
+

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.SpeedyTransforms.transform!Method
transform!(
     diagn::DiagnosticVariables,
     progn::PrognosticVariables,
     lf::Integer,
     random_process::SpeedyWeather.AbstractRandomProcess,
     spectral_transform::SpectralTransform
 )
-

General transform for random processes <: AbstractRandomProcess. Takes the spectral random_pattern in the prognostic variables and transforms it to spectral space in diagn.grid.random_pattern.

source
SpeedyWeather.WhichZenithMethod
WhichZenith(
+

General transform for random processes <: AbstractRandomProcess. Takes the spectral random_pattern in the prognostic variables and transforms it to spectral space in diagn.grid.random_pattern.

source
SpeedyWeather.WhichZenithMethod
WhichZenith(
     SG::SpectralGrid,
     P::SpeedyWeather.AbstractPlanet;
     kwargs...
 ) -> Union{SolarZenith, SolarZenithSeason}
-

Chooses from SolarZenith (daily and seasonal cycle) or SolarZenithSeason given the parameters in model.planet. In both cases the seasonal cycle can be disabled, calculating the solar declination from the initial time instead of current time.

source
SpeedyWeather.activateMethod
activate(
+

Chooses from SolarZenith (daily and seasonal cycle) or SolarZenithSeason given the parameters in model.planet. In both cases the seasonal cycle can be disabled, calculating the solar declination from the initial time instead of current time.

source
SpeedyWeather.activateMethod
activate(
     p::Particle{NF}
 ) -> Particle{_A, true} where _A<:AbstractFloat
-

Activate particle. Active particles can move.

source
SpeedyWeather.add!Method
add!(
     model::AbstractModel,
     key_callbacks::Pair{Symbol, <:SpeedyWeather.AbstractCallback}...
 ) -> Any
 

Add a or several callbacks to a model::AbstractModel. To be used like

add!(model, :my_callback => callback)
-add!(model, :my_callback1 => callback, :my_callback2 => other_callback)
source
SpeedyWeather.add!Method
add!(
     model::AbstractModel,
     callbacks::SpeedyWeather.AbstractCallback...
 )
 

Add a or several callbacks to a mdoel without specifying the key which is randomly created like callback_????. To be used like

add!(model.callbacks, callback)
-add!(model.callbacks, callback1, callback2).
source
SpeedyWeather.add!Method
add!(
     model::AbstractModel,
     outputvariables::SpeedyWeather.AbstractOutputVariable...
 )
-

Add outputvariables to the dictionary in output::NetCDFOutput of model, i.e. at model.output.variables.

source
SpeedyWeather.add!Method
add!(
+

Add outputvariables to the dictionary in output::NetCDFOutput of model, i.e. at model.output.variables.

source
SpeedyWeather.add!Method
add!(
     D::Dict{Symbol, SpeedyWeather.AbstractCallback},
     key_callbacks::Pair{Symbol, <:SpeedyWeather.AbstractCallback}...
 )
 

Add a or several callbacks to a Dict{String, AbstractCallback} dictionary. To be used like

add!(model.callbacks, :my_callback => callback)
-add!(model.callbacks, :my_callback1 => callback, :my_callback2 => other_callback)
source
SpeedyWeather.add!Method
add!(
+add!(model.callbacks, :my_callback1 => callback, :my_callback2 => other_callback)
source
SpeedyWeather.add!Method
add!(
     D::Dict{Symbol, SpeedyWeather.AbstractCallback},
     callbacks::SpeedyWeather.AbstractCallback...;
     verbose
 )
 

Add a or several callbacks to a Dict{Symbol, AbstractCallback} dictionary without specifying the key which is randomly created like callback_????. To be used like

add!(model.callbacks, callback)
-add!(model.callbacks, callback1, callback2).
source
SpeedyWeather.add!Method
add!(
     D::Dict{Symbol, SpeedyWeather.AbstractOutputVariable},
     outputvariables::SpeedyWeather.AbstractOutputVariable...
 ) -> Dict{Symbol, SpeedyWeather.AbstractOutputVariable}
-

Add outputvariables to a dictionary defining the variables subject to NetCDF output.

source
SpeedyWeather.add!Method
add!(
+

Add outputvariables to a dictionary defining the variables subject to NetCDF output.

source
SpeedyWeather.add!Method
add!(
     output::NetCDFOutput,
     outputvariables::SpeedyWeather.AbstractOutputVariable...
 ) -> NetCDFOutput
-

Add outputvariables to the dictionary in output::NetCDFOutput, i.e. at output.variables.

source
SpeedyWeather.add_default!Method
add_default!(
     output_variables::Dict{Symbol, SpeedyWeather.AbstractOutputVariable},
     Model::Type{<:Barotropic}
 ) -> Dict{Symbol, SpeedyWeather.AbstractOutputVariable}
-

Add default variables to output for a Barotropic model: Vorticity, zonal and meridional velocity.

source
SpeedyWeather.add_default!Method
add_default!(
     variables::Dict{Symbol, SpeedyWeather.AbstractOutputVariable},
     Model::Type{<:PrimitiveDry}
 ) -> Dict{Symbol, SpeedyWeather.AbstractOutputVariable}
-

Add default variables to output for a PrimitiveDry model, same as for a Barotropic model but also the surface pressure and temperature.

source
SpeedyWeather.add_default!Method
add_default!(
+

Add default variables to output for a PrimitiveDry model, same as for a Barotropic model but also the surface pressure and temperature.

source
SpeedyWeather.add_default!Method
add_default!(
     variables::Dict{Symbol, SpeedyWeather.AbstractOutputVariable},
     Model::Type{<:PrimitiveWet}
 ) -> Dict{Symbol, SpeedyWeather.AbstractOutputVariable}
-

Add default variables to output for a PrimitiveWet model, same as for a PrimitiveDry model but also the specific humidity.

source
SpeedyWeather.add_default!Method
add_default!(
+

Add default variables to output for a PrimitiveWet model, same as for a PrimitiveDry model but also the specific humidity.

source
SpeedyWeather.add_default!Method
add_default!(
     variables::Dict{Symbol, SpeedyWeather.AbstractOutputVariable},
     Model::Type{<:ShallowWater}
 ) -> Dict{Symbol, SpeedyWeather.AbstractOutputVariable}
-

Add default variables to output for a ShallowWater model, same as for a Barotropic model but also the interface displacement.

source
SpeedyWeather.bernoulli_potential!Method
bernoulli_potential!(
     diagn::DiagnosticVariables,
     S::SpectralTransform
 )
-

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.bulk_richardson!Method
bulk_richardson!(
     column::ColumnVariables,
     atmosphere::SpeedyWeather.AbstractAtmosphere
 ) -> Any
-

Calculate the bulk richardson number following Frierson, 2007. For vertical stability in the boundary layer.

source
SpeedyWeather.bulk_richardson_surfaceMethod
bulk_richardson_surface(
     column::ColumnVariables,
     atmosphere::SpeedyWeather.AbstractAtmosphere
 ) -> Any
-

Calculate the bulk richardson number following Frierson, 2007. For vertical stability in the boundary layer.

source
SpeedyWeather.callback!Method
callback!(
+

Calculate the bulk richardson number following Frierson, 2007. For vertical stability in the boundary layer.

source
SpeedyWeather.callback!Method
callback!(
     callback::GlobalSurfaceTemperatureCallback,
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::AbstractModel
 ) -> Any
-

Pulls the average temperature from the lowermost layer and stores it in the next element of the callback.temp vector.

source
SpeedyWeather.convection!Method
convection!(
+

Pulls the average temperature from the lowermost layer and stores it in the next element of the callback.temp vector.

source
SpeedyWeather.convection!Method
convection!(
     column::ColumnVariables{NF},
     DBM::DryBettsMiller,
     geometry::Geometry,
     atmosphere::SpeedyWeather.AbstractAtmosphere,
     model::PrimitiveEquation
 )
-

calculates temperature tendency for the dry convection scheme following the simplified Betts-Miller convection from Frierson 2007 but with zero humidity. Starts with a first-guess relaxation to determine the convective criterion, then adjusts the reference profiles for thermodynamic consistency (e.g. in dry convection the humidity profile is non-precipitating), and relaxes current vertical profiles to the adjusted references.

source
SpeedyWeather.convection!Method
convection!(
+

calculates temperature tendency for the dry convection scheme following the simplified Betts-Miller convection from Frierson 2007 but with zero humidity. Starts with a first-guess relaxation to determine the convective criterion, then adjusts the reference profiles for thermodynamic consistency (e.g. in dry convection the humidity profile is non-precipitating), and relaxes current vertical profiles to the adjusted references.

source
SpeedyWeather.convection!Method
convection!(
     column::ColumnVariables{NF},
     SBM::SimplifiedBettsMiller,
     clausius_clapeyron::SpeedyWeather.AbstractClausiusClapeyron,
@@ -275,70 +275,70 @@
     time_stepping::SpeedyWeather.AbstractTimeStepper,
     model::PrimitiveWet
 ) -> Union{Nothing, Int64}
-

calculates temperature and humidity tendencies for the convection scheme following the simplified Betts-Miller convection. Starts with a first-guess relaxation to determine the convective criteria (none, dry/shallow or deep), then adjusts reference profiles for thermodynamic consistency (e.g. in dry convection the humidity profile is non-precipitating), and relaxes current vertical profiles to the adjusted references.

source
SpeedyWeather.coriolisMethod
coriolis(grid::AbstractGridArray; kwargs...) -> 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(grid::AbstractGridArray; kwargs...) -> 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.cos_zenith!Method
cos_zenith!(
+

calculates temperature and humidity tendencies for the convection scheme following the simplified Betts-Miller convection. Starts with a first-guess relaxation to determine the convective criteria (none, dry/shallow or deep), then adjusts reference profiles for thermodynamic consistency (e.g. in dry convection the humidity profile is non-precipitating), and relaxes current vertical profiles to the adjusted references.

source
SpeedyWeather.coriolisMethod
coriolis(grid::AbstractGridArray; kwargs...) -> 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(grid::AbstractGridArray; kwargs...) -> 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.cos_zenith!Method
cos_zenith!(
     cos_zenith::AbstractGridArray{NF, 1, Array{NF, 1}},
     S::SolarZenith,
     time::DateTime,
     geometry::SpeedyWeather.AbstractGeometry
 )
-

Calculate cos of solar zenith angle with a daily cycle at time time. Seasonal cycle or time correction may be disabled, depending on parameters in SolarZenith.

source
SpeedyWeather.cos_zenith!Method
cos_zenith!(
+

Calculate cos of solar zenith angle with a daily cycle at time time. Seasonal cycle or time correction may be disabled, depending on parameters in SolarZenith.

source
SpeedyWeather.cos_zenith!Method
cos_zenith!(
     cos_zenith::AbstractGridArray{NF, 1, Array{NF, 1}},
     S::SolarZenithSeason,
     time::DateTime,
     geometry::SpeedyWeather.AbstractGeometry
 )
-

Calculate cos of solar zenith angle as daily average at time time. Seasonal cycle or time correction may be disabled, depending on parameters in SolarZenithSeason.

source
SpeedyWeather.create_output_folderMethod
create_output_folder(
+

Calculate cos of solar zenith angle as daily average at time time. Seasonal cycle or time correction may be disabled, depending on parameters in SolarZenithSeason.

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.deactivateMethod
deactivate(
+

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

source
SpeedyWeather.deactivateMethod
deactivate(
     p::Particle{NF}
 ) -> Particle{_A, false} where _A<:AbstractFloat
-

Deactivate particle. Inactive particles cannot move.

source
SpeedyWeather.default_sigma_coordinatesMethod
default_sigma_coordinates(
     nlayers::Integer
 ) -> Vector{Float64}
-

Vertical sigma coordinates defined by their nlayers+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. Default levels are equally spaced from 0 to 1 (including).

source
SpeedyWeather.drag!Method
drag!(diagn::DiagnosticVariables, drag::QuadraticDrag)
-

Quadratic drag for the momentum equations.

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

with cD the non-dimensional drag coefficient as defined in drag::QuadraticDrag. cD and layer thickness H are precomputed in initialize!(::QuadraticDrag, ::AbstractModel) and scaled by the radius as are the momentum equations.

source
SpeedyWeather.dry_adiabat!Method
dry_adiabat!(
+

Vertical sigma coordinates defined by their nlayers+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. Default levels are equally spaced from 0 to 1 (including).

source
SpeedyWeather.drag!Method
drag!(diagn::DiagnosticVariables, drag::QuadraticDrag)
+

Quadratic drag for the momentum equations.

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

with cD the non-dimensional drag coefficient as defined in drag::QuadraticDrag. cD and layer thickness H are precomputed in initialize!(::QuadraticDrag, ::AbstractModel) and scaled by the radius as are the momentum equations.

source
SpeedyWeather.dry_adiabat!Method
dry_adiabat!(
     temp_ref_profile::AbstractVector,
     temp_environment::AbstractVector,
     temp_parcel::Real,
     σ::AbstractVector,
     atmosphere::SpeedyWeather.AbstractAtmosphere
 ) -> Int64
-

Calculates the moist pseudo adiabat given temperature and humidity of surface parcel. Follows the dry adiabat till condensation and then continues on the pseudo moist-adiabat with immediate condensation to the level of zero buoyancy. Levels above are skipped, set to NaN instead and should be skipped in the relaxation.

source
SpeedyWeather.dry_static_energy!Method
dry_static_energy!(
+

Calculates the moist pseudo adiabat given temperature and humidity of surface parcel. Follows the dry adiabat till condensation and then continues on the pseudo moist-adiabat with immediate condensation to the level of zero buoyancy. Levels above are skipped, set to NaN instead and should be skipped in the relaxation.

source
SpeedyWeather.dry_static_energy!Method
dry_static_energy!(
     column::ColumnVariables,
     atmosphere::SpeedyWeather.AbstractAtmosphere
 )
-

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

source
SpeedyWeather.dynamics_tendencies!Method
dynamics_tendencies!(
     diagn::DiagnosticVariables,
     progn::PrognosticVariables,
     lf::Integer,
     model::Barotropic
 )
-

Calculate all tendencies for the BarotropicModel.

source
SpeedyWeather.dynamics_tendencies!Method
dynamics_tendencies!(
     diagn::DiagnosticVariables,
     progn::PrognosticVariables,
     lf::Integer,
     model::PrimitiveEquation
 )
-

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

source
SpeedyWeather.dynamics_tendencies!Method
dynamics_tendencies!(
     diagn::DiagnosticVariables,
     progn::PrognosticVariables,
     lf::Integer,
     model::ShallowWater
 )
-

Calculate all tendencies for the ShallowWaterModel.

source
SpeedyWeather.first_timesteps!Method
first_timesteps!(
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::AbstractModel
 )
-

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::LowerTriangularArray,
     A_grid::AbstractGridArray,
     diagn::DiagnosticVariables,
@@ -347,17 +347,17 @@
     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,
     planet::SpeedyWeather.AbstractPlanet,
     atmosphere::SpeedyWeather.AbstractAtmosphere
 )
-

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

source
SpeedyWeather.forcing!Method
forcing!(
     diagn::DiagnosticVariables,
     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, ::AbstractModel).

source
SpeedyWeather.geopotential!Function
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, ::AbstractModel).

source
SpeedyWeather.geopotential!Function
geopotential!(
     geopot::AbstractVector,
     temp::AbstractVector,
     G::Geopotential
@@ -368,17 +368,17 @@
     G::Geopotential,
     geopot_surf::Real
 )
-

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.geopotential!Method
geopotential!(
+

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.geopotential!Method
geopotential!(
     diagn::DiagnosticVariables,
     geopotential::Geopotential,
     orography::SpeedyWeather.AbstractOrography
 )
-

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::DiagnosticVariables,
     pres::LowerTriangularArray,
     planet::SpeedyWeather.AbstractPlanet
 )
-

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

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.get_column!Method
get_column!(
     C::ColumnVariables,
     D::DiagnosticVariables,
     P::PrognosticVariables,
@@ -391,15 +391,15 @@
     albedo::SpeedyWeather.AbstractAlbedo,
     implicit::SpeedyWeather.AbstractImplicit
 ) -> Any
-

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

source
SpeedyWeather.get_full_output_file_pathMethod
get_full_output_file_path(
+

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_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::PrimitiveEquation
 )
-

Calculate geopotentiala and dry static energy for the primitive equation model.

source
SpeedyWeather.get_Δt_millisecFunction
get_Δt_millisec(
     Δt_at_T31::Dates.TimePeriod,
     trunc,
     radius,
@@ -412,106 +412,106 @@
     adjust_with_output::Bool,
     output_dt::Dates.TimePeriod
 ) -> Any
-

Computes the time step in [ms]. Δt_at_T31 is always scaled with the resolution trunc of the model. In case adjust_Δt_with_output is true, the Δt_at_T31 is additionally adjusted to the closest divisor of output_dt so that the output time axis is keeping output_dt exactly.

source
SpeedyWeather.gradMethod
grad(CC::ClausiusClapeyron{NF}, temp_kelvin) -> Any
-

Gradient of Clausius-Clapeyron wrt to temperature, evaluated at temp_kelvin.

source
SpeedyWeather.gradMethod
grad(
+

Computes the time step in [ms]. Δt_at_T31 is always scaled with the resolution trunc of the model. In case adjust_Δt_with_output is true, the Δt_at_T31 is additionally adjusted to the closest divisor of output_dt so that the output time axis is keeping output_dt exactly.

source
SpeedyWeather.gradMethod
grad(CC::ClausiusClapeyron{NF}, temp_kelvin) -> Any
+

Gradient of Clausius-Clapeyron wrt to temperature, evaluated at temp_kelvin.

source
SpeedyWeather.gradMethod
grad(
     TetensCoefficients::TetensEquation{NF},
     temp_kelvin
 ) -> Any
-

Gradient of the Tetens equation wrt to temperature, evaluated at temp_kelvin.

source
SpeedyWeather.hasMethod
has(Model::Type{<:AbstractModel}, var_name::Symbol) -> Any
-

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.hasMethod
has(Model::Type{<:AbstractModel}, var_name::Symbol) -> Any
+

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::DiagnosticVariables,
     progn::PrognosticVariables,
     diffusion::SpeedyWeather.AbstractHorizontalDiffusion,
-    model::ShallowWater
+    model::PrimitiveEquation
 ) -> Any
 horizontal_diffusion!(
     diagn::DiagnosticVariables,
     progn::PrognosticVariables,
     diffusion::SpeedyWeather.AbstractHorizontalDiffusion,
-    model::ShallowWater,
+    model::PrimitiveEquation,
     lf::Integer
 ) -> Any
-

Apply horizontal diffusion to vorticity and divergence in the ShallowWaterModel.

source
SpeedyWeather.horizontal_diffusion!Function
horizontal_diffusion!(
+

Apply horizontal diffusion applied to vorticity, divergence, temperature, and humidity (PrimitiveWet only) in the PrimitiveEquation models.

source
SpeedyWeather.horizontal_diffusion!Function
horizontal_diffusion!(
     diagn::DiagnosticVariables,
     progn::PrognosticVariables,
     diffusion::SpeedyWeather.AbstractHorizontalDiffusion,
-    model::Barotropic
+    model::ShallowWater
 ) -> Any
 horizontal_diffusion!(
     diagn::DiagnosticVariables,
     progn::PrognosticVariables,
     diffusion::SpeedyWeather.AbstractHorizontalDiffusion,
-    model::Barotropic,
+    model::ShallowWater,
     lf::Integer
 ) -> Any
-

Apply horizontal diffusion to vorticity in the BarotropicModel.

source
SpeedyWeather.horizontal_diffusion!Function
horizontal_diffusion!(
     diagn::DiagnosticVariables,
     progn::PrognosticVariables,
     diffusion::SpeedyWeather.AbstractHorizontalDiffusion,
-    model::PrimitiveEquation
+    model::Barotropic
 ) -> Any
 horizontal_diffusion!(
     diagn::DiagnosticVariables,
     progn::PrognosticVariables,
     diffusion::SpeedyWeather.AbstractHorizontalDiffusion,
-    model::PrimitiveEquation,
+    model::Barotropic,
     lf::Integer
 ) -> Any
-

Apply horizontal diffusion applied to vorticity, divergence, temperature, and humidity (PrimitiveWet only) in the PrimitiveEquation models.

source
SpeedyWeather.horizontal_diffusion!Method
horizontal_diffusion!(
     tendency::LowerTriangularArray,
     var::LowerTriangularArray,
     expl::AbstractMatrix,
     impl::AbstractMatrix
 )
-

Apply horizontal diffusion to a 2D field var 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 var as well as its tendency tendency.

source
SpeedyWeather.implicit_correction!Method
implicit_correction!(
+

Apply horizontal diffusion to a 2D field var 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 var as well as its tendency tendency.

source
SpeedyWeather.implicit_correction!Method
implicit_correction!(
     diagn::DiagnosticVariables,
     implicit::ImplicitPrimitiveEquation,
     progn::PrognosticVariables
 )
-

Apply the implicit corrections to dampen gravity waves in the primitive equation models.

source
SpeedyWeather.implicit_correction!Method
implicit_correction!(
     diagn::DiagnosticVariables,
     progn::PrognosticVariables,
     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!Method
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!Method
initialize!(
     model::Barotropic;
     time
 ) -> Simulation{Model} where Model<:Barotropic
-

Calls all initialize! functions for most fields, representing components, of model, except for model.output and model.feedback which are always called at in time_stepping!.

source
SpeedyWeather.initialize!Method
initialize!(
+

Calls all initialize! functions for most fields, representing components, of model, except for model.output and model.feedback which are always called at in time_stepping!.

source
SpeedyWeather.initialize!Method
initialize!(
     clock::Clock,
     time_stepping::SpeedyWeather.AbstractTimeStepper
 ) -> Clock
-

Initialize the clock with the time step Δt in the time_stepping.

source
SpeedyWeather.initialize!Method
initialize!(
     orog::EarthOrography,
     P::SpeedyWeather.AbstractPlanet,
     S::SpectralTransform
 )
-

Initialize the arrays orography, geopot_surf in orog by reading the orography field from file.

source
SpeedyWeather.initialize!Method
initialize!(
     feedback::Feedback,
     clock::Clock,
     model::AbstractModel
 ) -> ProgressMeter.Progress
-

Initializes the a Feedback struct.

source
SpeedyWeather.initialize!Method
initialize!(
     geopotential::Geopotential,
     model::PrimitiveEquation
 )
-

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.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!(diffusion::HyperDiffusion, model::AbstractModel)
-

Precomputes the hyper diffusion terms in diffusion based on the model time step, and possibly with a changing strength/power in the vertical.

source
SpeedyWeather.initialize!Method
initialize!(
+

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.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!(diffusion::HyperDiffusion, model::AbstractModel)
+

Precomputes the hyper diffusion terms in diffusion based on the model time step, and possibly with a changing strength/power in the vertical.

source
SpeedyWeather.initialize!Method
initialize!(
     diffusion::HyperDiffusion,
     G::SpeedyWeather.AbstractGeometry,
     L::SpeedyWeather.AbstractTimeStepper
 )
-

Precomputes the hyper diffusion terms for all layers based on the model time step in L, the vertical level sigma level in G.

source
SpeedyWeather.initialize!Method
initialize!(
+

Precomputes the hyper diffusion terms for all layers based on the model time step in L, the vertical level sigma level in G.

source
SpeedyWeather.initialize!Method
initialize!(
     implicit::ImplicitPrimitiveEquation,
     dt::Real,
     diagn::DiagnosticVariables,
@@ -520,109 +520,109 @@
     atmosphere::SpeedyWeather.AbstractAtmosphere,
     adiabatic_conversion::SpeedyWeather.AbstractAdiabaticConversion
 )
-

Initialize the implicit terms for the PrimitiveEquation models.

source
SpeedyWeather.initialize!Method
initialize!(
     implicit::ImplicitShallowWater,
     dt::Real,
     planet::SpeedyWeather.AbstractPlanet,
     atmosphere::SpeedyWeather.AbstractAtmosphere
 )
-

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!(
+

initialize the JablonowskiRelaxation temperature relaxation by precomputing terms for the equilibrium temperature Teq and the frequency (strength of relaxation).

source
SpeedyWeather.initialize!Method
initialize!(
     land_sea_mask::LandSeaMask,
     model::PrimitiveEquation
 ) -> AbstractGrid{NF} where NF<:AbstractFloat
-

Reads a high-resolution land-sea mask from file and interpolates (grid-call average) onto the model grid for a fractional sea mask.

source
SpeedyWeather.initialize!Method
initialize!(L::Leapfrog, model::AbstractModel)
-

Initialize leapfrogging L by recalculating the timestep given the output time step output_dt from model.output. Recalculating will slightly adjust the time step to be a divisor such that an integer number of time steps matches exactly with the output time step.

source
SpeedyWeather.initialize!Method
initialize!(scheme::LinearDrag, model::PrimitiveEquation)
-

Precomputes the drag coefficients for this BoundaryLayerDrag scheme.

source
SpeedyWeather.initialize!Method
initialize!(
+

Reads a high-resolution land-sea mask from file and interpolates (grid-call average) onto the model grid for a fractional sea mask.

source
SpeedyWeather.initialize!Method
initialize!(L::Leapfrog, model::AbstractModel)
+

Initialize leapfrogging L by recalculating the timestep given the output time step output_dt from model.output. Recalculating will slightly adjust the time step to be a divisor such that an integer number of time steps matches exactly with the output time step.

source
SpeedyWeather.initialize!Method
initialize!(scheme::LinearDrag, model::PrimitiveEquation)
+

Precomputes the drag coefficients for this BoundaryLayerDrag scheme.

source
SpeedyWeather.initialize!Method
initialize!(
     model::PrimitiveDry;
     time
 ) -> Simulation{Model} where Model<:PrimitiveDry
-

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!(
+

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;
     time
 ) -> Simulation{Model} where Model<:PrimitiveWet
-

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!(
+

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::PrognosticVariables,
     _::PressureOnOrography,
     model::PrimitiveEquation
 )
-

Initialize surface pressure on orography by integrating the hydrostatic equation with the reference temperature lapse rate.

source
SpeedyWeather.initialize!Method
initialize!(
+

Initialize surface pressure on orography by integrating the hydrostatic equation with the reference temperature lapse rate.

source
SpeedyWeather.initialize!Method
initialize!(
     progn::PrognosticVariables,
     initial_conditions::RossbyHaurwitzWave,
     model::AbstractModel
 )
-

Rossby-Haurwitz wave initial conditions as in Williamson et al. 1992, J Computational Physics with an additional cut-off amplitude c to filter out tiny harmonics in the vorticity field.

source
SpeedyWeather.initialize!Method
initialize!(
+

Rossby-Haurwitz wave initial conditions as in Williamson et al. 1992, J Computational Physics with an additional cut-off amplitude c to filter out tiny harmonics in the vorticity field.

source
SpeedyWeather.initialize!Method
initialize!(
     progn_new::PrognosticVariables,
     initial_conditions::StartFromFile,
     model::AbstractModel
 ) -> 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::AbstractModel
 )
-

Initial conditions from Galewsky, 2004, Tellus

source
SpeedyWeather.initialize!Method
initialize!(scheduler::Schedule, clock::Clock) -> Schedule
-

Initialize a Schedule with a Clock (which is assumed to have been initialized). Takes both scheduler.every and scheduler.times into account, such that both periodic and events can be scheduled simulataneously. But execution will happen only once if they coincide on a given time step.

source
SpeedyWeather.initialize!Method
initialize!(scheduler::Schedule, clock::Clock) -> Schedule
+

Initialize a Schedule with a Clock (which is assumed to have been initialized). Takes both scheduler.every and scheduler.times into account, such that both periodic and events can be scheduled simulataneously. But execution will happen only once if they coincide on a given time step.

source
SpeedyWeather.initialize!Method
initialize!(
     model::ShallowWater;
     time
 ) -> Simulation{Model} where Model<:ShallowWater
-

Calls all initialize! functions for most components (=fields) of model, except for model.output and model.feedback which are always initialized in time_stepping! and model.implicit which is done in first_timesteps!.

source
SpeedyWeather.initialize!Method
initialize!(
+

Calls all initialize! functions for most components (=fields) of model, except for model.output and model.feedback which are always initialized in time_stepping! and model.implicit which is done in first_timesteps!.

source
SpeedyWeather.initialize!Method
initialize!(
     orog::ZonalRidge,
     P::SpeedyWeather.AbstractPlanet,
     S::SpectralTransform,
     G::Geometry
 )
-

Initialize the arrays orography, geopot_surf in orog following Jablonowski and Williamson, 2006.

source
SpeedyWeather.initialize!Method
initialize!(
     output::NetCDFOutput{Grid2D, Grid3D, Interpolator},
     feedback::SpeedyWeather.AbstractFeedback,
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::AbstractModel
 )
-

Initialize NetCDF output by creating a netCDF file and storing the initial conditions of diagn (and progn). To be called just before the first timesteps.

source
SpeedyWeather.initialize!Method
initialize!(
+

Initialize NetCDF output by creating a netCDF file and storing the initial conditions of diagn (and progn). To be called just before the first timesteps.

source
SpeedyWeather.initialize!Method
initialize!(
     particles::Array{Particle{NF}, 1},
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     particle_advection::ParticleAdvection2D
 ) -> Any
-

Initialize particle advection time integration: Store u,v interpolated initial conditions in diagn.particles.u and .v to be used when particle advection actually executed for first time.

source
SpeedyWeather.initialize!Method
initialize!(
+

Initialize particle advection time integration: Store u,v interpolated initial conditions in diagn.particles.u and .v to be used when particle advection actually executed for first time.

source
SpeedyWeather.initialize!Method
initialize!(
     callback::GlobalSurfaceTemperatureCallback{NF},
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::AbstractModel
 ) -> Int64
-

Initializes callback.temp vector that records the global mean surface temperature on every time step. Allocates vector of correct length (number of elements = total time steps plus one) and stores the global surface temperature of the initial conditions

source
SpeedyWeather.initialize!Method
initialize!(
+

Initializes callback.temp vector that records the global mean surface temperature on every time step. Allocates vector of correct length (number of elements = total time steps plus one) and stores the global surface temperature of the initial conditions

source
SpeedyWeather.initialize!Method
initialize!(
     progn::PrognosticVariables{NF},
     initial_conditions::JablonowskiTemperature,
     model::PrimitiveEquation
 )
-

Initial conditions from Jablonowski and Williamson, 2006, QJR Meteorol. Soc

source
SpeedyWeather.initialize!Method
initialize!(
     progn::PrognosticVariables{NF},
     initial_conditions::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},
     initial_conditions::StartWithRandomVorticity,
     model::Barotropic
 )
-

Start with random vorticity as initial conditions

source
SpeedyWeather.initialize!Method
initialize!(
     progn::PrognosticVariables{NF},
     initial_conditions::ZonalWind,
     model::PrimitiveEquation
 )
-

Initial conditions from Jablonowski and Williamson, 2006, QJR Meteorol. Soc

source
SpeedyWeather.initialize!Method
initialize!(
     particles::Array{P<:Particle, 1},
     model::AbstractModel
 )
-

Initialize particle locations uniformly in latitude, longitude and in the vertical σ coordinates. This uses a cosin-distribution in latitude for an equal-area uniformity.

source
SpeedyWeather.ismodMethod
ismod(p::Particle) -> Bool
-

Check that a particle is in longitude [0,360˚E), latitude [-90˚,90˚N], and σ in [0,1].

source
SpeedyWeather.isscheduledMethod
isscheduled(S::Schedule, clock::Clock) -> Bool
-

Evaluate whether (e.g. a callback) should be scheduled at the timestep given in clock. Returns true for scheduled executions, false for no execution on this time step.

source
SpeedyWeather.large_scale_condensation!Method
large_scale_condensation!(
+

Initialize particle locations uniformly in latitude, longitude and in the vertical σ coordinates. This uses a cosin-distribution in latitude for an equal-area uniformity.

source
SpeedyWeather.ismodMethod
ismod(p::Particle) -> Bool
+

Check that a particle is in longitude [0,360˚E), latitude [-90˚,90˚N], and σ in [0,1].

source
SpeedyWeather.isscheduledMethod
isscheduled(S::Schedule, clock::Clock) -> Bool
+

Evaluate whether (e.g. a callback) should be scheduled at the timestep given in clock. Returns true for scheduled executions, false for no execution on this time step.

source
SpeedyWeather.large_scale_condensation!Method
large_scale_condensation!(
     column::ColumnVariables,
     scheme::ImplicitCondensation,
     clausius_clapeyron::SpeedyWeather.AbstractClausiusClapeyron,
@@ -631,13 +631,13 @@
     atmosphere::SpeedyWeather.AbstractAtmosphere,
     time_stepping::SpeedyWeather.AbstractTimeStepper
 )
-

Large-scale condensation for a column by relaxation back to 100% relative humidity. Calculates the tendencies for specific humidity and temperature from latent heat release and integrates the large-scale precipitation vertically for output.

source
SpeedyWeather.launch_kernel!Method
launch_kernel!(
+

Large-scale condensation for a column by relaxation back to 100% relative humidity. Calculates the tendencies for specific humidity and temperature from latent heat release and integrates the large-scale precipitation vertically for output.

source
SpeedyWeather.launch_kernel!Method
launch_kernel!(
     device_setup::SpeedyWeather.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!(
+

Launches the kernel! on the device_setup with ndrange computations over the kernel and arguments kernel_args.

source
SpeedyWeather.leapfrog!Method
leapfrog!(
     A_old::LowerTriangularArray,
     A_new::LowerTriangularArray,
     tendency::LowerTriangularArray,
@@ -645,215 +645,215 @@
     lf::Int64,
     L::Leapfrog{NF}
 )
-

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::DiagnosticVariables,
     progn::PrognosticVariables,
     lf::Int64,
     atmosphere::SpeedyWeather.AbstractAtmosphere,
     implicit::ImplicitPrimitiveEquation
 )
-

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::DiagnosticVariables,
     progn::PrognosticVariables,
     lf::Integer,
     model::PrimitiveEquation
 )
-

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,
     lf::Integer,
     model::PrimitiveDry
 )
-

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::AbstractModel
 ) -> 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,
     clausius_clapeyron::SpeedyWeather.AbstractClausiusClapeyron
 )
-

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.moveMethod
move(
+

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.moveMethod
move(
     p::Particle{NF, false},
     args...
 ) -> Particle{NF, false} where NF
-

Inactive particles are not moved.

source
SpeedyWeather.moveMethod
move(
     p::Particle{NF, true},
     dlon,
     dlat,
     dσ
 ) -> Particle{_A, true} where _A<:AbstractFloat
-

Move a particle with increments (dlon, dlat, dσ) in those respective coordinates. Only active particles are moved.

source
SpeedyWeather.moveMethod
move(
+

Move a particle with increments (dlon, dlat, dσ) in those respective coordinates. Only active particles are moved.

source
SpeedyWeather.moveMethod
move(
     p::Particle{NF, true},
     dlon,
     dlat
 ) -> Particle{_A, true} where _A<:AbstractFloat
-

Move a particle with increments (dlon, dlat) in 2D. No movement in vertical σ. Only active particles are moved.

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!(
+

Move a particle with increments (dlon, dlat) in 2D. No movement in vertical σ. Only active particles are moved.

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.output!Method
output!(output::NetCDFOutput, time::DateTime)
-

Write the current time time::DateTime to the netCDF file in output.

source
SpeedyWeather.output!Method
output!(output::NetCDFOutput, time::DateTime)
+

Write the current time time::DateTime to the netCDF file in output.

source
SpeedyWeather.output!Method
output!(
     output::NetCDFOutput,
     output_variables::Dict{Symbol, SpeedyWeather.AbstractOutputVariable},
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::AbstractModel
 )
-

Loop over every variable in output.variables to call the respective output! method to write into the output.netcdf_file.

source
SpeedyWeather.output!Method
output!(
+

Loop over every variable in output.variables to call the respective output! method to write into the output.netcdf_file.

source
SpeedyWeather.output!Method
output!(
     output::NetCDFOutput,
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::AbstractModel
 )
-

Writes the variables from progn or diagn of time step i at time time into output.netcdf_file. Simply escapes for no netcdf output or if output shouldn't be written on this time step. Interpolates onto output grid and resolution as specified in output, converts to output number format, truncates the mantissa for higher compression and applies lossless compression.

source
SpeedyWeather.output!Method
output!(
+

Writes the variables from progn or diagn of time step i at time time into output.netcdf_file. Simply escapes for no netcdf output or if output shouldn't be written on this time step. Interpolates onto output grid and resolution as specified in output, converts to output number format, truncates the mantissa for higher compression and applies lossless compression.

source
SpeedyWeather.output!Method
output!(
     output::NetCDFOutput,
     variable::SpeedyWeather.CloudTopOutput,
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::AbstractModel
 )
-

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
+

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
     output::NetCDFOutput,
     variable::SpeedyWeather.ConvectivePrecipitationOutput,
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::AbstractModel
 )
-

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
+

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
     output::NetCDFOutput,
     variable::SpeedyWeather.DivergenceOutput,
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::AbstractModel
 )
-

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
+

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
     output::NetCDFOutput,
     variable::SpeedyWeather.HumidityOutput,
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::AbstractModel
 )
-

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
+

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
     output::NetCDFOutput,
     variable::SpeedyWeather.InterfaceDisplacementOutput,
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::AbstractModel
 )
-

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
+

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
     output::NetCDFOutput,
     variable::SpeedyWeather.LargeScalePrecipitationOutput,
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::AbstractModel
 )
-

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
+

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
     output::NetCDFOutput,
     variable::SpeedyWeather.MeridionalVelocityOutput,
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::AbstractModel
 )
-

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
+

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
     output::NetCDFOutput,
     variable::SpeedyWeather.OrographyOutput,
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::AbstractModel
 )
-

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
+

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
     output::NetCDFOutput,
     variable::SpeedyWeather.OutgoingLongwaveRadiationOutput,
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::AbstractModel
 )
-

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
+

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
     output::NetCDFOutput,
     variable::SpeedyWeather.OutgoingShortwaveRadiationOutput,
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::AbstractModel
 )
-

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
+

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
     output::NetCDFOutput,
     variable::SpeedyWeather.RandomPatternOutput,
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::AbstractModel
 )
-

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
+

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
     output::NetCDFOutput,
     variable::SpeedyWeather.SeaSurfaceTemperatureOutput,
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::AbstractModel
 )
-

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
+

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
     output::NetCDFOutput,
     variable::SpeedyWeather.SurfaceFluxHeatOutput,
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::AbstractModel
 )
-

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
+

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
     output::NetCDFOutput,
     variable::SpeedyWeather.SurfaceFluxHumidOutput,
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::AbstractModel
 )
-

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
+

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
     output::NetCDFOutput,
     variable::SpeedyWeather.SurfacePressureOutput,
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::AbstractModel
 )
-

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
+

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
     output::NetCDFOutput,
     variable::SpeedyWeather.TemperatureOutput,
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::AbstractModel
 )
-

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
+

output! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.output!Method
output!(
     output::NetCDFOutput,
     variable::SpeedyWeather.VorticityOutput,
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::AbstractModel
 )
-

Output the vorticity field vor from diagn.grid into the netCDF file output.netcdf_file. Interpolates the vorticity field onto the output grid and resolution as specified in output. Method required for all output variables <: AbstractOutputVariable with dispatch over the second argument.

source
SpeedyWeather.output!Method
output!(
+

Output the vorticity field vor from diagn.grid into the netCDF file output.netcdf_file. Interpolates the vorticity field onto the output grid and resolution as specified in output. Method required for all output variables <: AbstractOutputVariable with dispatch over the second argument.

source
SpeedyWeather.output!Method
output!(
     output::NetCDFOutput,
     variable::SpeedyWeather.ZonalVelocityOutput,
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     model::AbstractModel
 )
-

output! method for ZonalVelocityOutput to write the zonal velocity field u from diagn.grid, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.parameterization_tendencies!Method
parameterization_tendencies!(
+

output! method for ZonalVelocityOutput to write the zonal velocity field u from diagn.grid, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.

source
SpeedyWeather.parameterization_tendencies!Method
parameterization_tendencies!(
     diagn::DiagnosticVariables,
     progn::PrognosticVariables,
     time::DateTime,
     model::PrimitiveEquation
 )
-

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.physics_tendencies_only!Method
physics_tendencies_only!(
+

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.physics_tendencies_only!Method
physics_tendencies_only!(
     diagn::DiagnosticVariables,
     model::PrimitiveEquation
 )
-

For dynamics=false, after calling parameterization_tendencies! call this function to transform the physics tendencies from grid-point to spectral space including the necessary coslat⁻¹ scaling.

source
SpeedyWeather.pressure_gradient_flux!Method
pressure_gradient_flux!(
+

For dynamics=false, after calling parameterization_tendencies! call this function to transform the physics tendencies from grid-point to spectral space including the necessary coslat⁻¹ scaling.

source
SpeedyWeather.pressure_gradient_flux!Method
pressure_gradient_flux!(
     diagn::DiagnosticVariables,
     progn::PrognosticVariables,
     lf::Integer,
     S::SpectralTransform
 )
-

Compute the gradient ∇lnps of the logarithm of surface pressure, followed by its flux, (u,v) * ∇lnps.

source
SpeedyWeather.pseudo_adiabat!Method
pseudo_adiabat!(
     temp_ref_profile::AbstractVector,
     temp_parcel,
     humid_parcel::Real,
@@ -863,46 +863,46 @@
     σ::AbstractVector,
     clausius_clapeyron::SpeedyWeather.AbstractClausiusClapeyron
 ) -> Int64
-

Calculates the moist pseudo adiabat given temperature and humidity of surface parcel. Follows the dry adiabat till condensation and then continues on the pseudo moist-adiabat with immediate condensation to the level of zero buoyancy. Levels above are skipped, set to NaN instead and should be skipped in the relaxation.

source
SpeedyWeather.readable_secsMethod
readable_secs(secs::Real) -> Dates.CompoundPeriod
+

Calculates the moist pseudo adiabat given temperature and humidity of surface parcel. Follows the dry adiabat till condensation and then continues on the pseudo moist-adiabat with immediate condensation to the level of zero buoyancy. Levels above are skipped, set to NaN instead and should be skipped in the relaxation.

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> using SpeedyWeather: readable_secs
 
-julia> readable_secs(12345)
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.AbstractSimulation;
     period,
     output
 ) -> Union{UnicodePlots.Plot{T, Val{true}} where T<:UnicodePlots.HeatmapCanvas, UnicodePlots.Plot{T, Val{false}} where T<:UnicodePlots.HeatmapCanvas}
-

Run a SpeedyWeather.jl simulation. The simulation.model is assumed to be initialized.

source
SpeedyWeather.saturation_humidity!Method
saturation_humidity!(
     column::ColumnVariables,
     clausius_clapeyron::SpeedyWeather.AbstractClausiusClapeyron
 )
-

Compute the saturation water vapour pressure [Pa], the saturation humidity [kg/kg] and the relative humidity following clausius_clapeyron.

source
SpeedyWeather.saturation_humidityMethod
saturation_humidity(
+

Compute the saturation water vapour pressure [Pa], the saturation humidity [kg/kg] and the relative humidity following clausius_clapeyron.

source
SpeedyWeather.saturation_humidityMethod
saturation_humidity(
     temp_kelvin,
     pres,
     clausius_clapeyron::SpeedyWeather.AbstractClausiusClapeyron
 ) -> Any
 

Saturation humidity [kg/kg] from temperature [K], pressure [Pa] via

sat_vap_pres = clausius_clapeyron(temperature)
-saturation humidity = mol_ratio * sat_vap_pres / pressure
source
SpeedyWeather.saturation_humidityMethod
saturation_humidity(sat_vap_pres, pres; mol_ratio) -> Any
-

Saturation humidity from saturation vapour pressure and pressure via

qsat = mol_ratio*sat_vap_pres/pres

with both pressures in same units and qsat in kg/kg.

source
SpeedyWeather.saturation_humidityMethod
saturation_humidity(sat_vap_pres, pres; mol_ratio) -> Any
+

Saturation humidity from saturation vapour pressure and pressure via

qsat = mol_ratio*sat_vap_pres/pres

with both pressures in same units and qsat in kg/kg.

source
SpeedyWeather.scale!Method
scale!(
     diagn::DiagnosticVariables,
     var::Symbol,
     scale::Real
 ) -> Any
-

Scale the variable var inside diagn with scalar scale.

source
SpeedyWeather.scale!Method
scale!(
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     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, var::Symbol, scale::Real)
-

Scale the variable var inside progn with scalar scale.

source
SpeedyWeather.set!Method
set!(
+

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, var::Symbol, scale::Real)
+

Scale the variable var inside progn with scalar scale.

source
SpeedyWeather.set!Method
set!(
     model::AbstractModel;
     orography,
     land_sea_mask,
     albedo,
     kwargs...
 ) -> Bool
-

Sets a boundary condition fields for model. The input can be a function, RingGrid, LowerTriangularMatrix, or scalar as for other set! functions. If the keyword add==true the input is added to the exisiting field instead.

source
SpeedyWeather.set!Method
set!(
+

Sets a boundary condition fields for model. The input can be a function, RingGrid, LowerTriangularMatrix, or scalar as for other set! functions. If the keyword add==true the input is added to the exisiting field instead.

source
SpeedyWeather.set!Method
set!(
     progn::PrognosticVariables,
     geometry::Geometry;
     u,
@@ -923,39 +923,39 @@
     spectral_transform,
     coslat_scaling_included
 )
-

Sets new values for the keyword arguments (velocities, vorticity, divergence, etc..) into the prognostic variable struct progn at timestep index lf. If add==true they are added to the current value instead. If a SpectralTransform S is provided, it is used when needed to set the variable, otherwise it is recomputed. In case u and v are provied, actually the divergence and vorticity are set and coslat_scaling_included specficies whether or not the 1/cos(lat) scaling is already included in the arrays or not (default: false)

The input may be:

  • A function or callable object f(lond, latd, σ) -> value (multilevel variables)
  • A function or callable object f(lond, latd) -> value (surface level variables)
  • An instance of AbstractGridArray
  • An instance of LowerTriangularArray
  • A scalar <: Number (interpreted as a constant field in grid space)
source
SpeedyWeather.set!Method
set!(S::SpeedyWeather.AbstractSimulation; kwargs...) -> Any
-

Sets properties of the simuluation S. Convenience wrapper to call the other concrete set! methods. All kwargs are forwarded to these methods, which are documented seperately. See their documentation for possible kwargs.

source
SpeedyWeather.set_period!Method
set_period!(clock::Clock, period::Dates.Period) -> Second
-

Set the period of the clock to a new value. Converts any Dates.Period input to Second.

source
SpeedyWeather.set_period!Method
set_period!(clock::Clock, period::Real) -> Any
-

Set the period of the clock to a new value. Converts any ::Real input to Day.

source
SpeedyWeather.solar_hour_angleMethod
solar_hour_angle(
+

Sets new values for the keyword arguments (velocities, vorticity, divergence, etc..) into the prognostic variable struct progn at timestep index lf. If add==true they are added to the current value instead. If a SpectralTransform S is provided, it is used when needed to set the variable, otherwise it is recomputed. In case u and v are provied, actually the divergence and vorticity are set and coslat_scaling_included specficies whether or not the 1/cos(lat) scaling is already included in the arrays or not (default: false)

The input may be:

  • A function or callable object f(lond, latd, σ) -> value (multilevel variables)
  • A function or callable object f(lond, latd) -> value (surface level variables)
  • An instance of AbstractGridArray
  • An instance of LowerTriangularArray
  • A scalar <: Number (interpreted as a constant field in grid space)
source
SpeedyWeather.set!Method
set!(S::SpeedyWeather.AbstractSimulation; kwargs...) -> Any
+

Sets properties of the simuluation S. Convenience wrapper to call the other concrete set! methods. All kwargs are forwarded to these methods, which are documented seperately. See their documentation for possible kwargs.

source
SpeedyWeather.set_period!Method
set_period!(clock::Clock, period::Dates.Period) -> Second
+

Set the period of the clock to a new value. Converts any Dates.Period input to Second.

source
SpeedyWeather.set_period!Method
set_period!(clock::Clock, period::Real) -> Any
+

Set the period of the clock to a new value. Converts any ::Real input to Day.

source
SpeedyWeather.solar_hour_angleMethod
solar_hour_angle(
     _::Type{T},
     time::DateTime,
     λ,
     length_of_day::Second
 ) -> Any
-

Fraction of day as angle in radians [0...2π]. TODO: Takes length of day as argument, but a call to Dates.Time() currently have this hardcoded anyway.

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,
+

Fraction of day as angle in radians [0...2π]. TODO: Takes length of day as argument, but a call to Dates.Time() currently have this hardcoded anyway.

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_anomaly!Method
temperature_anomaly!(
+                            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_anomaly!Method
temperature_anomaly!(
     diagn::DiagnosticVariables,
     implicit::ImplicitPrimitiveEquation
 )
-

Convert absolute and virtual temperature to anomalies wrt to the reference profile

source
SpeedyWeather.temperature_average!Method
temperature_average!(
     diagn::DiagnosticVariables,
     temp::LowerTriangularArray,
     S::SpectralTransform
 )
-

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::HeldSuarez,
     atmosphere::SpeedyWeather.AbstractAtmosphere
 )
-

Apply temperature relaxation following Held and Suarez 1996, BAMS.

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::DiagnosticVariables,
     adiabatic_conversion::SpeedyWeather.AbstractAdiabaticConversion,
     atmosphere::SpeedyWeather.AbstractAtmosphere,
@@ -963,111 +963,111 @@
     G::Geometry,
     S::SpectralTransform
 )
-

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!(
+

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::AbstractModel
 ) -> Union{UnicodePlots.Plot{T, Val{true}} where T<:UnicodePlots.HeatmapCanvas, UnicodePlots.Plot{T, Val{false}} where T<:UnicodePlots.HeatmapCanvas}
-

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,
-    model::Barotropic
+    model::ShallowWater
 ) -> Union{Nothing, Bool}
 timestep!(
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     dt::Real,
-    model::Barotropic,
+    model::ShallowWater,
     lf1::Integer
 ) -> Union{Nothing, Bool}
 timestep!(
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     dt::Real,
-    model::Barotropic,
+    model::ShallowWater,
     lf1::Integer,
     lf2::Integer
 ) -> Union{Nothing, Bool}
-

Calculate a single time step for the barotropic model.

source
SpeedyWeather.timestep!Function
timestep!(
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     dt::Real,
-    model::PrimitiveEquation
+    model::Barotropic
 ) -> Union{Nothing, Bool}
 timestep!(
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     dt::Real,
-    model::PrimitiveEquation,
+    model::Barotropic,
     lf1::Integer
 ) -> Union{Nothing, Bool}
 timestep!(
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     dt::Real,
-    model::PrimitiveEquation,
+    model::Barotropic,
     lf1::Integer,
     lf2::Integer
 ) -> Union{Nothing, Bool}
-

Calculate a single time step for the model<:PrimitiveEquation

source
SpeedyWeather.timestep!Function
timestep!(
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     dt::Real,
-    model::ShallowWater
+    model::PrimitiveEquation
 ) -> Union{Nothing, Bool}
 timestep!(
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     dt::Real,
-    model::ShallowWater,
+    model::PrimitiveEquation,
     lf1::Integer
 ) -> Union{Nothing, Bool}
 timestep!(
     progn::PrognosticVariables,
     diagn::DiagnosticVariables,
     dt::Real,
-    model::ShallowWater,
+    model::PrimitiveEquation,
     lf1::Integer,
     lf2::Integer
 ) -> Union{Nothing, Bool}
-

Calculate a single time step for the model <: ShallowWater.

source
SpeedyWeather.treeMethod
tree(M::AbstractModel; modules, with_size, kwargs...)
-

Create a tree of fields inside a model and fields within these fields as long as they are defined within the modules argument (default SpeedyWeather). Other keyword arguments are max_level::Integer=10, with_types::Bool=false or `with_size::Bool=false.

source
SpeedyWeather.treeMethod
tree(S; modules, with_size, kwargs...)
-

Create a tree of fields inside S and fields within these fields as long as they are defined within the modules argument (default SpeedyWeather). Other keyword arguments are max_level::Integer=10, with_types::Bool=false or `with_size::Bool=false.

source
SpeedyWeather.treeMethod
tree(S::Simulation{M}; modules, with_size, kwargs...)
-

Create a tree of fields inside a Simulation instance and fields within these fields as long as they are defined within the modules argument (default SpeedyWeather). Other keyword arguments are max_level::Integer=10, with_types::Bool=false or `with_size::Bool=false.

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!(diagn::DiagnosticVariables) -> Int64
-

Undo the radius-scaling of vorticity and divergence from scale!(diagn, scale::Real).

source
SpeedyWeather.unscale!Method
unscale!(progn::PrognosticVariables) -> Int64
-

Undo the radius-scaling of vorticity and divergence from scale!(progn, scale::Real).

source
SpeedyWeather.treeMethod
tree(M::AbstractModel; modules, with_size, kwargs...)
+

Create a tree of fields inside a model and fields within these fields as long as they are defined within the modules argument (default SpeedyWeather). Other keyword arguments are max_level::Integer=10, with_types::Bool=false or `with_size::Bool=false.

source
SpeedyWeather.treeMethod
tree(S; modules, with_size, kwargs...)
+

Create a tree of fields inside S and fields within these fields as long as they are defined within the modules argument (default SpeedyWeather). Other keyword arguments are max_level::Integer=10, with_types::Bool=false or `with_size::Bool=false.

source
SpeedyWeather.treeMethod
tree(S::Simulation{M}; modules, with_size, kwargs...)
+

Create a tree of fields inside a Simulation instance and fields within these fields as long as they are defined within the modules argument (default SpeedyWeather). Other keyword arguments are max_level::Integer=10, with_types::Bool=false or `with_size::Bool=false.

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!(diagn::DiagnosticVariables) -> Int64
+

Undo the radius-scaling of vorticity and divergence from scale!(diagn, scale::Real).

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!(
     diagn::DiagnosticVariables,
     progn::PrognosticVariables,
     lf::Integer,
     geometry::Geometry
 )
-

Calculates the vertically averaged (weighted by the thickness of the σ level) velocities (*coslat) and divergence. E.g.

u_mean = ∑_k=1^nlayers Δσ_k * u_k

u, v are averaged in grid-point space, divergence in spectral space.

source
SpeedyWeather.vertical_interpolate!Method
vertical_interpolate!(
+

Calculates the vertically averaged (weighted by the thickness of the σ level) velocities (*coslat) and divergence. E.g.

u_mean = ∑_k=1^nlayers Δσ_k * u_k

u, v are averaged in grid-point space, divergence in spectral space.

source
SpeedyWeather.vertical_interpolate!Method
vertical_interpolate!(
     A_half::Vector,
     A_full::Vector,
     G::Geometry
 )
-

Given a vector in column defined at full levels, do a linear interpolation in log(σ) to calculate its values at half-levels, skipping top (k=1/2), extrapolating to bottom (k=nlayers+1/2).

source
SpeedyWeather.virtual_temperature!Method
virtual_temperature!(
+

Given a vector in column defined at full levels, do a linear interpolation in log(σ) to calculate its values at half-levels, skipping top (k=1/2), extrapolating to bottom (k=nlayers+1/2).

source
SpeedyWeather.virtual_temperature!Method
virtual_temperature!(
     diagn::DiagnosticVariables,
     model::PrimitiveDry
 )
-

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.virtual_temperature!Method
virtual_temperature!(
+

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.virtual_temperature!Method
virtual_temperature!(
     diagn::DiagnosticVariables,
     model::PrimitiveWet
 )
-

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.volume_flux_divergence!Method
volume_flux_divergence!(
+

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.volume_flux_divergence!Method
volume_flux_divergence!(
     diagn::DiagnosticVariables,
     orog::SpeedyWeather.AbstractOrography,
     atmosphere::SpeedyWeather.AbstractAtmosphere,
     G::SpeedyWeather.AbstractGeometry,
     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::DiagnosticVariables,
     coriolis::SpeedyWeather.AbstractCoriolis,
     atmosphere::SpeedyWeather.AbstractAtmosphere,
@@ -1076,15 +1076,15 @@
 )
 

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.vorticity_flux!Method
vorticity_flux!(
     diagn::DiagnosticVariables,
     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::DiagnosticVariables,
     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::DiagnosticVariables,
     coriolis::SpeedyWeather.AbstractCoriolis,
     geometry::Geometry,
@@ -1092,27 +1092,27 @@
     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(
+

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(
     device::SpeedyWeather.AbstractDevice
 ) -> Int64
-

Returns a workgroup size depending on device. WIP: Will be expanded in the future to also include grid information.

source
SpeedyWeather.write_column_tendencies!Method
write_column_tendencies!(
     diagn::DiagnosticVariables,
     column::ColumnVariables,
     planet::SpeedyWeather.AbstractPlanet,
     ij::Integer
 )
-

Write the parametrization tendencies from C::ColumnVariables into the horizontal fields of tendencies stored in D::DiagnosticVariables at gridpoint index ij.

source
SpeedyWeather.write_restart_fileMethod
write_restart_file(
+

Write the parametrization tendencies from C::ColumnVariables into the horizontal fields of tendencies stored in D::DiagnosticVariables at gridpoint index ij.

source
SpeedyWeather.write_restart_fileMethod
write_restart_file(
     output::SpeedyWeather.AbstractOutput,
     progn::PrognosticVariables{T}
 ) -> Any
-

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.year_angleMethod
year_angle(
+

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.year_angleMethod
year_angle(
     _::Type{T},
     time::DateTime,
     length_of_day::Second,
     length_of_year::Second
 ) -> Any
-

Fraction of year as angle in radians [0...2π]. TODO: Takes length of day/year as argument, but calls to Dates.Time(), Dates.dayofyear() currently have these hardcoded.

source
SpeedyWeather.σ_interpolation_weightsMethod
σ_interpolation_weights(
+

Fraction of year as angle in radians [0...2π]. TODO: Takes length of day/year as argument, but calls to Dates.Time(), Dates.dayofyear() currently have these hardcoded.

source
SpeedyWeather.σ_interpolation_weightsMethod
σ_interpolation_weights(
     σ_levels_full::AbstractVector,
     σ_levels_half::AbstractVector
 ) -> Any
-

Interpolation weights for full to half level interpolation on sigma coordinates. Following Fortran SPEEDY documentation eq. (1).

source
+

Interpolation weights for full to half level interpolation on sigma coordinates. Following Fortran SPEEDY documentation eq. (1).

source
diff --git a/previews/PR645/gradients/index.html b/previews/PR645/gradients/index.html index c005053f6..eeb50f3da 100644 --- a/previews/PR645/gradients/index.html +++ b/previews/PR645/gradients/index.html @@ -56,4 +56,4 @@ η = SpeedyTransforms.∇⁻²(fζ_g_spectral) * R^2 η_grid = transform(η, Grid=spectral_grid.Grid)

Note the manual scaling with the radius $R^2$ here. We now compare the results

using CairoMakie
 heatmap(η_grid, title="Geostrophic interface displacement η [m]")

Geostrophic eta

Which is the interface displacement assuming geostrophy. The actual interface displacement contains also ageostrophy

η_grid2 = simulation.diagnostic_variables.grid.pres_grid
-heatmap(η_grid2, title="Interface displacement η [m] with ageostrophy")

Ageostrophic eta

Strikingly similar! The remaining differences are the ageostrophic motions but also note that the mean can be 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$ can be higher or lower depending on the mass/volume in the shallow water system, see Mass conservation.

+heatmap(η_grid2, title="Interface displacement η [m] with ageostrophy")

Ageostrophic eta

Strikingly similar! The remaining differences are the ageostrophic motions but also note that the mean can be 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$ can be higher or lower depending on the mass/volume in the shallow water system, see Mass conservation.

diff --git a/previews/PR645/grids/index.html b/previews/PR645/grids/index.html index 4a4005478..0cb443111 100644 --- a/previews/PR645/grids/index.html +++ b/previews/PR645/grids/index.html @@ -17,4 +17,4 @@ globe(FullClenshawGrid, 24, interactive=false)

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)

using CairoMakie, GeoMakie    # when using GLMakie, use interactive=true (default) for zoom and rotation
 globe(OctahedralClenshawGrid, 24, interactive=false)

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)

using CairoMakie, GeoMakie    # when using GLMakie, use interactive=true (default) for zoom and rotation
 globe(HEALPixGrid, 24, interactive=false)

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)

using CairoMakie, GeoMakie    # when using GLMakie, use interactive=true (default) for zoom and rotation
-globe(OctaHEALPixGrid, 24, interactive=false)

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

+globe(OctaHEALPixGrid, 24, interactive=false)

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/PR645/how_to_run_speedy/index.html b/previews/PR645/how_to_run_speedy/index.html index 0dec42c48..5d92bcccf 100644 --- a/previews/PR645/how_to_run_speedy/index.html +++ b/previews/PR645/how_to_run_speedy/index.html @@ -133,4 +133,4 @@ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄ -90 ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ └──┘ └────────────────────────────────────────────────────────────┘ -8e⁻⁵ - 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/PR645/index.html b/previews/PR645/index.html index b41c12c93..a7b0c9692 100644 --- a/previews/PR645/index.html +++ b/previews/PR645/index.html @@ -10,4 +10,4 @@ author = {Milan Klöwer and Maximilian Gelbrecht and Daisuke Hotta and Justin Willmert and Simone Silvestri and Gregory L. Wagner and Alistair White and Sam Hatfield and Tom Kimpson and Navid C. Constantinou and Chris Hill}, title = {{SpeedyWeather.jl: Reinventing atmospheric general circulation models towards interactivity and extensibility}}, journal = {Journal of Open Source Software} -}

Funding

MK received funding by the European Research Council under Horizon 2020 within the ITHACA project, grant agreement number 741112 from 2021-2022. From 2022-2024 this project was also funded by the National Science Foundation NSF. Since 2024, the main funding is from Schmidt Sciences LLC through MK's Eric & Wendy Schmidt AI in Science Fellowship.

+}

Funding

MK received funding by the European Research Council under Horizon 2020 within the ITHACA project, grant agreement number 741112 from 2021-2022. From 2022-2024 this project was also funded by the National Science Foundation NSF. Since 2024, the main funding is from Schmidt Sciences LLC through MK's Eric & Wendy Schmidt AI in Science Fellowship.

diff --git a/previews/PR645/initial_conditions/index.html b/previews/PR645/initial_conditions/index.html index 7a2bfac3b..d0f3be980 100644 --- a/previews/PR645/initial_conditions/index.html +++ b/previews/PR645/initial_conditions/index.html @@ -56,4 +56,4 @@ model = PrimitiveDryModel(spectral_grid; time_stepping, initial_conditions, orography, physics=false) simulation = initialize!(model) run!(simulation, period=Day(5))

Note that we chose a lower resolution here (T42) as we are simulating 8 vertical layers now too. Let us visualise the surface vorticity ([:, 8] is the lowermost layer)

vor = simulation.diagnostic_variables.grid.vor_grid[:, 8]
-heatmap(vor, title="Relative vorticity [1/s], primitive Rossby-Haurwitz wave")

Rossby-Haurwitz wave in primitive equations

As you can see the actual Rossby-Haurwitz wave is not as stable anymore (because those initial conditions are not a stable solution of the primitive equations) and so the 3-day integration looks already different from the barotropic model!

References

+heatmap(vor, title="Relative vorticity [1/s], primitive Rossby-Haurwitz wave")

Rossby-Haurwitz wave in primitive equations

As you can see the actual Rossby-Haurwitz wave is not as stable anymore (because those initial conditions are not a stable solution of the primitive equations) and so the 3-day integration looks already different from the barotropic model!

References

diff --git a/previews/PR645/installation/index.html b/previews/PR645/installation/index.html index eafb38c0b..c7e3a286a 100644 --- a/previews/PR645/installation/index.html +++ b/previews/PR645/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")

In a similar manner, other branches can be also installed. You can also type, equivalently,

julia> ] add https://github.com/SpeedyWeather/SpeedyWeather.jl#main

Compatibility with Julia versions

SpeedyWeather.jl requires Julia v1.10 or later. The package is tested on Julia 1.10, and 1.11.

Extensions

SpeedyWeather.jl has a weak dependency on

This is an extension, meaning that this functionality is only loaded from SpeedyWeather when using Makie (or its backends CairoMakie.jl, GLMakie.jl, ...). Hence, if you want to make use of this extension (some Examples show this) you need to install Makie.jl manually.

+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")

In a similar manner, other branches can be also installed. You can also type, equivalently,

julia> ] add https://github.com/SpeedyWeather/SpeedyWeather.jl#main

Compatibility with Julia versions

SpeedyWeather.jl requires Julia v1.10 or later. The package is tested on Julia 1.10, and 1.11.

Extensions

SpeedyWeather.jl has a weak dependency on

This is an extension, meaning that this functionality is only loaded from SpeedyWeather when using Makie (or its backends CairoMakie.jl, GLMakie.jl, ...). Hence, if you want to make use of this extension (some Examples show this) you need to install Makie.jl manually.

diff --git a/previews/PR645/land_sea_mask/index.html b/previews/PR645/land_sea_mask/index.html index 9ed62a2f0..3e8c0a4a4 100644 --- a/previews/PR645/land_sea_mask/index.html +++ b/previews/PR645/land_sea_mask/index.html @@ -60,4 +60,4 @@ simulation = initialize!(model, time=DateTime(1999,12,29)) run!(simulation, period=Day(5)) -heatmap(model.land_sea_mask.mask, title="Land-sea mask after MilleniumFlood callback")
[ Info: Everything flooded on 2000-01-01T00:00:00

Land-sea mask2

And the land-sea mask has successfully been set to ocean everywhere at the start of the 21st century. Note that while we added an @info line into the callback! function, this is here not printed because of how the Documenter works. If you execute this in the REPL you'll see it.

+heatmap(model.land_sea_mask.mask, title="Land-sea mask after MilleniumFlood callback")
[ Info: Everything flooded on 2000-01-01T00:00:00

Land-sea mask2

And the land-sea mask has successfully been set to ocean everywhere at the start of the 21st century. Note that while we added an @info line into the callback! function, this is here not printed because of how the Documenter works. If you execute this in the REPL you'll see it.

diff --git a/previews/PR645/large_scale_condensation/index.html b/previews/PR645/large_scale_condensation/index.html index 4649c1876..4e9ecf633 100644 --- a/previews/PR645/large_scale_condensation/index.html +++ b/previews/PR645/large_scale_condensation/index.html @@ -14,4 +14,4 @@ \left( 1 + \frac{L_v}{c_p} \frac{\partial q^\star}{\partial T}(T_i) \right)} \\ \delta T = \frac{T_{i+1} - T_i}{\Delta t} &= -\frac{L_v}{c_p}( \frac{q_{i+1} - q_i}{\Delta t} ) \end{aligned}\]

For $\Delta t = \Delta t_c$ we have an immediate condensation, for $n = \frac{\Delta t_c}{\Delta t}$ condensation takes place over $n$ time steps. One could tie this time scale for condensation to a physical unit, like 6 hours, but because the time step here is ideally short, but cannot be too short for numerical stability, we tie it here to the time step of the numerical integration. This also means that at higher resolution condensation is more immediate than at low resolution, but the dispersive time integration of this term is in all cases similar (and not much higher at lower resolution).

The above implied that condensation takes place at 100% relative humidity as we are directly comparing the specific humidity to the saturation specific humidity in $q^\star - q_i$. However, in coarse-resolution models there is a good argument that condensation may already take place below a grid-cell average of 100% relative humidity. Given some subgrid-scale variability parts of the grid cells may condense even though the average humidity is below 100%. To change this threshold one can introduce a parameter $r$, e.g. $r=0.95$ for condensation to take place at 95% relative humidity, as follows

\[\delta q = \frac{q_{i+1} - q_i}{\Delta t} = \frac{rq^\star(T_i) - q_i}{ \Delta t_c -\left( 1 + \frac{L_vr}{c_p} \frac{\partial q^\star}{\partial T}(T_i) \right)}\]

$r$ is a linear scale and therefore can be taken out of the gradient $\frac{\partial q^\star}{\partial T}$ in the denominator.

Large-scale precipitation

The tendencies $\delta q$ in units of kg/kg/s are vertically integrated to diagnose the large-scale precipitation $P$ in units of meters

\[P = -\int \frac{\Delta t}{g \rho} \delta q dp\]

with gravity $g$, water density $\rho$ and time step $\Delta t$. $P$ is therefore interpreted as the amount of precipitation that falls down during the time step $\Delta t$ of the time integration. Note that $\delta q$ is always negative due to the $q > q^\star$ condition for saturation, hence $P$ is positive only. It is then accumulated over several time steps, e.g. over the course of an hour to yield a typical rain rate of mm/h. The water density is taken as reference density of $1000~kg/m^3$

References

+\left( 1 + \frac{L_vr}{c_p} \frac{\partial q^\star}{\partial T}(T_i) \right)}\]

$r$ is a linear scale and therefore can be taken out of the gradient $\frac{\partial q^\star}{\partial T}$ in the denominator.

Large-scale precipitation

The tendencies $\delta q$ in units of kg/kg/s are vertically integrated to diagnose the large-scale precipitation $P$ in units of meters

\[P = -\int \frac{\Delta t}{g \rho} \delta q dp\]

with gravity $g$, water density $\rho$ and time step $\Delta t$. $P$ is therefore interpreted as the amount of precipitation that falls down during the time step $\Delta t$ of the time integration. Note that $\delta q$ is always negative due to the $q > q^\star$ condition for saturation, hence $P$ is positive only. It is then accumulated over several time steps, e.g. over the course of an hour to yield a typical rain rate of mm/h. The water density is taken as reference density of $1000~kg/m^3$

References

diff --git a/previews/PR645/lowertriangularmatrices/index.html b/previews/PR645/lowertriangularmatrices/index.html index 2111b1602..114dd1a3d 100644 --- a/previews/PR645/lowertriangularmatrices/index.html +++ b/previews/PR645/lowertriangularmatrices/index.html @@ -97,12 +97,12 @@ The function `inv` exists, but no method is defined for this combination of argument types. Closest candidates are: - inv(::Geodesy.ECEFfromLLA) - @ Geodesy ~/.julia/packages/Geodesy/otwWk/src/transformations.jl:191 - inv(::Proj.PJ_DIRECTION) - @ Proj ~/.julia/packages/Proj/8LMij/src/coord.jl:353 - inv(::CoordinateTransformations.PolarFromCartesian) - @ CoordinateTransformations ~/.julia/packages/CoordinateTransformations/jYKYg/src/coordinatesystems.jl:66 + inv(::CoordinateTransformations.CylindricalFromSpherical) + @ CoordinateTransformations ~/.julia/packages/CoordinateTransformations/jYKYg/src/coordinatesystems.jl:253 + inv(::Crayons.ANSIColor) + @ Crayons ~/.julia/packages/Crayons/u3AH8/src/crayon.jl:64 + inv(::CoordinateTransformations.SphericalFromCylindrical) + @ CoordinateTransformations ~/.julia/packages/CoordinateTransformations/jYKYg/src/coordinatesystems.jl:254 ...

And many other operations that require L to be a AbstractMatrix which it isn't. In contrast, typical vector operations like a scalar product between two "LowerTriangularMatrix" vectors does work

julia> L' * L1.767894f0

Broadcasting with LowerTriangularArray

In contrast to linear algebra, many element-wise operations work as expected thanks to broadcasting, so operations that can be written in . notation whether implicit +, 2*, ... or explicitly written .+, .^, ... or via the @. macro

julia> L + L6-element, 3x3 LowerTriangularMatrix{Float32}
  0.869594  0.0       0.0
  1.41462   1.43041   0.0
@@ -114,39 +114,39 @@
   0.566745   0.607602   0.0
  -2.70268   -1.09749   -0.542568

GPU

LowerTriangularArray{T, N, ArrayType} wraps around an array of type ArrayType. If this array is a GPU array (e.g. CuArray), all operations are performed on GPU as well (work in progress). The implementation was written so that scalar indexing is avoided in almost all cases, so that GPU operation should be performant. To use LowerTriangularArray on GPU you can e.g. just adapt an existing LowerTriangularArray.

using Adapt
 L = rand(LowerTriangularArray{Float32}, 5, 5, 5)
-L_gpu = adapt(CuArray, L)

Function and type index

SpeedyWeather.LowerTriangularMatrices.LowerTriangularArrayType

A lower triangular array implementation that only stores the non-zero entries explicitly. L<:AbstractArray{T,N-1} although we do allow both "flat" N-1-dimensional indexing and additional N-dimensional or "matrix-style" indexing.

Supports n-dimensional lower triangular arrays, so that for all trailing dimensions L[:, :, ..] is a matrix in lower triangular form, e.g. a (5x5x3)-LowerTriangularArray would hold 3 lower triangular matrices.

source
SpeedyWeather.LowerTriangularMatrices.LowerTriangularArrayMethod
LowerTriangularArray(
+L_gpu = adapt(CuArray, L)

Function and type index

SpeedyWeather.LowerTriangularMatrices.LowerTriangularArrayType

A lower triangular array implementation that only stores the non-zero entries explicitly. L<:AbstractArray{T,N-1} although we do allow both "flat" N-1-dimensional indexing and additional N-dimensional or "matrix-style" indexing.

Supports n-dimensional lower triangular arrays, so that for all trailing dimensions L[:, :, ..] is a matrix in lower triangular form, e.g. a (5x5x3)-LowerTriangularArray would hold 3 lower triangular matrices.

source
SpeedyWeather.LowerTriangularMatrices.OneBasedType

Abstract type to dispatch for 1-based indexing of the spherical harmonic degree l and order m, i.e. l=m=1 is the mean, the zonal modes are m=1 etc. This indexing matches Julia's 1-based indexing for arrays.

source
Base.fill!Method
fill!(L::LowerTriangularArray, x) -> LowerTriangularArray
-

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
Base.lengthMethod
length(L::LowerTriangularArray) -> Any
-

Length of a LowerTriangularArray defined as number of non-zero elements.

source
Base.sizeFunction
size(L::LowerTriangularArray; ...) -> Any
+

Create a LowerTriangularArray L from Array M by copying over the non-zero elements in M.

source
SpeedyWeather.LowerTriangularMatrices.OneBasedType

Abstract type to dispatch for 1-based indexing of the spherical harmonic degree l and order m, i.e. l=m=1 is the mean, the zonal modes are m=1 etc. This indexing matches Julia's 1-based indexing for arrays.

source
Base.fill!Method
fill!(L::LowerTriangularArray, x) -> LowerTriangularArray
+

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
Base.lengthMethod
length(L::LowerTriangularArray) -> Any
+

Length of a LowerTriangularArray defined as number of non-zero elements.

source
Base.sizeFunction
size(L::LowerTriangularArray; ...) -> Any
 size(
     L::LowerTriangularArray,
     base::Type{<:SpeedyWeather.LowerTriangularMatrices.IndexBasis};
     as
 ) -> Any
-

Size of a LowerTriangularArray defined as size of the flattened array if as <: AbstractVector and as if it were a full matrix when as <: AbstractMatrix` .

source
SpeedyWeather.LowerTriangularMatrices.eachharmonicMethod
eachharmonic(
     L1::LowerTriangularArray,
     Ls::LowerTriangularArray...
 ) -> Any
-

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.eachharmonicMethod
eachharmonic(L::LowerTriangularArray) -> Any
-

creates unit_range::UnitRange to loop over all non-zeros/spherical harmonics numbers in a LowerTriangularArray L. Like eachindex but skips the upper triangle with zeros in L.

source
SpeedyWeather.LowerTriangularMatrices.eachmatrixMethod
eachmatrix(
+

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.eachharmonicMethod
eachharmonic(L::LowerTriangularArray) -> Any
+

creates unit_range::UnitRange to loop over all non-zeros/spherical harmonics numbers in a LowerTriangularArray L. Like eachindex but skips the upper triangle with zeros in L.

source
SpeedyWeather.LowerTriangularMatrices.eachmatrixMethod
eachmatrix(
     L1::LowerTriangularArray,
     Ls::LowerTriangularArray...
 ) -> Any
-

Iterator for the non-horizontal dimensions in LowerTriangularArrays. Checks that the LowerTriangularArrays match according to lowertriangular_match.

source
SpeedyWeather.LowerTriangularMatrices.eachmatrixMethod
eachmatrix(L::LowerTriangularArray) -> Any
 

Iterator for the non-horizontal dimensions in LowerTriangularArrays. To be used like

for k in eachmatrix(L)
-    L[1, k]

to loop over every non-horizontal dimension of L.

source
SpeedyWeather.LowerTriangularMatrices.ij2kMethod
ij2k(i::Integer, j::Integer, m::Integer) -> Any
-

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
SpeedyWeather.LowerTriangularMatrices.k2ijMethod
k2ij(k::Integer, m::Integer) -> Tuple{Any, Any}
-

Converts the linear index k in the lower triangle into a pair (i, j) of indices of the matrix in column-major form. (Formula taken from Angeletti et al, 2019, https://hal.science/hal-02047514/document)

source
SpeedyWeather.LowerTriangularMatrices.ij2kMethod
ij2k(i::Integer, j::Integer, m::Integer) -> Any
+

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
SpeedyWeather.LowerTriangularMatrices.k2ijMethod
k2ij(k::Integer, m::Integer) -> Tuple{Any, Any}
+

Converts the linear index k in the lower triangle into a pair (i, j) of indices of the matrix in column-major form. (Formula taken from Angeletti et al, 2019, https://hal.science/hal-02047514/document)

source
SpeedyWeather.LowerTriangularMatrices.lowertriangular_matchMethod
lowertriangular_match(
     L1::LowerTriangularArray,
     L2::LowerTriangularArray;
     horizontal_only
 ) -> Any
-

True if both L1 and L2 are of the same size (as matrix), but ignores singleton dimensions, e.g. 5x5 and 5x5x1 would match. With horizontal_only=true (default false) ignore the non-horizontal dimensions, e.g. 5x5, 5x5x1, 5x5x2 would all match.

source
SpeedyWeather.LowerTriangularMatrices.lowertriangular_matchMethod
lowertriangular_match(
+

True if both L1 and L2 are of the same size (as matrix), but ignores singleton dimensions, e.g. 5x5 and 5x5x1 would match. With horizontal_only=true (default false) ignore the non-horizontal dimensions, e.g. 5x5, 5x5x1, 5x5x2 would all match.

source
+

True if all lower triangular matrices provided as arguments match according to lowertriangular_match wrt to L1 (and therefore all).

source diff --git a/previews/PR645/ocean/index.html b/previews/PR645/ocean/index.html index 209389e62..3a63c2c56 100644 --- a/previews/PR645/ocean/index.html +++ b/previews/PR645/ocean/index.html @@ -29,4 +29,4 @@ # ocean.sea_ice_concentration on any timestep # you should also synchronize the clocks when executed like # ocean.time = time -end

which is called on every time step before the land and before the parameterization and therefore also before the dynamics. You can schedule the execution with Schedules or you can use the ocean.time time to determine when last the ocean time step was executed and whether it should be executed now, e.g. (time - ocean.time) < ocean_model.Δt && return nothing would not execute unless the period of the ocean_model.Δt time step has passed. Note that the ocean.sea_surface_temperature or .sea_ice_concentration are unchanged if the ocean time step is not executed, meaning that the sea surface temperatures for example can lag behind the dynamical core for some days essentially assuming constant temperatures throughout that period. Any ocean model with constant temperatures and sea ice should just return nothing.

+end

which is called on every time step before the land and before the parameterization and therefore also before the dynamics. You can schedule the execution with Schedules or you can use the ocean.time time to determine when last the ocean time step was executed and whether it should be executed now, e.g. (time - ocean.time) < ocean_model.Δt && return nothing would not execute unless the period of the ocean_model.Δt time step has passed. Note that the ocean.sea_surface_temperature or .sea_ice_concentration are unchanged if the ocean time step is not executed, meaning that the sea surface temperatures for example can lag behind the dynamical core for some days essentially assuming constant temperatures throughout that period. Any ocean model with constant temperatures and sea ice should just return nothing.

diff --git a/previews/PR645/orography/index.html b/previews/PR645/orography/index.html index d4a462fb8..2812c7c95 100644 --- a/previews/PR645/orography/index.html +++ b/previews/PR645/orography/index.html @@ -67,4 +67,4 @@ geopot_surf .*= model.planet.gravity spectral_truncation!(geopot_surf) return nothing -end +end diff --git a/previews/PR645/output/index.html b/previews/PR645/output/index.html index 69c153b25..18c802796 100644 --- a/previews/PR645/output/index.html +++ b/previews/PR645/output/index.html @@ -214,4 +214,4 @@

Constructor for NetCDFOutput based on S::SpectralGrid and optionally the Model type (e.g. ShallowWater, PrimitiveWet) as second positional argument. The output grid is optionally determined by keyword arguments output_Grid (its type, full grid required), nlat_half (resolution) and output_NF (number format). By default, uses the full grid equivalent of the grid and resolution used in SpectralGrid S.

- + diff --git a/previews/PR645/parameterizations/index.html b/previews/PR645/parameterizations/index.html index 3cb654b00..b7cf6a628 100644 --- a/previews/PR645/parameterizations/index.html +++ b/previews/PR645/parameterizations/index.html @@ -9,4 +9,4 @@ ├ relative_humidity::Float32 = 0.7 └ surface_temp_humid::NoSurfacePerturbation = NoSurfacePerturbation <: SpeedyWeather.AbstractSurfacePerturbation

Further keyword arguments can be added or omitted all together (using the default setup), only the spectral_grid is required. We have chosen here the name of that parameterization to be convection but you could choose any other name too. However, with this choice one can conveniently pass on via matching the convection field inside PrimitiveWetModel, see Keyword Arguments.

model = PrimitiveWetModel(spectral_grid; convection)

otherwise we would need to write

my_convection = SimplifiedBettsMiller(spectral_grid)
-model = PrimitiveWetModel(spectral_grid, convection=my_convection)

The following is an overview of what the parameterization fields inside the model are called. See also Tree structure, and therein PrimitiveDryModel and PrimitiveWetModel

Note that the parameterizations are executed in the order of the list above. That way, for example, radiation can depend on calculations in large-scale condensation but not vice versa (only at the next time step).

Custom boundary layer drag

A boundary layer drag can serve two purposes: (1) Actually define a tendency to the momentum equations that acts as a drag term, or (2) calculate the drag coefficient $C$ in column.boundary_layer_drag that is used in the Surface fluxes.

Custom temperature relaxation

By default, there is no temperature relaxation in the primitive equation models (i.e. temperature_relaxation = NoTemperatureRelaxation()). This parameterization exists for the Held-Suarez forcing.

Custom vertical diffusion

While vertical diffusion may be applied to temperature (usually via some form of static energy to account for adiabatic diffusion), humidity and/or momentum, they are grouped together. You can define a vertical diffusion for only one or several of these variables where you then can internally call functions like diffuse_temperature!(...) for each variable. For vertical diffusion

Custom convection

Note that we define convection here for a model of type PrimitiveEquation, i.e. both dry and moist convection. If your CustomConvection only makes sense for one of them use ::PrimitiveDry or ::PrimitiveWet instead.

Custom large-scale condensation

Custom radiation

AbstractRadiation has two subtypes, AbstractShortwave and AbstractLongwave representing two (from a software engineering perspective) independent parameterizations that are called one after another (short then long). For shortwave

For longwave this is similar but using <: AbstractLongwave and longwave_radiation!.

Custom surface fluxes

Surface fluxes are the most complicated to customize as they depend on the Ocean and Land model, The land-sea mask, and by default the Bulk Richardson-based drag coefficient, see Custom boundary layer drag. The computation of the surface fluxes is split into four (five if you include the boundary layer drag coefficient in Custom boundary layer drag) components that are called one after another

  1. Surface thermodynamics to calculate the surface values of lowermost layer variables
  1. Surface wind to calculate wind stress (momentum flux) as well as surface wind used
  1. Surface (sensible) heat flux
  1. Surface evaporation

You can customize individual components and leave the other ones as default or by setting them to NoSurfaceWind, NoSurfaceHeatFlux, NoSurfaceEvaporation, but note that without the surface wind the heat and evaporative fluxes are also effectively disabled as they scale with the column.surface_wind_speed set by default with the surface_wind_stress! in (2.) above.

+model = PrimitiveWetModel(spectral_grid, convection=my_convection)

The following is an overview of what the parameterization fields inside the model are called. See also Tree structure, and therein PrimitiveDryModel and PrimitiveWetModel

Note that the parameterizations are executed in the order of the list above. That way, for example, radiation can depend on calculations in large-scale condensation but not vice versa (only at the next time step).

Custom boundary layer drag

A boundary layer drag can serve two purposes: (1) Actually define a tendency to the momentum equations that acts as a drag term, or (2) calculate the drag coefficient $C$ in column.boundary_layer_drag that is used in the Surface fluxes.

Custom temperature relaxation

By default, there is no temperature relaxation in the primitive equation models (i.e. temperature_relaxation = NoTemperatureRelaxation()). This parameterization exists for the Held-Suarez forcing.

Custom vertical diffusion

While vertical diffusion may be applied to temperature (usually via some form of static energy to account for adiabatic diffusion), humidity and/or momentum, they are grouped together. You can define a vertical diffusion for only one or several of these variables where you then can internally call functions like diffuse_temperature!(...) for each variable. For vertical diffusion

Custom convection

Note that we define convection here for a model of type PrimitiveEquation, i.e. both dry and moist convection. If your CustomConvection only makes sense for one of them use ::PrimitiveDry or ::PrimitiveWet instead.

Custom large-scale condensation

Custom radiation

AbstractRadiation has two subtypes, AbstractShortwave and AbstractLongwave representing two (from a software engineering perspective) independent parameterizations that are called one after another (short then long). For shortwave

For longwave this is similar but using <: AbstractLongwave and longwave_radiation!.

Custom surface fluxes

Surface fluxes are the most complicated to customize as they depend on the Ocean and Land model, The land-sea mask, and by default the Bulk Richardson-based drag coefficient, see Custom boundary layer drag. The computation of the surface fluxes is split into four (five if you include the boundary layer drag coefficient in Custom boundary layer drag) components that are called one after another

  1. Surface thermodynamics to calculate the surface values of lowermost layer variables
  1. Surface wind to calculate wind stress (momentum flux) as well as surface wind used
  1. Surface (sensible) heat flux
  1. Surface evaporation

You can customize individual components and leave the other ones as default or by setting them to NoSurfaceWind, NoSurfaceHeatFlux, NoSurfaceEvaporation, but note that without the surface wind the heat and evaporative fluxes are also effectively disabled as they scale with the column.surface_wind_speed set by default with the surface_wind_stress! in (2.) above.

diff --git a/previews/PR645/particles/index.html b/previews/PR645/particles/index.html index 5d40674fe..c6281bcb6 100644 --- a/previews/PR645/particles/index.html +++ b/previews/PR645/particles/index.html @@ -100,4 +100,4 @@ fig = Figure() ga = GeoAxis(fig[1, 1]; dest = "+proj=ortho +lon_0=45 +lat_0=45") [lines!(ga, lon[i,:], lat[i,:]) for i in 1:nparticles] -fig

Particle advection

+fig

Particle advection

diff --git a/previews/PR645/primitiveequation/index.html b/previews/PR645/primitiveequation/index.html index d4635f440..930903744 100644 --- a/previews/PR645/primitiveequation/index.html +++ b/previews/PR645/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/PR645/radiation/index.html b/previews/PR645/radiation/index.html index 4be0c7032..8d05d3b6d 100644 --- a/previews/PR645/radiation/index.html +++ b/previews/PR645/radiation/index.html @@ -7,4 +7,4 @@ UniformCooling

Uniform cooling

Following Paulius and Garner[PG06], the uniform cooling of the atmosphere is defined as

\[\frac{\partial T}{\partial t} = \begin{cases} - \tau^{-1}\quad&\text{for}\quad T > T_{min} \\ \frac{T_{strat} - T}{\tau_{strat}} \quad &\text{else.} \end{cases}\]

with $\tau = 16~h$ resulting in a cooling of -1.5K/day for most of the atmosphere, except below temperatures of $T_{min} = 207.5~K$ in the stratosphere where a relaxation towards $T_{strat} = 200~K$ with a time scale of $\tau_{strat} = 5~days$ is present.

Jeevanjee radiation

Jeevanjee and Zhou [JZ22] (eq. 2) define a longwave radiative flux $F$ for atmospheric cooling as (following Seeley and Wordsworth [SW23], eq. 1)

\[\frac{dF}{dT} = α*(T_t - T)\]

The flux $F$ (in $W/m^2/K$) is a vertical upward flux between two layers (vertically adjacent) of temperature difference $dT$. The change of this flux across layers depends on the temperature $T$ and is a relaxation term towards a prescribed stratospheric temperature $T_t = 200~K$ with a radiative forcing constant $\alpha = 0.025 W/m^2/K^2$. Two layers of identical temperatures $T_1 = T_2$ would have no net flux between them, but a layer below at higher temperature would flux into colder layers above as long as its temperature $T > T_t$. This flux is applied above the lowermost layer and above, leaving the surface fluxes unchanged. The uppermost layer is tied to $T_t$ through a relaxation at time scale $\tau = 6~h$

\[\frac{\partial T}{\partial t} = \frac{T_t - T}{\tau}\]

The flux $F$ is converted to temperature tendencies at layer $k$ via

\[\frac{\partial T_k}{\partial t} = (F_{k+1/2} - F_{k-1/2})\frac{g}{\Delta p c_p}\]

The term in parentheses is the absorbed flux in layer $k$ of the upward flux from below at interface $k+1/2$ ($k$ increases downwards, see Vertical coordinates and resolution and Sigma coordinates). $\Delta p = p_{k+1/2} - p_{k-1/2}$ is the pressure thickness of layer $k$, gravity $g$ and heat capacity $c_p$.

Shortwave radiation

Currently implemented is

subtypes(SpeedyWeather.AbstractShortwave)
2-element Vector{Any}:
  NoShortwave
- TransparentShortwave

References

+ TransparentShortwave

References

diff --git a/previews/PR645/ringgrids/index.html b/previews/PR645/ringgrids/index.html index dd995dbc3..0e6bb72cd 100644 --- a/previews/PR645/ringgrids/index.html +++ b/previews/PR645/ringgrids/index.html @@ -282,30 +282,30 @@ |< Δcd >| 0...............1 # fraction of distance Δcd between c, d -^ fraction of distance Δy between a-b and c-d.

This interpolation is chosen as by definition of the ring grids, a and b share the same latitude, so do c and d, but the longitudes can be different for all four, a, b, c, d.

Function index

Core.TypeMethod

() Initialize an instance of the grid from an Array. For keyword argument input_as=Vector (default) the leading dimension is interpreted as a flat vector of all horizontal entries in one layer. For input_as==Matrx the first two leading dimensions are interpreted as longitute and latitude. This is only possible for full grids that are a subtype of AbstractFullGridArray.

source
SpeedyWeather.RingGrids.AbstractFullGridType

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 latitudes (also called Clenshaw from Clenshaw-Curtis quadrature), or others.

source
SpeedyWeather.RingGrids.AbstractFullGridArrayType

Subtype of AbstractGridArray for all N-dimensional arrays of ring grids that have the same number of longitude points on every ring. As such these (horizontal) grids are representable as a matrix, with denser grid points towards the poles.

source
SpeedyWeather.RingGrids.AbstractGridType

Abstract supertype for all ring grids, representing 2-dimensional data on the sphere unravelled into a Julia Vector. Subtype of AbstractGridArray with N=1 and ArrayType=Vector{T} of eltype T.

source
SpeedyWeather.RingGrids.AbstractGridArrayType

Abstract supertype for all arrays of ring grids, representing N-dimensional data on the sphere in two dimensions (but unravelled into a vector in the first dimension, the actual "ring grid") plus additional N-1 dimensions for the vertical and/or time etc. Parameter T is the eltype of the underlying data, held as in the array type ArrayType (Julia's Array for CPU or others for GPU).

Ring grids have several consecuitive grid points share the same latitude (= a ring), grid points on a given ring are equidistant. Grid points are ordered 0 to 360˚E, starting around the north pole, ring by ring to the south pole.

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

Horizontal abstract type for all AbstractReducedGridArray with N=1 (i.e. horizontal only) and ArrayType of Vector{T} with element type T.

source
SpeedyWeather.RingGrids.AbstractReducedGridArrayType

Subtype of AbstractGridArray for arrays of rings grids that have a reduced number of longitude points towards the poles, i.e. they are not "full", see AbstractFullGridArray. Data on these grids cannot be represented as matrix and has to be unravelled into a vector, ordered 0 to 360˚E then north to south, ring by ring. Examples for reduced grids are the octahedral Gaussian or Clenshaw grids, or the HEALPix grid.

source
SpeedyWeather.RingGrids.AbstractSphericalDistanceType
abstract type AbstractSphericalDistance end

Super type of formulas to calculate the spherical distance or great-circle distance. To define a NewFormula, define struct NewFormula <: AbstractSphericalDistance end and the actual calculation as a functor

function NewFormula(lonlat1::Tuple, lonlat2::Tuple; radius=DEFAULT_RADIUS, kwargs...)

assuming inputs in degrees and returning the distance in meters (or radians for radius=1). Then use the general interface spherical_distance(NewFormula, args...; kwargs...)

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
+^ fraction of distance Δy between a-b and c-d.

This interpolation is chosen as by definition of the ring grids, a and b share the same latitude, so do c and d, but the longitudes can be different for all four, a, b, c, d.

Function index

Core.TypeMethod

() Initialize an instance of the grid from an Array. For keyword argument input_as=Vector (default) the leading dimension is interpreted as a flat vector of all horizontal entries in one layer. For input_as==Matrx the first two leading dimensions are interpreted as longitute and latitude. This is only possible for full grids that are a subtype of AbstractFullGridArray.

source
SpeedyWeather.RingGrids.AbstractFullGridType

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 latitudes (also called Clenshaw from Clenshaw-Curtis quadrature), or others.

source
SpeedyWeather.RingGrids.AbstractFullGridArrayType

Subtype of AbstractGridArray for all N-dimensional arrays of ring grids that have the same number of longitude points on every ring. As such these (horizontal) grids are representable as a matrix, with denser grid points towards the poles.

source
SpeedyWeather.RingGrids.AbstractGridType

Abstract supertype for all ring grids, representing 2-dimensional data on the sphere unravelled into a Julia Vector. Subtype of AbstractGridArray with N=1 and ArrayType=Vector{T} of eltype T.

source
SpeedyWeather.RingGrids.AbstractGridArrayType

Abstract supertype for all arrays of ring grids, representing N-dimensional data on the sphere in two dimensions (but unravelled into a vector in the first dimension, the actual "ring grid") plus additional N-1 dimensions for the vertical and/or time etc. Parameter T is the eltype of the underlying data, held as in the array type ArrayType (Julia's Array for CPU or others for GPU).

Ring grids have several consecuitive grid points share the same latitude (= a ring), grid points on a given ring are equidistant. Grid points are ordered 0 to 360˚E, starting around the north pole, ring by ring to the south pole.

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

Subtype of AbstractGridArray for arrays of rings grids that have a reduced number of longitude points towards the poles, i.e. they are not "full", see AbstractFullGridArray. Data on these grids cannot be represented as matrix and has to be unravelled into a vector, ordered 0 to 360˚E then north to south, ring by ring. Examples for reduced grids are the octahedral Gaussian or Clenshaw grids, or the HEALPix grid.

source
SpeedyWeather.RingGrids.AbstractSphericalDistanceType
abstract type AbstractSphericalDistance end

Super type of formulas to calculate the spherical distance or great-circle distance. To define a NewFormula, define struct NewFormula <: AbstractSphericalDistance end and the actual calculation as a functor

function NewFormula(lonlat1::Tuple, lonlat2::Tuple; radius=DEFAULT_RADIUS, kwargs...)

assuming inputs in degrees and returning the distance in meters (or radians for radius=1). Then use the general interface spherical_distance(NewFormula, args...; kwargs...)

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

A FullClenshawArray is an array of full grid, subtyping AbstractFullGridArray, that use equidistant latitudes for each ring (a regular lon-lat grid). These require the Clenshaw-Curtis quadrature in the spectral transform, hence the name. One ring is on the equator, total number of rings is odd, no rings on the north or south pole. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings. Fields are

  • data::AbstractArray

  • nlat_half::Int64

  • rings::Vector{UnitRange{Int64}}

source
SpeedyWeather.RingGrids.FullGaussianArrayType

A FullGaussianArray is an array of full grids, subtyping AbstractFullGridArray, that use Gaussian latitudes for each ring. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings. Fields are

  • data::AbstractArray: Data array, west to east, ring by ring, north to south.

  • nlat_half::Int64: Number of latitudes on one hemisphere

  • rings::Vector{UnitRange{Int64}}: Precomputed ring indices, ranging from first to last grid point on every ring.

source
SpeedyWeather.RingGrids.FullHEALPixArrayType

A FullHEALPixArray is an array of full grids, subtyping AbstractFullGridArray, that use HEALPix latitudes for each ring. This type primarily equists to interpolate data from the (reduced) HEALPixGrid onto a full grid for output.

First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings. Fields are

  • data::AbstractArray

  • nlat_half::Int64

  • rings::Vector{UnitRange{Int64}}

source
SpeedyWeather.RingGrids.FullOctaHEALPixArrayType

A FullOctaHEALPixArray is an array of full grids, subtyping AbstractFullGridArray that use OctaHEALPix latitudes for each ring. This type primarily equists to interpolate data from the (reduced) OctaHEALPixGrid onto a full grid for output.

First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings. Fields are

  • data::AbstractArray

  • nlat_half::Int64

  • rings::Vector{UnitRange{Int64}}

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

A HEALPixArray is an array of HEALPix grids, subtyping AbstractReducedGridArray. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) which has to be even (non-fatal error thrown otherwise) which is less strict than the original HEALPix formulation (only powers of two for nside = nlat_half/2). Ring indices are precomputed in rings.

A HEALPix grid has 12 faces, each nsidexnside grid points, each covering the same area of the sphere. They start with 4 longitude points on the northern-most ring, increase by 4 points per ring in the "polar cap" (the top half of the 4 northern-most faces) but have a constant number of longitude points in the equatorial belt. The southern hemisphere is symmetric to the northern, mirrored around the Equator. HEALPix grids have a ring on the Equator. For more details see Górski et al. 2005, DOI:10.1086/427976.

rings are the precomputed ring indices, for nlat_half = 4 it is rings = [1:4, 5:12, 13:20, 21:28, 29:36, 37:44, 45:48]. So the first ring has indices 1:4 in the unravelled first dimension, etc. For efficient looping see eachring and eachgrid. Fields are

  • data::AbstractArray

  • nlat_half::Int64

  • rings::Vector{UnitRange{Int64}}

source
SpeedyWeather.RingGrids.HaversineMethod
Haversine(lonlat1::Tuple, lonlat2::Tuple; radius) -> Any
-

Haversine formula calculating the great-circle or spherical distance (in meters) on the sphere between two tuples of longitude-latitude points in degrees ˚E, ˚N. Use keyword argument radius to change the radius of the sphere (default 6371e3 meters, Earth's radius), use radius=1 to return the central angle in radians or radius=360/2π to return degrees.

source
SpeedyWeather.RingGrids.OctaHEALPixArrayType

An OctaHEALPixArray is an array of OctaHEALPix grids, subtyping AbstractReducedGridArray. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings.

An OctaHEALPix grid has 4 faces, each nlat_half x nlat_half in size, covering 90˚ in longitude, pole to pole. As part of the HEALPix family of grids, the grid points are equal area. They start with 4 longitude points on the northern-most ring, increase by 4 points per ring towards the Equator with one ring on the Equator before reducing the number of points again towards the south pole by 4 per ring. There is no equatorial belt for OctaHEALPix grids. The southern hemisphere is symmetric to the northern, mirrored around the Equator. OctaHEALPix grids have a ring on the Equator. For more details see Górski et al. 2005, DOI:10.1086/427976, the OctaHEALPix grid belongs to the family of HEALPix grids with Nθ = 1, Nφ = 4 but is not explicitly mentioned therein.

rings are the precomputed ring indices, for nlat_half = 3 (in contrast to HEALPix this can be odd) it is rings = [1:4, 5:12, 13:24, 25:32, 33:36]. For efficient looping see eachring and eachgrid. Fields are

  • data::AbstractArray

  • nlat_half::Int64

  • rings::Vector{UnitRange{Int64}}

source
SpeedyWeather.RingGrids.OctahedralClenshawArrayType

An OctahedralClenshawArray is an array of octahedral grids, subtyping AbstractReducedGridArray, that use equidistant latitudes for each ring, the same as for FullClenshawArray. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings.

These grids are called octahedral (same as for the OctahedralGaussianArray which only uses different latitudes) because after starting with 20 points on the first ring around the north pole (default) they increase the number of longitude points for each ring by 4, such that they can be conceptually thought of as lying on the 4 faces of an octahedron on each hemisphere. Hence, these grids have 20, 24, 28, ... longitude points for ring 1, 2, 3, ... Clenshaw grids have a ring on the Equator which has 16 + 4nlat_half longitude points before reducing the number of longitude points per ring by 4 towards the southern-most ring j = nlat. rings are the precomputed ring indices, the the example above rings = [1:20, 21:44, 45:72, ...]. For efficient looping see eachring and eachgrid. Fields are

  • data::AbstractArray

  • nlat_half::Int64

  • rings::Vector{UnitRange{Int64}}

source
SpeedyWeather.RingGrids.OctahedralGaussianArrayType

An OctahedralGaussianArray is an array of octahedral grids, subtyping AbstractReducedGridArray, that use Gaussian latitudes for each ring. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings.

These grids are called octahedral because after starting with 20 points on the first ring around the north pole (default) they increase the number of longitude points for each ring by 4, such that they can be conceptually thought of as lying on the 4 faces of an octahedron on each hemisphere. Hence, these grids have 20, 24, 28, ... longitude points for ring 1, 2, 3, ... There is no ring on the Equator and the two rings around it have 16 + 4nlat_half longitude points before reducing the number of longitude points per ring by 4 towards the southern-most ring j = nlat. rings are the precomputed ring indices, the the example above rings = [1:20, 21:44, 45:72, ...]. For efficient looping see eachring and eachgrid. Fields are

  • data::AbstractArray

  • nlat_half::Int64

  • rings::Vector{UnitRange{Int64}}

source
SpeedyWeather.RingGrids.OctaminimalGaussianArrayType

An OctaminimalGaussianArray is an array of octahedral grids, subtyping AbstractReducedGridArray, that use Gaussian latitudes for each ring. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings.

These grids are called octahedral because after starting with 4 points on the first ring around the north pole (default) they increase the number of longitude points for each ring by 4, such that they can be conceptually thought of as lying on the 4 faces of an octahedron on each hemisphere. Hence, these grids have 4, 8, 12, ... longitude points for ring 1, 2, 3, ... which is in contrast to the OctahedralGaussianArray which starts with 20 points around the poles, hence "minimal". There is no ring on the Equator and the two rings around it have 4nlat_half longitude points before reducing the number of longitude points per ring by 4 towards the southern-most ring j = nlat. rings are the precomputed ring indices, in the example above rings = [1:4, 5:12, 13:24, ...]. For efficient looping see eachring and eachgrid. Fields are

  • data::AbstractArray

  • nlat_half::Int64

  • rings::Vector{UnitRange{Int64}}

source
Base.sizeofMethod
sizeof(G::AbstractGridArray) -> Any
-

Size of underlying data array plus precomputed ring indices.

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

A FullClenshawArray is an array of full grid, subtyping AbstractFullGridArray, that use equidistant latitudes for each ring (a regular lon-lat grid). These require the Clenshaw-Curtis quadrature in the spectral transform, hence the name. One ring is on the equator, total number of rings is odd, no rings on the north or south pole. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings. Fields are

  • data::AbstractArray

  • nlat_half::Int64

  • rings::Vector{UnitRange{Int64}}

source
SpeedyWeather.RingGrids.FullGaussianArrayType

A FullGaussianArray is an array of full grids, subtyping AbstractFullGridArray, that use Gaussian latitudes for each ring. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings. Fields are

  • data::AbstractArray: Data array, west to east, ring by ring, north to south.

  • nlat_half::Int64: Number of latitudes on one hemisphere

  • rings::Vector{UnitRange{Int64}}: Precomputed ring indices, ranging from first to last grid point on every ring.

source
SpeedyWeather.RingGrids.FullHEALPixArrayType

A FullHEALPixArray is an array of full grids, subtyping AbstractFullGridArray, that use HEALPix latitudes for each ring. This type primarily equists to interpolate data from the (reduced) HEALPixGrid onto a full grid for output.

First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings. Fields are

  • data::AbstractArray

  • nlat_half::Int64

  • rings::Vector{UnitRange{Int64}}

source
SpeedyWeather.RingGrids.FullOctaHEALPixArrayType

A FullOctaHEALPixArray is an array of full grids, subtyping AbstractFullGridArray that use OctaHEALPix latitudes for each ring. This type primarily equists to interpolate data from the (reduced) OctaHEALPixGrid onto a full grid for output.

First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings. Fields are

  • data::AbstractArray

  • nlat_half::Int64

  • rings::Vector{UnitRange{Int64}}

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

A HEALPixArray is an array of HEALPix grids, subtyping AbstractReducedGridArray. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) which has to be even (non-fatal error thrown otherwise) which is less strict than the original HEALPix formulation (only powers of two for nside = nlat_half/2). Ring indices are precomputed in rings.

A HEALPix grid has 12 faces, each nsidexnside grid points, each covering the same area of the sphere. They start with 4 longitude points on the northern-most ring, increase by 4 points per ring in the "polar cap" (the top half of the 4 northern-most faces) but have a constant number of longitude points in the equatorial belt. The southern hemisphere is symmetric to the northern, mirrored around the Equator. HEALPix grids have a ring on the Equator. For more details see Górski et al. 2005, DOI:10.1086/427976.

rings are the precomputed ring indices, for nlat_half = 4 it is rings = [1:4, 5:12, 13:20, 21:28, 29:36, 37:44, 45:48]. So the first ring has indices 1:4 in the unravelled first dimension, etc. For efficient looping see eachring and eachgrid. Fields are

  • data::AbstractArray

  • nlat_half::Int64

  • rings::Vector{UnitRange{Int64}}

source
SpeedyWeather.RingGrids.HaversineMethod
Haversine(lonlat1::Tuple, lonlat2::Tuple; radius) -> Any
+

Haversine formula calculating the great-circle or spherical distance (in meters) on the sphere between two tuples of longitude-latitude points in degrees ˚E, ˚N. Use keyword argument radius to change the radius of the sphere (default 6371e3 meters, Earth's radius), use radius=1 to return the central angle in radians or radius=360/2π to return degrees.

source
SpeedyWeather.RingGrids.OctaHEALPixArrayType

An OctaHEALPixArray is an array of OctaHEALPix grids, subtyping AbstractReducedGridArray. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings.

An OctaHEALPix grid has 4 faces, each nlat_half x nlat_half in size, covering 90˚ in longitude, pole to pole. As part of the HEALPix family of grids, the grid points are equal area. They start with 4 longitude points on the northern-most ring, increase by 4 points per ring towards the Equator with one ring on the Equator before reducing the number of points again towards the south pole by 4 per ring. There is no equatorial belt for OctaHEALPix grids. The southern hemisphere is symmetric to the northern, mirrored around the Equator. OctaHEALPix grids have a ring on the Equator. For more details see Górski et al. 2005, DOI:10.1086/427976, the OctaHEALPix grid belongs to the family of HEALPix grids with Nθ = 1, Nφ = 4 but is not explicitly mentioned therein.

rings are the precomputed ring indices, for nlat_half = 3 (in contrast to HEALPix this can be odd) it is rings = [1:4, 5:12, 13:24, 25:32, 33:36]. For efficient looping see eachring and eachgrid. Fields are

  • data::AbstractArray

  • nlat_half::Int64

  • rings::Vector{UnitRange{Int64}}

source
SpeedyWeather.RingGrids.OctahedralClenshawArrayType

An OctahedralClenshawArray is an array of octahedral grids, subtyping AbstractReducedGridArray, that use equidistant latitudes for each ring, the same as for FullClenshawArray. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings.

These grids are called octahedral (same as for the OctahedralGaussianArray which only uses different latitudes) because after starting with 20 points on the first ring around the north pole (default) they increase the number of longitude points for each ring by 4, such that they can be conceptually thought of as lying on the 4 faces of an octahedron on each hemisphere. Hence, these grids have 20, 24, 28, ... longitude points for ring 1, 2, 3, ... Clenshaw grids have a ring on the Equator which has 16 + 4nlat_half longitude points before reducing the number of longitude points per ring by 4 towards the southern-most ring j = nlat. rings are the precomputed ring indices, the the example above rings = [1:20, 21:44, 45:72, ...]. For efficient looping see eachring and eachgrid. Fields are

  • data::AbstractArray

  • nlat_half::Int64

  • rings::Vector{UnitRange{Int64}}

source
SpeedyWeather.RingGrids.OctahedralGaussianArrayType

An OctahedralGaussianArray is an array of octahedral grids, subtyping AbstractReducedGridArray, that use Gaussian latitudes for each ring. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings.

These grids are called octahedral because after starting with 20 points on the first ring around the north pole (default) they increase the number of longitude points for each ring by 4, such that they can be conceptually thought of as lying on the 4 faces of an octahedron on each hemisphere. Hence, these grids have 20, 24, 28, ... longitude points for ring 1, 2, 3, ... There is no ring on the Equator and the two rings around it have 16 + 4nlat_half longitude points before reducing the number of longitude points per ring by 4 towards the southern-most ring j = nlat. rings are the precomputed ring indices, the the example above rings = [1:20, 21:44, 45:72, ...]. For efficient looping see eachring and eachgrid. Fields are

  • data::AbstractArray

  • nlat_half::Int64

  • rings::Vector{UnitRange{Int64}}

source
SpeedyWeather.RingGrids.OctaminimalGaussianArrayType

An OctaminimalGaussianArray is an array of octahedral grids, subtyping AbstractReducedGridArray, that use Gaussian latitudes for each ring. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings.

These grids are called octahedral because after starting with 4 points on the first ring around the north pole (default) they increase the number of longitude points for each ring by 4, such that they can be conceptually thought of as lying on the 4 faces of an octahedron on each hemisphere. Hence, these grids have 4, 8, 12, ... longitude points for ring 1, 2, 3, ... which is in contrast to the OctahedralGaussianArray which starts with 20 points around the poles, hence "minimal". There is no ring on the Equator and the two rings around it have 4nlat_half longitude points before reducing the number of longitude points per ring by 4 towards the southern-most ring j = nlat. rings are the precomputed ring indices, in the example above rings = [1:4, 5:12, 13:24, ...]. For efficient looping see eachring and eachgrid. Fields are

  • data::AbstractArray

  • nlat_half::Int64

  • rings::Vector{UnitRange{Int64}}

source
Base.sizeofMethod
sizeof(G::AbstractGridArray) -> Any
+

Size of underlying data array plus precomputed ring indices.

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!(
+        )

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;
     kwargs...
 ) -> 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!(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!(
+

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{AbstractArray{T, 2}, OctahedralClenshawGrid}...;
     quadrant_rotation,
     matrix_quadrant
 ) -> Union{Tuple, AbstractMatrix}
-

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!(
+

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!(
     grid::AbstractGridArray{T, N, ArrayType} where {N, ArrayType<:AbstractArray{T, N}},
     v::AbstractVector
 ) -> AbstractGridArray
-

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   >|
 
@@ -317,88 +317,88 @@
     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::AbstractGridArray{NF<:Integer, 1, Array{NF<:Integer, 1}},
     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.average_on_polesMethod
average_on_poles(
     A::AbstractArray{NF<:AbstractFloat, 1},
     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.clenshaw_curtis_weightsMethod
clenshaw_curtis_weights(nlat_half::Integer) -> Any
-

The Clenshaw-Curtis weights for a Clenshaw grid (full or octahedral) of size nlathalf. Clenshaw-Curtis weights are of length nlat, i.e. a vector for every latitude ring, pole to pole. `sum(clenshawcurtisweights(nlathalf))is always2` as int0^π sin(x) dx = 2 (colatitudes), or equivalently int-pi/2^pi/2 cos(x) dx (latitudes).

Integration (and therefore the spectral transform) is exact (only rounding errors) when using Clenshaw grids provided that nlat >= 2(T + 1), meaning that a grid resolution of at least 128x64 (nlon x nlat) is sufficient for an exact transform with a T=31 spectral truncation.

source
SpeedyWeather.RingGrids.each_index_in_ring!Method
each_index_in_ring!(
+

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.clenshaw_curtis_weightsMethod
clenshaw_curtis_weights(nlat_half::Integer) -> Any
+

The Clenshaw-Curtis weights for a Clenshaw grid (full or octahedral) of size nlathalf. Clenshaw-Curtis weights are of length nlat, i.e. a vector for every latitude ring, pole to pole. `sum(clenshawcurtisweights(nlathalf))is always2` as int0^π sin(x) dx = 2 (colatitudes), or equivalently int-pi/2^pi/2 cos(x) dx (latitudes).

Integration (and therefore the spectral transform) is exact (only rounding errors) when using Clenshaw grids provided that nlat >= 2(T + 1), meaning that a grid resolution of at least 128x64 (nlon x nlat) is sufficient for an exact transform with a T=31 spectral truncation.

source
SpeedyWeather.RingGrids.each_index_in_ring!Method
each_index_in_ring!(
     rings::Vector{<:UnitRange{<:Integer}},
     Grid::Type{<:OctahedralGaussianArray},
     nlat_half::Integer
 )
-

precompute a Vector{UnitRange{Int}} to index grid points on every ringj(elements of the vector) ofGridat resolutionnlat_half. Seeeachringandeachgrid` for efficient looping over grid points.

source
SpeedyWeather.RingGrids.each_index_in_ring!Method
each_index_in_ring!(
+

precompute a Vector{UnitRange{Int}} to index grid points on every ringj(elements of the vector) ofGridat resolutionnlat_half. Seeeachringandeachgrid` for efficient looping over grid points.

source
SpeedyWeather.RingGrids.each_index_in_ring!Method
each_index_in_ring!(
     rings::Vector{<:UnitRange{<:Integer}},
     Grid::Type{<:OctaminimalGaussianArray},
     nlat_half::Integer
 )
-

precompute a Vector{UnitRange{Int}} to index grid points on every ringj(elements of the vector) ofGridat resolutionnlat_half. Seeeachringandeachgrid` for efficient looping over grid points.

source
SpeedyWeather.RingGrids.each_index_in_ringMethod
each_index_in_ring(
+

precompute a Vector{UnitRange{Int}} to index grid points on every ringj(elements of the vector) ofGridat resolutionnlat_half. Seeeachringandeachgrid` for efficient looping over grid points.

source
SpeedyWeather.RingGrids.each_index_in_ringMethod
each_index_in_ring(
     Grid::Type{<:SpeedyWeather.RingGrids.AbstractFullGridArray},
     j::Integer,
     nlat_half::Integer
 ) -> Any
-

UnitRange for every grid point of grid Grid of resolution nlat_half on ring j (j=1 is closest ring around north pole, j=nlat around south pole).

source
SpeedyWeather.RingGrids.eachgridMethod
eachgrid(grid::AbstractGridArray) -> Any
-

CartesianIndices for the 2nd to last dimension of an AbstractGridArray, to be used like

for k in eachgrid(grid) for ring in eachring(grid) for ij in ring grid[ij, k]

source
SpeedyWeather.RingGrids.eachgridpointMethod
eachgridpoint(grid::AbstractGridArray) -> Base.OneTo
-

UnitRange to access each horizontal grid point on grid grid. For a NxM (N horizontal grid points, M vertical layers) OneTo(N) is returned.

source
SpeedyWeather.RingGrids.eachgridMethod
eachgrid(grid::AbstractGridArray) -> Any
+

CartesianIndices for the 2nd to last dimension of an AbstractGridArray, to be used like

for k in eachgrid(grid) for ring in eachring(grid) for ij in ring grid[ij, k]

source
SpeedyWeather.RingGrids.eachgridpointMethod
eachgridpoint(grid::AbstractGridArray) -> Base.OneTo
+

UnitRange to access each horizontal grid point on grid grid. For a NxM (N horizontal grid points, M vertical layers) OneTo(N) is returned.

source
SpeedyWeather.RingGrids.eachgridpointMethod
eachgridpoint(
     grid1::AbstractGridArray,
     grids::Grid<:AbstractGridArray...
 ) -> Base.OneTo
-

Like eachgridpoint(::AbstractGridArray) but checks for equal size between input arguments first.

source
SpeedyWeather.RingGrids.eachringMethod
eachring(
     grid1::AbstractGridArray,
     grids::AbstractGridArray...
 ) -> Any
-

Same as eachring(grid) but performs a bounds check to assess that all grids according to grids_match (non-parametric grid type, nlat_half and length).

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

Same as eachring(grid) but performs a bounds check to assess that all grids according to grids_match (non-parametric grid type, nlat_half and length).

source
SpeedyWeather.RingGrids.eachringMethod
eachring(grid::AbstractGridArray) -> 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]

Accesses precomputed grid.rings.

source
SpeedyWeather.RingGrids.eachringMethod
eachring(
     Grid::Type{<:AbstractGridArray},
     nlat_half::Integer
 ) -> Any
-

Computes the ring indices i0:i1 for start and end of every longitudinal point on a given ring j of Grid at resolution nlat_half. Used to loop over rings of a grid. These indices are also precomputed in every grid.rings.

source
SpeedyWeather.RingGrids.equal_area_weightsMethod
equal_area_weights(
+

Computes the ring indices i0:i1 for start and end of every longitudinal point on a given ring j of Grid at resolution nlat_half. Used to loop over rings of a grid. These indices are also precomputed in every grid.rings.

source
SpeedyWeather.RingGrids.equal_area_weightsMethod
equal_area_weights(
     Grid::Type{<:AbstractGridArray},
     nlat_half::Integer
 ) -> Any
-

The equal-area weights used for the HEALPix grids (original or OctaHEALPix) of size nlathalf. The weights are of length nlat, i.e. a vector for every latitude ring, pole to pole. `sum(equalareaweights(nlathalf))is always2` as int0^π sin(x) dx = 2 (colatitudes), or equivalently int-pi/2^pi/2 cos(x) dx (latitudes). Integration (and therefore the spectral transform) is not exact with these grids but errors reduce for higher resolution.

source
SpeedyWeather.RingGrids.full_array_typeMethod
full_array_type(grid::AbstractGridArray) -> Any
-

Full grid array type for grid. Always returns the N-dimensional *Array not the two-dimensional (N=1) *Grid. For reduced grids the corresponding full grid that share the same latitudes.

source
SpeedyWeather.RingGrids.full_grid_typeMethod
full_grid_type(grid::AbstractGridArray) -> Any
-

Full (horizontal) grid type for grid. Always returns the two-dimensional (N=1) *Grid type. For reduced grids the corresponding full grid that share the same latitudes.

source
SpeedyWeather.RingGrids.gaussian_weightsMethod
gaussian_weights(nlat_half::Integer) -> Any
-

The Gaussian weights for a Gaussian grid (full or octahedral) of size nlathalf. Gaussian weights are of length nlat, i.e. a vector for every latitude ring, pole to pole. `sum(gaussianweights(nlathalf))is always2` as int0^π sin(x) dx = 2 (colatitudes), or equivalently int_-pi/2^pi/2 cos(x) dx (latitudes).

Integration (and therefore the spectral transform) is exact (only rounding errors) when using Gaussian grids provided that nlat >= 3(T + 1)/2, meaning that a grid resolution of at least 96x48 (nlon x nlat) is sufficient for an exact transform with a T=31 spectral truncation.

source
SpeedyWeather.RingGrids.get_latdlondsMethod
get_latdlonds(grid::AbstractGridArray) -> Tuple{Any, Any}
-

Latitudes (in degrees, -90˚-90˚N) and longitudes (0-360˚E) for every (horizontal) grid point in grid. Ordered 0-360˚E then north to south.

source
SpeedyWeather.RingGrids.get_latlonsMethod
get_latlons(grid::AbstractGridArray) -> Tuple{Any, Any}
-

Latitudes (in radians, 0-2π) and longitudes (-π/2 - π/2) for every (horizontal) grid point in grid.

source
SpeedyWeather.RingGrids.get_nlon_per_ringMethod
get_nlon_per_ring(
+

The equal-area weights used for the HEALPix grids (original or OctaHEALPix) of size nlathalf. The weights are of length nlat, i.e. a vector for every latitude ring, pole to pole. `sum(equalareaweights(nlathalf))is always2` as int0^π sin(x) dx = 2 (colatitudes), or equivalently int-pi/2^pi/2 cos(x) dx (latitudes). Integration (and therefore the spectral transform) is not exact with these grids but errors reduce for higher resolution.

source
SpeedyWeather.RingGrids.full_array_typeMethod
full_array_type(grid::AbstractGridArray) -> Any
+

Full grid array type for grid. Always returns the N-dimensional *Array not the two-dimensional (N=1) *Grid. For reduced grids the corresponding full grid that share the same latitudes.

source
SpeedyWeather.RingGrids.full_grid_typeMethod
full_grid_type(grid::AbstractGridArray) -> Any
+

Full (horizontal) grid type for grid. Always returns the two-dimensional (N=1) *Grid type. For reduced grids the corresponding full grid that share the same latitudes.

source
SpeedyWeather.RingGrids.gaussian_weightsMethod
gaussian_weights(nlat_half::Integer) -> Any
+

The Gaussian weights for a Gaussian grid (full or octahedral) of size nlathalf. Gaussian weights are of length nlat, i.e. a vector for every latitude ring, pole to pole. `sum(gaussianweights(nlathalf))is always2` as int0^π sin(x) dx = 2 (colatitudes), or equivalently int_-pi/2^pi/2 cos(x) dx (latitudes).

Integration (and therefore the spectral transform) is exact (only rounding errors) when using Gaussian grids provided that nlat >= 3(T + 1)/2, meaning that a grid resolution of at least 96x48 (nlon x nlat) is sufficient for an exact transform with a T=31 spectral truncation.

source
SpeedyWeather.RingGrids.get_latdlondsMethod
get_latdlonds(grid::AbstractGridArray) -> Tuple{Any, Any}
+

Latitudes (in degrees, -90˚-90˚N) and longitudes (0-360˚E) for every (horizontal) grid point in grid. Ordered 0-360˚E then north to south.

source
SpeedyWeather.RingGrids.get_latlonsMethod
get_latlons(grid::AbstractGridArray) -> Tuple{Any, Any}
+

Latitudes (in radians, 0-2π) and longitudes (-π/2 - π/2) for every (horizontal) grid point in grid.

source
SpeedyWeather.RingGrids.get_nlonsMethod
get_nlons(
     Grid::Type{<:AbstractGridArray},
     nlat_half::Integer;
     both_hemispheres
 ) -> Any
-

Returns a vector nlons for the number of longitude points per latitude ring, north to south. Provide grid Grid and its resolution parameter nlat_half. For keyword argument both_hemispheres=false only the northern hemisphere (incl Equator) is returned.

source
SpeedyWeather.RingGrids.get_verticesMethod
get_vertices(
+

Returns a vector nlons for the number of longitude points per latitude ring, north to south. Provide grid Grid and its resolution parameter nlat_half. For keyword argument both_hemispheres=false only the northern hemisphere (incl Equator) is returned.

source
SpeedyWeather.RingGrids.get_verticesMethod
get_vertices(
     Grid::Type{<:AbstractGridArray},
     nlat_half::Integer
 ) -> NTuple{4, Any}
@@ -409,68 +409,68 @@
 
 o --- w --- c --- e --- o
 
-     o ----- s ------ o

with cell center c (the grid point), e, s, w, n the vertices and o the surrounding grid points. Returns 2xnpoints arrays for east, south, west, north each containing the longitude and latitude of the vertices.

source
SpeedyWeather.RingGrids.get_verticesMethod
get_vertices(
+     o ----- s ------ o

with cell center c (the grid point), e, s, w, n the vertices and o the surrounding grid points. Returns 2xnpoints arrays for east, south, west, north each containing the longitude and latitude of the vertices.

source
SpeedyWeather.RingGrids.get_verticesMethod
get_vertices(
     Grid::Type{<:SpeedyWeather.RingGrids.AbstractFullGridArray},
     nlat_half::Integer
 ) -> NTuple{4, Any}
-

Vertices for full grids, other definition than for reduced grids to prevent a diamond shape of the cells. Use default rectangular instead.

source
SpeedyWeather.RingGrids.grid_cell_average!Method
grid_cell_average!(
     output::AbstractGrid,
     input::SpeedyWeather.RingGrids.AbstractFullGrid
 ) -> 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{<: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.grids_matchMethod
grids_match(
+

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.grids_matchMethod
grids_match(
     A::AbstractGridArray,
     B::AbstractGridArray;
     horizontal_only,
     vertical_only
 ) -> Any
-

True if both A and B are of the same nonparametric grid type (e.g. OctahedralGaussianArray, regardless type parameter T or underyling array type ArrayType) and of same resolution (nlat_half) and total grid points (length). Sizes of (4,) and (4,1) would match for example, but (8,1) and (4,2) would not (nlat_half not identical).

source
SpeedyWeather.RingGrids.grids_matchMethod
grids_match(
+

True if both A and B are of the same nonparametric grid type (e.g. OctahedralGaussianArray, regardless type parameter T or underyling array type ArrayType) and of same resolution (nlat_half) and total grid points (length). Sizes of (4,) and (4,1) would match for example, but (8,1) and (4,2) would not (nlat_half not identical).

source
SpeedyWeather.RingGrids.grids_matchMethod
grids_match(
     A::AbstractGridArray,
     B::AbstractGridArray...;
     kwargs...
 ) -> Any
-

True if all grids A, B, C, ... provided as arguments match according to grids_match wrt to A (and therefore all).

source
SpeedyWeather.RingGrids.nonparametric_typeMethod
nonparametric_type(grid::AbstractGridArray) -> Any
-

For any instance of AbstractGridArray type its n-dimensional type (*Grid{T, N, ...} returns *Array) but without any parameters {T, N, ArrayType}

source
SpeedyWeather.RingGrids.nonparametric_typeMethod
nonparametric_type(grid::AbstractGridArray) -> Any
+

For any instance of AbstractGridArray type its n-dimensional type (*Grid{T, N, ...} returns *Array) but without any parameters {T, N, ArrayType}

source
SpeedyWeather.RingGrids.npoints_added_per_ringMethod
npoints_added_per_ring(
     _::Type{<:OctahedralGaussianArray}
 ) -> Int64
-

[EVEN MORE EXPERIMENTAL] number of longitude points added (removed) for every ring towards the Equator (on the southern hemisphere towards the south pole).

source
SpeedyWeather.RingGrids.npoints_added_per_ringMethod
npoints_added_per_ring(
     _::Type{<:OctaminimalGaussianArray}
 ) -> Int64
-

[EVEN MORE EXPERIMENTAL] number of longitude points added (removed) for every ring towards the Equator (on the southern hemisphere towards the south pole).

source
SpeedyWeather.RingGrids.npoints_poleMethod
npoints_pole(_::Type{<:OctahedralGaussianArray}) -> Int64
-

[EXPERIMENTAL] additional number of longitude points on the first and last ring. Change to 0 to start with 4 points on the first ring.

source
SpeedyWeather.RingGrids.npoints_poleMethod
npoints_pole(_::Type{<:OctaminimalGaussianArray}) -> Int64
-

[EXPERIMENTAL] additional number of longitude points on the first and last ring. Change to 0 to start with 4 points on the first ring.

source
SpeedyWeather.RingGrids.nside_healpixMethod
nside_healpix(nlat_half::Integer) -> Any
-

The original Nside resolution parameter of the HEALPix grids. The number of grid points on one side of each (square) face. While we use nlat_half across all ring grids, this function translates this to Nside. Even nlat_half only.

source
SpeedyWeather.RingGrids.npoints_poleMethod
npoints_pole(_::Type{<:OctahedralGaussianArray}) -> Int64
+

[EXPERIMENTAL] additional number of longitude points on the first and last ring. Change to 0 to start with 4 points on the first ring.

source
SpeedyWeather.RingGrids.npoints_poleMethod
npoints_pole(_::Type{<:OctaminimalGaussianArray}) -> Int64
+

[EXPERIMENTAL] additional number of longitude points on the first and last ring. Change to 0 to start with 4 points on the first ring.

source
SpeedyWeather.RingGrids.nside_healpixMethod
nside_healpix(nlat_half::Integer) -> Any
+

The original Nside resolution parameter of the HEALPix grids. The number of grid points on one side of each (square) face. While we use nlat_half across all ring grids, this function translates this to Nside. Even nlat_half only.

source
SpeedyWeather.RingGrids.spherical_distanceMethod
spherical_distance(
     Formula::Type{<:SpeedyWeather.RingGrids.AbstractSphericalDistance},
     args...;
     kwargs...
 ) -> Any
-

Spherical distance, or great-circle distance, between two points lonlat1 and lonlat2 using the Formula (default Haversine).

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

Obtain ring index j from gridpoint ij and rings describing rind indices as obtained from eachring(::Grid)

source
+

Obtain ring index j from gridpoint ij and rings describing rind indices as obtained from eachring(::Grid)

source
SpeedyWeather.RingGrids.zonal_meanMethod
zonal_mean(grid::AbstractGridArray) -> Any
+

Zonal mean of grid, i.e. along its latitude rings.

source
diff --git a/previews/PR645/run_0001/parameters.txt b/previews/PR645/run_0001/parameters.txt index aea22c47d..12a033975 100644 --- a/previews/PR645/run_0001/parameters.txt +++ b/previews/PR645/run_0001/parameters.txt @@ -316,7 +316,7 @@ Feedback <: AbstractFeedback ├ start::Int64 = 0 ├ barlen::Nothing = nothing ├ barglyphs::ProgressMeter.BarGlyphs = ProgressMeter.BarGlyphs('|', '█', ['▏', '▎', '▍', '▌', '▋', '▊', '▉'], ' ', '|') -└ core::ProgressMeter.ProgressCore = ProgressMeter.ProgressCore(:green, "Weather is speedy: run 0001 ", 0.1, false, 0, IOContext(Base.PipeEndpoint(RawFD(-1) closed, 0 bytes waiting)), true, 1, 0, ReentrantLock(nothing, 0x00000000, 0x00, Base.GenericCondition{Base.Threads.SpinLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), (0, 0, 3039)), 0, 1, false, false, 1.734444152654275e9, 1.734444152654275e9, 1.734444152654275e9) +└ core::ProgressMeter.ProgressCore = ProgressMeter.ProgressCore(:green, "Weather is speedy: run 0001 ", 0.1, false, 0, IOContext(Base.PipeEndpoint(RawFD(-1) closed, 0 bytes waiting)), true, 1, 0, ReentrantLock(nothing, 0x00000000, 0x00, Base.GenericCondition{Base.Threads.SpinLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), (1, 80, 5)), 0, 1, false, false, 1.734444558010813e9, 1.734444558010813e9, 1.734444558010813e9) ├ progress_txt::Nothing = nothing └ nars_detected::Bool = false diff --git a/previews/PR645/run_0001/progress.txt b/previews/PR645/run_0001/progress.txt index 5002b6d37..8af0ff860 100644 --- a/previews/PR645/run_0001/progress.txt +++ b/previews/PR645/run_0001/progress.txt @@ -1,4 +1,4 @@ -Starting SpeedyWeather.jl run 0001 on Tue, 17 Dec 2024 14:02:39 +Starting SpeedyWeather.jl run 0001 on Tue, 17 Dec 2024 14:09:25 Integrating: SpectralGrid: ├ Spectral: T31 LowerTriangularMatrix{Complex{Float32}}, radius = 6.371e6 m @@ -22,7 +22,7 @@ All data will be stored in /home/runner/work/SpeedyWeather.jl/SpeedyWeather.jl/d 40%, ETA: 0:00:01, Inf millenia/day 45%, ETA: 0:00:01, Inf millenia/day 50%, ETA: 0:00:01, Inf millenia/day - 55%, ETA: 0:00:01, Inf millenia/day + 55%, ETA: 0:00:00, Inf millenia/day 60%, ETA: 0:00:00, Inf millenia/day 65%, ETA: 0:00:00, Inf millenia/day 70%, ETA: 0:00:00, Inf millenia/day diff --git a/previews/PR645/run_0002/parameters.txt b/previews/PR645/run_0002/parameters.txt index ef24a86f1..9573ae7d9 100644 --- a/previews/PR645/run_0002/parameters.txt +++ b/previews/PR645/run_0002/parameters.txt @@ -155,7 +155,7 @@ Feedback <: AbstractFeedback ├ start::Int64 = 0 ├ barlen::Nothing = nothing ├ barglyphs::ProgressMeter.BarGlyphs = ProgressMeter.BarGlyphs('|', '█', ['▏', '▎', '▍', '▌', '▋', '▊', '▉'], ' ', '|') -└ core::ProgressMeter.ProgressCore = ProgressMeter.ProgressCore(:green, "Weather is speedy: run 0002 ", 0.1, false, 0, IOContext(Base.PipeEndpoint(RawFD(-1) closed, 0 bytes waiting)), true, 1, 431, ReentrantLock(nothing, 0x00000000, 0x00, Base.GenericCondition{Base.Threads.SpinLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), (25, 2520, 1)), 0, 1, false, false, 1.734444169341635e9, 1.734444169341635e9, 1.734444169341635e9) +└ core::ProgressMeter.ProgressCore = ProgressMeter.ProgressCore(:green, "Weather is speedy: run 0002 ", 0.1, false, 0, IOContext(Base.PipeEndpoint(RawFD(-1) closed, 0 bytes waiting)), true, 1, 431, ReentrantLock(nothing, 0x00000000, 0x00, Base.GenericCondition{Base.Threads.SpinLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), (29, 2940, 1)), 0, 1, false, false, 1.734444574990052e9, 1.734444574990052e9, 1.734444574990052e9) ├ progress_txt::Nothing = nothing └ nars_detected::Bool = false diff --git a/previews/PR645/run_0002/progress.txt b/previews/PR645/run_0002/progress.txt index e4ba2797c..c9bbadf17 100644 --- a/previews/PR645/run_0002/progress.txt +++ b/previews/PR645/run_0002/progress.txt @@ -1,4 +1,4 @@ -Starting SpeedyWeather.jl run 0002 on Tue, 17 Dec 2024 14:02:51 +Starting SpeedyWeather.jl run 0002 on Tue, 17 Dec 2024 14:09:37 Integrating: SpectralGrid: ├ Spectral: T63 LowerTriangularMatrix{Complex{Float32}}, radius = 6.371e6 m @@ -17,7 +17,7 @@ All data will be stored in /home/runner/work/SpeedyWeather.jl/SpeedyWeather.jl/d 25%, ETA: 0:00:01, Inf millenia/day 30%, ETA: 0:00:01, Inf millenia/day 35%, ETA: 0:00:01, Inf millenia/day - 40%, ETA: 0:00:01, Inf millenia/day + 40%, ETA: 0:00:00, Inf millenia/day 45%, ETA: 0:00:00, Inf millenia/day 50%, ETA: 0:00:00, Inf millenia/day 55%, ETA: 0:00:00, Inf millenia/day diff --git a/previews/PR645/run_0003/parameters.txt b/previews/PR645/run_0003/parameters.txt index 6b6c7a09b..66676b7c2 100644 --- a/previews/PR645/run_0003/parameters.txt +++ b/previews/PR645/run_0003/parameters.txt @@ -163,7 +163,7 @@ Feedback <: AbstractFeedback ├ start::Int64 = 0 ├ barlen::Nothing = nothing ├ barglyphs::ProgressMeter.BarGlyphs = ProgressMeter.BarGlyphs('|', '█', ['▏', '▎', '▍', '▌', '▋', '▊', '▉'], ' ', '|') -└ core::ProgressMeter.ProgressCore = ProgressMeter.ProgressCore(:green, "Weather is speedy: run 0003 ", 0.1, false, 0, IOContext(Base.PipeEndpoint(RawFD(-1) closed, 0 bytes waiting)), true, 1, 0, ReentrantLock(nothing, 0x00000000, 0x00, Base.GenericCondition{Base.Threads.SpinLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), (0, 0, 432272651661)), 0, 1, false, false, 1.734444173493546e9, 1.734444173493546e9, 1.734444173493546e9) +└ core::ProgressMeter.ProgressCore = ProgressMeter.ProgressCore(:green, "Weather is speedy: run 0003 ", 0.1, false, 0, IOContext(Base.PipeEndpoint(RawFD(-1) closed, 0 bytes waiting)), true, 1, 0, ReentrantLock(nothing, 0x00000000, 0x00, Base.GenericCondition{Base.Threads.SpinLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), (972, 1, 1)), 0, 1, false, false, 1.734444579176389e9, 1.734444579176389e9, 1.73444457917639e9) ├ progress_txt::Nothing = nothing └ nars_detected::Bool = false diff --git a/previews/PR645/run_0003/progress.txt b/previews/PR645/run_0003/progress.txt index 576b68ce6..63b2230d5 100644 --- a/previews/PR645/run_0003/progress.txt +++ b/previews/PR645/run_0003/progress.txt @@ -1,4 +1,4 @@ -Starting SpeedyWeather.jl run 0003 on Tue, 17 Dec 2024 14:02:54 +Starting SpeedyWeather.jl run 0003 on Tue, 17 Dec 2024 14:09:40 Integrating: SpectralGrid: ├ Spectral: T63 LowerTriangularMatrix{Complex{Float32}}, radius = 6.371e6 m @@ -11,7 +11,7 @@ Time: 12.0 days at Δt = 1200.0s All data will be stored in /home/runner/work/SpeedyWeather.jl/SpeedyWeather.jl/docs/build/run_0003 5%, ETA: 0:00:01, Inf millenia/day - 10%, ETA: 0:00:01, Inf millenia/day + 10%, ETA: 0:00:02, Inf millenia/day 15%, ETA: 0:00:01, Inf millenia/day 20%, ETA: 0:00:01, Inf millenia/day 25%, ETA: 0:00:01, Inf millenia/day @@ -23,7 +23,7 @@ All data will be stored in /home/runner/work/SpeedyWeather.jl/SpeedyWeather.jl/d 55%, ETA: 0:00:01, Inf millenia/day 60%, ETA: 0:00:01, Inf millenia/day 65%, ETA: 0:00:01, Inf millenia/day - 70%, ETA: 0:00:00, Inf millenia/day + 70%, ETA: 0:00:01, Inf millenia/day 75%, ETA: 0:00:00, Inf millenia/day 80%, ETA: 0:00:00, Inf millenia/day 85%, ETA: 0:00:00, Inf millenia/day diff --git a/previews/PR645/run_0004/parameters.txt b/previews/PR645/run_0004/parameters.txt index 3df21457b..b2dc2b231 100644 --- a/previews/PR645/run_0004/parameters.txt +++ b/previews/PR645/run_0004/parameters.txt @@ -170,7 +170,7 @@ Feedback <: AbstractFeedback ├ start::Int64 = 0 ├ barlen::Nothing = nothing ├ barglyphs::ProgressMeter.BarGlyphs = ProgressMeter.BarGlyphs('|', '█', ['▏', '▎', '▍', '▌', '▋', '▊', '▉'], ' ', '|') -└ core::ProgressMeter.ProgressCore = ProgressMeter.ProgressCore(:green, "Weather is speedy: run 0004 ", 0.1, false, 0, IOContext(Base.PipeEndpoint(RawFD(-1) closed, 0 bytes waiting)), true, 1, 0, ReentrantLock(nothing, 0x00000000, 0x00, Base.GenericCondition{Base.Threads.SpinLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), (0, 0, -281044959039874)), 0, 1, false, false, 1.734444307021621e9, 1.734444307021621e9, 1.734444307021621e9) +└ core::ProgressMeter.ProgressCore = ProgressMeter.ProgressCore(:green, "Weather is speedy: run 0004 ", 0.1, false, 0, IOContext(Base.PipeEndpoint(RawFD(-1) closed, 0 bytes waiting)), true, 1, 0, ReentrantLock(nothing, 0x00000000, 0x00, Base.GenericCondition{Base.Threads.SpinLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), (5, 0, -1)), 0, 1, false, false, 1.734444716463566e9, 1.734444716463566e9, 1.734444716463566e9) ├ progress_txt::Nothing = nothing └ nars_detected::Bool = false diff --git a/previews/PR645/run_0004/progress.txt b/previews/PR645/run_0004/progress.txt index 9c54d4317..471b146f5 100644 --- a/previews/PR645/run_0004/progress.txt +++ b/previews/PR645/run_0004/progress.txt @@ -1,4 +1,4 @@ -Starting SpeedyWeather.jl run 0004 on Tue, 17 Dec 2024 14:05:07 +Starting SpeedyWeather.jl run 0004 on Tue, 17 Dec 2024 14:11:56 Integrating: SpectralGrid: ├ Spectral: T42 LowerTriangularMatrix{Complex{Float32}}, radius = 6.371e6 m @@ -10,9 +10,9 @@ Time: 1.0 days at Δt = 1786.047s All data will be stored in /home/runner/work/SpeedyWeather.jl/SpeedyWeather.jl/docs/build/run_0004 - 25%, ETA: 0:00:00, 56440.73 millenia/day - 50%, ETA: 0:00:00, 118012.43 millenia/day - 75%, ETA: 0:00:00, 179584.14 millenia/day -100%, ETA: 0:00:00, 241155.84 millenia/day + 25%, ETA: 0:00:00, Inf millenia/day + 50%, ETA: 0:00:00, Inf millenia/day + 75%, ETA: 0:00:00, Inf millenia/day +100%, ETA: 0:00:00, Inf millenia/day Integration done in empty period. diff --git a/previews/PR645/run_0005/parameters.txt b/previews/PR645/run_0005/parameters.txt index fc5b46158..4824875c4 100644 --- a/previews/PR645/run_0005/parameters.txt +++ b/previews/PR645/run_0005/parameters.txt @@ -170,7 +170,7 @@ Feedback <: AbstractFeedback ├ start::Int64 = 0 ├ barlen::Nothing = nothing ├ barglyphs::ProgressMeter.BarGlyphs = ProgressMeter.BarGlyphs('|', '█', ['▏', '▎', '▍', '▌', '▋', '▊', '▉'], ' ', '|') -└ core::ProgressMeter.ProgressCore = ProgressMeter.ProgressCore(:green, "Weather is speedy: run 0005 ", 0.1, false, 0, IOContext(Base.PipeEndpoint(RawFD(-1) closed, 0 bytes waiting)), true, 1, 0, ReentrantLock(nothing, 0x00000000, 0x00, Base.GenericCondition{Base.Threads.SpinLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), (1, 140257051052256, 8)), 0, 1, false, false, 1.734444307421093e9, 1.734444307421093e9, 1.734444307421094e9) +└ core::ProgressMeter.ProgressCore = ProgressMeter.ProgressCore(:green, "Weather is speedy: run 0005 ", 0.1, false, 0, IOContext(Base.PipeEndpoint(RawFD(-1) closed, 0 bytes waiting)), true, 1, 0, ReentrantLock(nothing, 0x00000000, 0x00, Base.GenericCondition{Base.Threads.SpinLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), (1, 11, 8)), 0, 1, false, false, 1.734444716837185e9, 1.734444716837185e9, 1.734444716837185e9) ├ progress_txt::Nothing = nothing └ nars_detected::Bool = false diff --git a/previews/PR645/run_0005/progress.txt b/previews/PR645/run_0005/progress.txt index 0e1cd1800..e2499efaf 100644 --- a/previews/PR645/run_0005/progress.txt +++ b/previews/PR645/run_0005/progress.txt @@ -1,4 +1,4 @@ -Starting SpeedyWeather.jl run 0005 on Tue, 17 Dec 2024 14:05:07 +Starting SpeedyWeather.jl run 0005 on Tue, 17 Dec 2024 14:11:56 Integrating: SpectralGrid: ├ Spectral: T42 LowerTriangularMatrix{Complex{Float32}}, radius = 6.371e6 m diff --git a/previews/PR645/run_test/parameters.txt b/previews/PR645/run_test/parameters.txt index d818634f4..ef29c1b53 100644 --- a/previews/PR645/run_test/parameters.txt +++ b/previews/PR645/run_test/parameters.txt @@ -170,7 +170,7 @@ Feedback <: AbstractFeedback ├ start::Int64 = 0 ├ barlen::Nothing = nothing ├ barglyphs::ProgressMeter.BarGlyphs = ProgressMeter.BarGlyphs('|', '█', ['▏', '▎', '▍', '▌', '▋', '▊', '▉'], ' ', '|') -└ core::ProgressMeter.ProgressCore = ProgressMeter.ProgressCore(:green, "Weather is speedy: run test ", 0.1, false, 0, IOContext(Base.PipeEndpoint(RawFD(-1) closed, 0 bytes waiting)), true, 1, 1919, ReentrantLock(nothing, 0x00000000, 0x00, Base.GenericCondition{Base.Threads.SpinLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), (1, 8, 433372752855)), 0, 1, false, false, 1.734444279426866e9, 1.734444279426867e9, 1.734444279426867e9) +└ core::ProgressMeter.ProgressCore = ProgressMeter.ProgressCore(:green, "Weather is speedy: run test ", 0.1, false, 0, IOContext(Base.PipeEndpoint(RawFD(-1) closed, 0 bytes waiting)), true, 1, 1919, ReentrantLock(nothing, 0x00000000, 0x00, Base.GenericCondition{Base.Threads.SpinLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), (2, 3, 432243320548)), 0, 1, false, false, 1.734444688176551e9, 1.734444688176551e9, 1.734444688176551e9) ├ progress_txt::Nothing = nothing └ nars_detected::Bool = false diff --git a/previews/PR645/run_test/progress.txt b/previews/PR645/run_test/progress.txt index ac3f57738..fbeb1b7bb 100644 --- a/previews/PR645/run_test/progress.txt +++ b/previews/PR645/run_test/progress.txt @@ -1,4 +1,4 @@ -Starting SpeedyWeather.jl run test on Tue, 17 Dec 2024 14:04:43 +Starting SpeedyWeather.jl run test on Tue, 17 Dec 2024 14:11:32 Integrating: SpectralGrid: ├ Spectral: T63 LowerTriangularMatrix{Complex{Float32}}, radius = 6.371e6 m @@ -15,7 +15,7 @@ All data will be stored in /home/runner/work/SpeedyWeather.jl/SpeedyWeather.jl/d 15%, ETA: 0:00:02, Inf millenia/day 20%, ETA: 0:00:02, Inf millenia/day 25%, ETA: 0:00:02, Inf millenia/day - 30%, ETA: 0:00:01, Inf millenia/day + 30%, ETA: 0:00:02, Inf millenia/day 35%, ETA: 0:00:01, Inf millenia/day 40%, ETA: 0:00:01, Inf millenia/day 45%, ETA: 0:00:01, Inf millenia/day diff --git a/previews/PR645/search_index.js b/previews/PR645/search_index.js index 8dcc93620..96bdc9364 100644 --- a/previews/PR645/search_index.js +++ b/previews/PR645/search_index.js @@ -1,3 +1,3 @@ var documenterSearchIndex = {"docs": -[{"location":"land_sea_mask/#The-land-sea-mask","page":"Land-Sea Mask","title":"The land-sea mask","text":"","category":"section"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"The following describes how a custom land-sea mask can be defined. SpeedyWeather uses a fractional land-sea mask, i.e. for every grid-point","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"1 indicates land\n0 indicates ocean\na value in between indicates a grid-cell partially covered by ocean and land","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"Setting the land-sea mask to ocean therefore will disable any fluxes that may come from land, and vice versa. However, with an ocean-everywhere land-sea mask you must also define sea surface temperatures everywhere, otherwise the fluxes in those regions will be zero.","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"For more details, see Surface fluxes and the Land-sea mask section therein.","category":"page"},{"location":"land_sea_mask/#Manual-land-sea-mask","page":"Land-Sea Mask","title":"Manual land-sea mask","text":"","category":"section"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"You can create the default land-sea mask as follows","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=31, nlayers=8)\nland_sea_mask = LandSeaMask(spectral_grid)","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"which will automatically interpolate the land-sea mask onto grid and resolution as defined in spectral_grid at initialization. The actual mask is in land_sea_mask.mask and you can visualise it with","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"model = PrimitiveWetModel(spectral_grid; land_sea_mask)\nsimulation = initialize!(model) # triggers also initialization of model.land_sea_mask\n\nusing CairoMakie\nheatmap(land_sea_mask.mask, title=\"Land-sea mask at T31 resolution\")\nsave(\"land-sea_mask.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"(Image: Land-sea mask)","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"Now after initialization (otherwise you reinitialize the mask, overwriting your changes) you could manually change the land-sea mask with the set! function which can take scalars as global constants or functions of two arguments longitude lambda and varphi. You can use an anonymous function (λ, φ) -> ... but you do not have to, defining function f(λ, φ) and then using land_sea_mask = f works too. ","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"set!(model, land_sea_mask=0) # aqua planet\nset!(model, land_sea_mask=1) # rocky planet\nset!(model, land_sea_mask=(λ, φ) -> rand()-1) # random small islands\n\n# snowball planet with ocean in the tropics between 10˚S and 10˚N\nset!(model, land_sea_mask=(λ, φ) -> abs(φ) < 10 ? 0 : 1)\n\n# flood the northern hemisphere only, values are automatically clamped into [0, 1]\ninitialize!(model.land_sea_mask, model) # back to Earth's mask\nset!(model, land_sea_mask=(λ, φ) -> φ > 0 ? -1 : 0, add=true)\n\n# visualise\nheatmap(land_sea_mask.mask, title=\"Land-sea mask with Northern Hemisphere ocean\")\nsave(\"nh_ocean.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"(Image: NH ocean)","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"And now you can run the simulation as usual with run!(simulation).","category":"page"},{"location":"land_sea_mask/#Earth's-land-sea-mask","page":"Land-Sea Mask","title":"Earth's land-sea mask","text":"","category":"section"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"The Earth's LandSeaMask has itself the option to load another land-sea mask from file, but you also have to specify the grid that mask from files comes on. It will then attempt to read it via NCDatasets and interpolate onto the model grid.","category":"page"},{"location":"land_sea_mask/#AquaPlanetMask","page":"Land-Sea Mask","title":"AquaPlanetMask","text":"","category":"section"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"Predefined is also the AquaPlanetMask which can be created as","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"land_sea_mask = AquaPlanetMask(spectral_grid)","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"and is equivalent to using Earth's LandSeaMask but setting the entire mask to zero afterwards land_sea_mask.mask .= 0.","category":"page"},{"location":"land_sea_mask/#Custom-land-sea-mask","page":"Land-Sea Mask","title":"Custom land-sea mask","text":"","category":"section"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"Every (custom) land-sea mask has to be a subtype of AbstractLandSeaMask. A custom land-sea mask has to be defined as a new type (struct or mutable struct)","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"CustomMask{NF<:AbstractFloat, Grid<:AbstractGrid{NF}} <: AbstractLandSeaMask{NF, Grid}","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"and needs to have at least a field called mask::Grid that uses a Grid as defined by the spectral grid object, so of correct size and with the number format NF. All AbstractLandSeaMask have a convenient generator function to be used like mask = CustomMask(spectral_grid, option=argument), but you may add your own or customize by defining CustomMask(args...) which should return an instance of type CustomMask{NF, Grid} with parameters matching the spectral grid. Then the initialize function has to be extended for that new mask","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"initialize!(mask::CustomMask, model::PrimitiveEquation)","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"which generally is used to tweak the mask.mask grid as you like, using any other options you have included in CustomMask as fields or anything else (preferably read-only, because this is only to initialize the land-sea mask, nothing else) from model. You can for example read something from file, set some values manually, or use coordinates from model.geometry.","category":"page"},{"location":"land_sea_mask/#Time-dependent-land-sea-mask","page":"Land-Sea Mask","title":"Time-dependent land-sea mask","text":"","category":"section"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"It is possible to define an intrusive callback to change the land-sea mask during integration. The grid in model.land_sea_mask.mask is mutable, meaning you can change the values of grid points in-place but not replace the entire mask or change its size. If that mask is changed, this will be reflected in all relevant model components. For example, we can define a callback that floods the entire planet at the beginning of the 21st century as","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"Base.@kwdef struct MilleniumFlood <: SpeedyWeather.AbstractCallback\n schedule::Schedule = Schedule(DateTime(2000,1,1))\nend\n\n# initialize the schedule\nfunction SpeedyWeather.initialize!(\n callback::MilleniumFlood,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel,\n)\n initialize!(callback.schedule, progn.clock)\nend\n\nfunction SpeedyWeather.callback!(\n callback::MilleniumFlood,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel,\n)\n # escape immediately if not scheduled yet\n isscheduled(callback.schedule, progn.clock) || return nothing\n\n # otherwise set the entire land-sea mask to ocean\n model.land_sea_mask.mask .= 0\n @info \"Everything flooded on $(progn.clock.time)\"\nend\n\n# nothing needs to be done after simulation is finished\nSpeedyWeather.finalize!(::MilleniumFlood, args...) = nothing","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"Note that the flooding will take place only at the start of the 21st century, last indefinitely, but not if the model integration period does not cover that exact event, see Schedules. Initializing a model a few days earlier would then have this MilleniumFlood take place","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"land_sea_mask = LandSeaMask(spectral_grid) # start with Earth's land-sea mask\nmodel = PrimitiveWetModel(spectral_grid; land_sea_mask)\nadd!(model, MilleniumFlood()) # or MilleniumFlood(::DateTime) for any non-default date\n\nsimulation = initialize!(model, time=DateTime(1999,12,29))\nrun!(simulation, period=Day(5))\nheatmap(model.land_sea_mask.mask, title=\"Land-sea mask after MilleniumFlood callback\")\nsave(\"land-sea_mask2.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"(Image: Land-sea mask2)","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"And the land-sea mask has successfully been set to ocean everywhere at the start of the 21st century. Note that while we added an @info line into the callback! function, this is here not printed because of how the Documenter works. If you execute this in the REPL you'll see it.","category":"page"},{"location":"surface_fluxes/#Surface-fluxes","page":"Surface fluxes","title":"Surface fluxes","text":"","category":"section"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"The surfaces fluxes in SpeedyWeather represent the exchange of momentum, heat, and moisture between ocean and land as surface into the lowermost atmospheric layer. Surface fluxes of momentum represent a drag that the boundary layer wind experiences due to friction over more or less rough ground on land or over sea. Surface fluxes of heat represent a sensible heat flux from a warmer or colder ocean or land into or out of the surface layer of the atmosphere. Surface fluxes of moisture represent evaporation of sea water into undersaturated surface air masses or, similarly, evaporation from land with a given soil moisture and vegetation's evapotranspiration.","category":"page"},{"location":"surface_fluxes/#Surface-flux-implementations","page":"Surface fluxes","title":"Surface flux implementations","text":"","category":"section"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"Currently implemented surface fluxes of momentum are","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"using InteractiveUtils # hide\nusing SpeedyWeather\nsubtypes(SpeedyWeather.AbstractSurfaceWind)","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"note: Interdependence of surface flux computations\nSurfaceWind computes the surface fluxes of momentum but also the computation of the surface wind (which by default includes wind gusts) meaning that NoSurfaceWind will also effectively disable other surface fluxes unless custom surface fluxes have been implemented that do not rely on column.surface_wind_speed.","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"with more explanation below. The surface heat fluxes currently implemented are","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"subtypes(SpeedyWeather.AbstractSurfaceHeatFlux)","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"and the surface moisture fluxes, i.e. evaporation (this does not include Convection or Large-scale condensation which currently immediately removes humidity instead of fluxing it out at the bottom) implemented are","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"subtypes(SpeedyWeather.AbstractSurfaceEvaporation)","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"The calculation of thermodynamic quantities at the surface (air density, temperature, humidity) are handled by","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"subtypes(SpeedyWeather.AbstractSurfaceThermodynamics)","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"and the computation of drag coefficients (which is used by default for the surface fluxes above) is handled through the model.boundary_layer where currently implemented are","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"subtypes(SpeedyWeather.AbstractBoundaryLayer)","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"Note that LinearDrag is the linear drag following Held and Suarez (see Held-Suarez forcing) which does not compute a drag coefficient and therefore by default effectively disables other surface fluxes (as the Held and Suarez forcing and drag is supposed to be used instead of physical parameterizations).","category":"page"},{"location":"surface_fluxes/#Fluxes-to-tendencies","page":"Surface fluxes","title":"Fluxes to tendencies","text":"","category":"section"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"In SpeedyWeather.jl, parameterizations can be defined either in terms of tendencies for a given layer or as fluxes between two layers including the surface flux and a top-of-the-atmosphere flux. The upward flux F^uparrow out of layer k+1 into layer k (vertical indexing k increases downwards) is F^uparrow_k+h (h = frac12 for half, as the flux sits on the cell face, the half-layer in between k and k+1) and similarly F^downarrow_k+h is the downward flux. For clarity we may define fluxes as either upward or downward depending on the process although an upward flux can always be regarded as a negative downward flux. The absorbed flux in layer k is","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"Delta F_k = (F^uparrow_k+h - F^uparrow_k-h) + (F^downarrow_k-h - F^downarrow_k+h)","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"A quick overview of the units","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"Quantity Variable Unit Flux unit\nMomentum Velocity u v ms Pa = kgms^2\nHeat Temperature T ms Wm^2 = kgs^3\nMoisture Specific humidity q kgkg kgm^2s","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"The time-stepping in SpeedyWeather.jl (see Time integration) eventually requires tendencies which are calculated from the absorbed fluxes of momentum u or v and moisture q as","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"fracpartial u_kpartial t = fracg Delta F_kDelta p_k","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"with gravity g and layer-thickness Delta p_k (see Sigma coordinates) so that the right-hand side divides the absorbed flux by the mass of layer k (per unit area). Tendencies for v q equivalently with their respective absorbed fluxes.","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"The temperature tendency is calculated from the absorbed heat flux as","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"fracpartial T_kpartial t = fracg Delta F_kc_p Delta p_k","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"with heat capacity of air at constant pressure c_p with a typical value of 1004JkgK. Because we define the heat flux as having units of Wm^2 the conversion includes the division by the heat capacity to convert to a rate of temperature change.","category":"page"},{"location":"surface_fluxes/#Bulk-Richardson-based-drag-coefficient","page":"Surface fluxes","title":"Bulk Richardson-based drag coefficient","text":"","category":"section"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"All surface fluxes depend on a dimensionless drag coefficient C which we calculate as a function of the bulk Richardson number Ri following Frierson, et al. 2006 [Frierson2006] with some simplification as outlined below. We use the same drag coefficient for momentum, heat and moisture fluxes. The bulk Richardson number at the lowermost model layer k = N of height z_N is","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"Ri_N = fracgz_N left( Theta_v(z_N) - Theta_v(0) right)v(z_N)^2 Theta_v(0)","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"with gz_N = Phi_N the Geopotential at z = z_N, Theta = c_pT_v + gz the virtual dry static energy and T_v the Virtual temperature. Then the drag coefficient C follows as","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"C = begincases\n kappa^2 left( log(fracz_Nz_0) right)^-2 quad textfor quad Ri_N leq 0\n kappa^2 left( log(fracz_Nz_0) right)^-2 left(1 - fracRi_NRi_cright)^2 quad textfor quad 0 Ri_N Ri_c \n 0 quad textfor quad Ri_N geq Ri_c \n endcases","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"with kappa = 04 the von Kármán constant, z_0 = 321 cdot 10^-5 the roughness length. There is a maximum drag C for negative bulk Richardson numbers Ri_N but the drag becomes 0 for bulk Richardson numbers being larger than a critical Ri_c = 1 with a smooth transition in between. The height of the N-th model layer is z_N = tfracPhi_N - Phi_0g with the geopotential","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"Phi_N = Phi_0 + T_N R_d ( log p_N+h - log p_N)","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"which depends on the temperature T_N of that layer. To simplify this calculation and avoid the logarithm we use a constant Z approx z_N from a reference temperature T_ref instead of T_N in the calculation of log(z_Nz_0). While z_N depends on the vertical resolution (Delta p_N of the lowermost layer) it is proportional to that layer's temperature which in Kelvin does not change much in space and in time, especially with the logarithm applied. For T_ref = 200K with specific gas constant R_d = 287 JkgK on sigma level sigma_N = 095 we have log(z_Nz_0) approx 161 for T_ref = 300K this increases to log(z_Nz_0) approx 165 a 2.5% increase which we are happy to approximate. Note that we do not apply this approximation within the bulk Richardson number Ri_N. So we calculate once a typical height of the lowermost layer Z = T_refR_d log(1sigma_N)g^-1 for the given parameter choices and then define a maximum drag constant","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"C_max = left(frackappalog(fracZz_0) right)^2","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"to simplify the drag coefficient calculation to","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"C = C_max left(1 - fracRi_N^*Ri_cright)^2","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"with Ri_N^* = max(0 min(Ri_N Ri_c)) the clamped Ri_N which is at least 0 and at most Ri_c.","category":"page"},{"location":"surface_fluxes/#Surface-momentum-fluxes","page":"Surface fluxes","title":"Surface momentum fluxes","text":"","category":"section"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"The surface momentum flux is calculated from the surface wind velocities","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"u_s = f_w u_N quad v_s = f_w v_N","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"meaning it is scaled down by f_w = 095 (Fortran SPEEDY default, [SPEEDY]) from the lowermost layer wind velocities u_N v_N. A wind speed scale accounting for gustiness with V_gust = 5ms (Fortran SPEEDY default, [SPEEDY]) is then defined as","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"V_0 = sqrtu_s^2 + v_s^2 + V_gust^2","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"such that for low wind speeds the fluxes are somewhat higher because of unresolved winds on smaller time and length scales. The momentum flux is then","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"beginaligned\nF_u^uparrow = - rho_s C V_0 u_s \nF_v^uparrow = - rho_s C V_0 v_s\nendaligned","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"with rho_s = fracp_sR_d T_N the surface air density calculated from surface pressure p_s and lowermost layer temperature T_N. Better would be to extrapolate T_N to T_s a surface air temperature assuming adiabatic descent but that is currently not implemented.","category":"page"},{"location":"surface_fluxes/#Surface-heat-fluxes","page":"Surface fluxes","title":"Surface heat fluxes","text":"","category":"section"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"The surface heat flux is proportional to the difference of the surface air temperature T_s and the land or ocean skin temperature T_skin. We currently just approximate as T_N the lowermost layer temperature although an adiabatic descent from pressure p_N to surface pressure p_s would be more accurate. We also use land and sea surface temperature to approximate T_skin although future improvements should account for faster radiative effects on T_skin compared to sea and land surface temperatures determined by a higher heat capacity of the relevant land surface layer or the mixed layer in the ocean. We then compute","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"F_T^uparrow = rho_s C V_0 c_p (T_skin - T_s)","category":"page"},{"location":"surface_fluxes/#Surface-evaporation","page":"Surface fluxes","title":"Surface evaporation","text":"","category":"section"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"The surface moisture flux, i.e. evaporation of soil moisture over land and evaporation of sea water over the ocean is proportional to the difference of the surface specific humidity q_s and the saturation specific humidity given T_skin and surface pressure p_s. This assumes that a very thin layer of air just above the ocean is saturated but over land this assumption is less well justified as it should be a function of the soil moisture and how much of that is available to evaporate given vegetation. We again make the simplification that q_s = q_N, i.e. specific humidity of the surface is the same as in the lowermost atmospheric layer above. ","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"The surface evaporative flux is then (always positive)","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"F_q^uparrow = rho_s C V_0 max(0 alpha_sw q^* - q_s)","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"with q^* the saturation specific humidity calculated from the skin temperature T_skin and surface pressure p_s. The available of soil water over land is represented by (over the ocean alpha_sw = 1)","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"alpha_sw = fracD_top W_top + f_veg D_root max(0 W_root - W_wil)\n D_topW_cap + D_root(W_cap - W_wil)","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"following the Fortran SPEEDY documentation[SPEEDY] which follows Viterbo and Beljiars 1995 [Viterbo95]. The variables (or spatially prescribed arrays) are water content in the top soil layer W_top and the root layer below W_root using the vegetation fraction f_veg = veg_high + 08 veg_low composed of a (dimensionless) high and low vegetation cover per grid cell veg_high veg_low. The constants are depth of top soil layer D_top = 7cm, depth of root layer D_root = 21cm, soil wetness at field capacity (volume fraction) W_cap = 03, and soil wetness at wilting point (volume fraction) W_wil = 017.","category":"page"},{"location":"surface_fluxes/#Land-sea-mask","page":"Surface fluxes","title":"Land-sea mask","text":"","category":"section"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"SpeedyWeather uses a fractional land-sea mask, i.e. for every grid-point","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"1 indicates land\n0 indicates ocean\na value in between indicates a grid-cell partially covered by ocean and land","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"The land-sea mask determines solely how to weight the surface fluxes coming from land or from the ocean. For the sensible heat fluxes this uses land and sea surface temperatures and weights the respective fluxes proportional to the fractional mask. Similar for evaporation. You can therefore define an ocean on top of a mountain, or a land without heat fluxes when the land-surface temperature is not defined, i.e. NaN. Let F_L F_S be the fluxes coming from land and sea, respectively. Then the land-sea mask a in 01 weights them as follows for the total flux F","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"F = aF_L + (1-a)F_S","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"but F=F_L if the sea flux is NaN (because the ocean temperature is not defined) and F=F_S if the land flux is NaN (because the land temperature or soil moisture is not defined, for sensible heat fluxes or evaporation), and F=0 if both fluxes are NaN.","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"Setting the land-sea mask to ocean therefore will disable any fluxes that may come from land, and vice versa. However, with an ocean-everywhere land-sea mask you must also define sea surface temperatures everywhere, otherwise the fluxes in those regions will be zero.","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"For more details see The land-sea mask implementation section.","category":"page"},{"location":"surface_fluxes/#References","page":"Surface fluxes","title":"References","text":"","category":"section"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"[Frierson2006]: Frierson, D. M. W., I. M. Held, and P. Zurita-Gotor, 2006: A Gray-Radiation Aquaplanet Moist GCM. Part I: Static Stability and Eddy Scale. J. Atmos. Sci., 63, 2548-2566. DOI: 10.1175/JAS3753.1. ","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"[SPEEDY]: Franco Molteni and Fred Kucharski, 20??. Description of the ICTP AGCM (SPEEDY) Version 41. https://users.ictp.it/~kucharsk/speedydescription/kmver41_appendixA.pdf","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"[Viterbo95]: Viterbo, P., and A. C. M. Beljaars, 1995: An Improved Land Surface Parameterization Scheme in the ECMWF Model and Its Validation. J. Climate, 8, 2716-2748, DOI:10.1175/1520-0442(1995)008<2716:AILSPS>2.0.CO;2. ","category":"page"},{"location":"parameterizations/#Parameterizations","page":"Parameterizations","title":"Parameterizations","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"The following is an overview of how our parameterizations from a software engineering perspective are internally defined and how a new parameterization can be accordingly implemented. For the mathematical formulation and the physics they represent see","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"Vertical diffusion\nConvection\nLarge-scale condensation\nRadiation\nSurface fluxes ","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"We generally recommend reading Extending SpeedyWeather first, which explains the logic of how to extend many of the components in SpeedyWeather. The same logic applies here and we will not iterate on many of the details, but want to highlight which abstract supertype new parameterizations have to subtype respectively and which functions and signatures they have to extend.","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"In general, every parameterization \"class\" (e.g. convection) is just a conceptual class for clarity. You can define a custom convection parameterization that acts as a longwave radiation and vice versa. This also means that if you want to implement a parameterization that does not fit into any of the \"classes\" described here you can still implement it under any name and any class. From a software engineering perspective they are all the same except that they are executed in the order as outlined in Pass on to model construction. That's also why below we write for every parameterization \"expected to write into some.array_name\" as this would correspond conceptually to this class, but no hard requirement exists that a parameterization actually does that.","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"We start by highlighting some general do's and don'ts for parameterization before listing specifics for individual parameterizations.","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"info: Parameterizations for PrimitiveEquation models only\nThe parameterizations described here can only be used for the primitive equation models PrimitiveDryModel and PrimitiveWetModel as the parameterizations are defined to act on a vertical column. For the 2D models BarotropicModel and ShallowWaterModel additional terms have to be defined as a custom forcing or drag, see Extending SpeedyWeather.","category":"page"},{"location":"parameterizations/#Use-ColumnVariables-work-arrays","page":"Parameterizations","title":"Use ColumnVariables work arrays","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"When defining a new (mutable) parameterization with (mutable) fields do make sure that is constant during the model integration. While you can and are encouraged to use the initialize! function to precompute arrays (e.g. something that depends on latitude using model.geometry.latd) these should not be used as work arrays on every time step of the model integration. The reason is that the parameterization are executed in a parallel loop over all grid points and a mutating parameterization object would create a race condition with undefined behaviour.","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"Instead, column::ColumnVariables has several work arrays that you can reuse column.a and .b, .c, .d. Depending on the number of threads there will be several column objects to avoid the race condition if several threads would compute the parameterizations for several columns in parallel. An example is the Simplified Betts-Miller convection scheme which needs to compute reference profiles which should not live inside the model.convection object (as there's always only one of those). Instead this parameterization does the following inside convection!(column::ColumnVariables, ...)","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"# use work arrays for temp_ref_profile, humid_ref_profile\ntemp_ref_profile = column.a\nhumid_ref_profile = column.b","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"These work arrays have an unknown state so you should overwrite every entry and you also should not use them to retain information after that parameterization has been executed.","category":"page"},{"location":"parameterizations/#Accumulate-do-not-overwrite","page":"Parameterizations","title":"Accumulate do not overwrite","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"Every parameterization either computes tendencies directly or indirectly via fluxes (upward or downward, see Fluxes to tendencies). Both of these are arrays in which every parameterization writes into, meaning they should be accumulated not overwritten. Otherwise any parameterization that executed beforehand is effectively disabled. Hence, do","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"column.temp_tend[k] += something_you_calculated","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"not column.temp_tend[k] = something_you_calculated which would overwrite any previous tendency. The tendencies are reset to zero for every grid point at the beginning of the evaluation of the parameterization for that grid point, meaning you can do tend += a even for the first parameterization that writes into a given tendency as this translates to tend = 0 + a.","category":"page"},{"location":"parameterizations/#Pass-on-to-model-construction","page":"Parameterizations","title":"Pass on to model construction","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"After defining a (custom) parameterization it is recommended to also define a generator function that takes in the SpectralGrid object (see How to run SpeedyWeather.jl) as first (positional) argument, all other arguments can then be passed on as keyword arguments with defaults defined. Creating the default convection parameterization for example would be","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=31, nlayers=8)\nconvection = SimplifiedBettsMiller(spectral_grid, time_scale=Hour(4))","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"Further keyword arguments can be added or omitted all together (using the default setup), only the spectral_grid is required. We have chosen here the name of that parameterization to be convection but you could choose any other name too. However, with this choice one can conveniently pass on via matching the convection field inside PrimitiveWetModel, see Keyword Arguments.","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"model = PrimitiveWetModel(spectral_grid; convection)\nnothing # hide","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"otherwise we would need to write","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"my_convection = SimplifiedBettsMiller(spectral_grid)\nmodel = PrimitiveWetModel(spectral_grid, convection=my_convection)\nnothing # hide","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"The following is an overview of what the parameterization fields inside the model are called. See also Tree structure, and therein PrimitiveDryModel and PrimitiveWetModel","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"model.boundary_layer_drag\nmodel.temperature_relaxation\nmodel.vertical_diffusion\nmodel.convection\nmodel.large_scale_condensation (PrimitiveWetModel only)\nmodel.shortwave_radiation\nmodel.longwave_radiation\nmodel.surface_thermodynamics\nmodel.surface_wind\nmodel.surface_heat_flux\nmodel.surface_evaporation (PrimitiveWetModel only)","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"Note that the parameterizations are executed in the order of the list above. That way, for example, radiation can depend on calculations in large-scale condensation but not vice versa (only at the next time step).","category":"page"},{"location":"parameterizations/#Custom-boundary-layer-drag","page":"Parameterizations","title":"Custom boundary layer drag","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"A boundary layer drag can serve two purposes: (1) Actually define a tendency to the momentum equations that acts as a drag term, or (2) calculate the drag coefficient C in column.boundary_layer_drag that is used in the Surface fluxes.","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"subtype CustomDrag <: AbstractBoundaryLayer\ndefine initialize!(::CustomDrag, ::PrimitiveEquation)\ndefine boundary_layer_drag!(::ColumnVariables, ::CustomDrag, ::PrimitiveEquation)\nexpected to accumulate (+=) into column.u_tend and column.v_tend\nor calculate column.boundary_layer_drag to be used in surface fluxes","category":"page"},{"location":"parameterizations/#Custom-temperature-relaxation","page":"Parameterizations","title":"Custom temperature relaxation","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"By default, there is no temperature relaxation in the primitive equation models (i.e. temperature_relaxation = NoTemperatureRelaxation()). This parameterization exists for the Held-Suarez forcing.","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"subtype CustomTemperatureRelaxation <: AbstractTemperatureRelaxation\ndefine initialize!(::CustomTemperatureRelaxation, ::PrimitiveEquation)\ndefine temperature_relaxation!(::ColumnVariables, ::CustomTemperatureRelaxation, ::PrimitiveEquation)\nexpected to accumulate (+=) into column.temp_tend","category":"page"},{"location":"parameterizations/#Custom-vertical-diffusion","page":"Parameterizations","title":"Custom vertical diffusion","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"While vertical diffusion may be applied to temperature (usually via some form of static energy to account for adiabatic diffusion), humidity and/or momentum, they are grouped together. You can define a vertical diffusion for only one or several of these variables where you then can internally call functions like diffuse_temperature!(...) for each variable. For vertical diffusion","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"subtype CustomVerticalDiffusion <: AbstractVerticalDiffusion\ndefine initialize!(::CustomVerticalDiffusion, ::PrimitiveEquation)\ndefine vertical_diffusion!(::ColumnVariables, ::CustomVerticalDiffusion, ::PrimitiveEquation)\nexpected to accumulate (+=) into column.temp_tend, and similarly for humid, u, and/or v\nor using fluxes like column.flux_temp_upward","category":"page"},{"location":"parameterizations/#Custom-convection","page":"Parameterizations","title":"Custom convection","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"subtype CustomConvection <: AbstractConvection\ndefine initialize!(::CustomConvection, ::PrimitiveEquation)\ndefine convection!(::ColumnVariables, ::CustomConvection, ::PrimitiveEquation)\nexpected to accumulate (+=) into column.temp_tend and column.humid_tend\nor using fluxes, .flux_temp_upward or similarly for humid or downward","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"Note that we define convection here for a model of type PrimitiveEquation, i.e. both dry and moist convection. If your CustomConvection only makes sense for one of them use ::PrimitiveDry or ::PrimitiveWet instead.","category":"page"},{"location":"parameterizations/#Custom-large-scale-condensation","page":"Parameterizations","title":"Custom large-scale condensation","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"subtype CustomCondensation <: AbstractCondensation\ndefine initialize!(::CustomCondensation, ::PrimitiveWet)\ndefine condensation!(::ColumnVariables, ::CustomCondensation, ::PrimitiveWet)\nexpected to accumulate (+=) into column.humid_tend and column.temp_tend\nor using fluxes, .flux_humid_downward or similarly for temp or upward","category":"page"},{"location":"parameterizations/#Custom-radiation","page":"Parameterizations","title":"Custom radiation","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"AbstractRadiation has two subtypes, AbstractShortwave and AbstractLongwave representing two (from a software engineering perspective) independent parameterizations that are called one after another (short then long). For shortwave","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"subtype CustomShortwave <: AbstractShortwave\ndefine initialize!(::CustomShortwave, ::PrimitiveEquation)\ndefine shortwave_radiation!(::ColumnVariables, ::CustomShortwave, ::PrimitiveEquation)\nexpected to accumulate (+=) into column.flux_temp_upward, .flux_temp_downward\nor directly into the tendency .temp_tend","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"For longwave this is similar but using <: AbstractLongwave and longwave_radiation!.","category":"page"},{"location":"parameterizations/#Custom-surface-fluxes","page":"Parameterizations","title":"Custom surface fluxes","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"Surface fluxes are the most complicated to customize as they depend on the Ocean and Land model, The land-sea mask, and by default the Bulk Richardson-based drag coefficient, see Custom boundary layer drag. The computation of the surface fluxes is split into four (five if you include the boundary layer drag coefficient in Custom boundary layer drag) components that are called one after another","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"Surface thermodynamics to calculate the surface values of lowermost layer variables","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"subtype CustomSurfaceThermodynamics <: AbstractSurfaceThermodynamics\ndefine initialize!(::CustomSurfaceThermodynamics, ::PrimitiveEquation)\ndefine surface_thermodynamics!(::ColumnVariables, ::CustomSurfaceThermodynamics, ::PrimitiveEquation)\nexpected to set column.surface_temp, .surface_humid, .surface_air_density","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"Surface wind to calculate wind stress (momentum flux) as well as surface wind used","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"subtype CustomSurfaceWind <: AbstractSurfaceWind\ndefine initialize!(::CustomSurfaceWind, ::PrimitiveEquation)\ndefine surface_wind_stress!(::ColumnVariables, ::CustomSurfaceWind, ::PrimitiveEquation)\nexpected to set column.surface_wind_speed for other fluxes\nand accumulate (+=) into column.flux_u_upward and .flux_v_upward","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"Surface (sensible) heat flux","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"subtype CustomSurfaceHeatFlux <: AbstractSurfaceHeatFlux\ndefine initialize!(::CustomSurfaceHeatFlux, ::PrimitiveEquation)\ndefine surface_heat_flux!(::ColumnVariables, ::CustomSurfaceHeatFlux, ::PrimitiveEquation)\nexpected to accumulate (+=) into column.flux_temp_upward","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"Surface evaporation","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"subtype CustomSurfaceEvaporation <: AbstractSurfaceEvaporation\ndefine initialize!(::CustomSurfaceEvaporation, ::PrimitiveEquation)\ndefine surface_evaporation!(::ColumnVariables, ::CustomSurfaceEvaporation, ::PrimitiveEquation)\nexpected to accumulate (+=) into column.flux_humid_upward","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"You can customize individual components and leave the other ones as default or by setting them to NoSurfaceWind, NoSurfaceHeatFlux, NoSurfaceEvaporation, but note that without the surface wind the heat and evaporative fluxes are also effectively disabled as they scale with the column.surface_wind_speed set by default with the surface_wind_stress! in (2.) above.","category":"page"},{"location":"vertical_diffusion/#Vertical-diffusion","page":"Vertical diffusion","title":"Vertical diffusion","text":"","category":"section"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"Vertical diffusion in SpeedyWeather.jl is implemented as a Laplacian in the vertical Sigma coordinates with a diffusion coefficient K that in general depends on space and time and is flow-aware, meaning it is recalculated on every time step depending on the vertical stability of the atmospheric column.","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"Vertical diffusion can be applied to velocities u v, temperature T (done via dry static energy, see below) and specific humidity q.","category":"page"},{"location":"vertical_diffusion/#Implementations","page":"Vertical diffusion","title":"Implementations","text":"","category":"section"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"The following schemes for vertical diffusion are currently implemented","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"using InteractiveUtils # hide\nusing SpeedyWeather\nsubtypes(SpeedyWeather.AbstractVerticalDiffusion)","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"NoVerticalDiffusion disabled all vertical diffusion, BulkRichardsonDiffusion is explained in the following.","category":"page"},{"location":"vertical_diffusion/#Laplacian-in-sigma-coordinates","page":"Vertical diffusion","title":"Laplacian in sigma coordinates","text":"","category":"section"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"The vertical diffusion of a variable, say u, takes the in sigma coordinates the form","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"fracpartial upartial t = fracpartialpartial sigma K\nfracpartial upartial sigma","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"as a tendency to u with no-flux boundary conditions fracpartial upartial sigma = 0 at sigma = 0 (top of the atmosphere) and sigma = 1 (the surface). That way the diffusion preserves the integral of the variable u from sigma = 0 to sigma = 1.","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"fracpartialpartial t int_0^1 u dsigma = int_0^1 fracpartialpartial sigma K\nfracpartial upartial sigma dsigma = \nKfracpartial upartial sigma vert_sigma = 1 - Kfracpartial upartial sigma vert_sigma = 0\n= 0","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"Discretising the diffusion operator partial_sigma K partial_sigma over N vertical layers k = 1N with u_k, K_k on those layers at respective coordinates sigma_k that are generally not equally spaced using centred finite differences","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"fracpartialpartial sigma K fracpartial upartial sigma approx\nfrac\n fracK_k+1 + K_k2 fracu_k+1 - u_k sigma_k+1 - sigma_k - \n fracK_k + K_k-12 fracu_k - u_k-1sigma_k - sigma_k-1\n\n sigma_k+12 - sigma_k-12\n","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"We reconstruct K on the faces k+12 with a simple arithmetic average of K_k and K_k+1. This is necessary for the multiplication with the gradients which are only available on the faces after the centred gradients are computed. We then take the gradient again to obtain the final tendencies again at cell centres k.","category":"page"},{"location":"vertical_diffusion/#Bulk-Richardson-based-diffusion-coefficient","page":"Vertical diffusion","title":"Bulk Richardson-based diffusion coefficient","text":"","category":"section"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"We calculate the diffusion coefficient K based on the bulk Richardson number Ri [Frierson2006] which is computed as follows","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"Ri = fracgz left( Theta_v(z) - Theta_v(z_N) right)v(z)^2 Theta_v(z_N)","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"(see Bulk Richardson-based drag coefficient in comparison). Gravitational acceleration is g, height z, Theta_v the virtual potential temperature where we use the virtual dry static energy c_pT_v + gz with T_v the Virtual temperature. The boundary layer height h (vertical index k_h) is defined as the height of the lowermost layer where Ri_k_h Ri_c with Ri_c = 1 the critical Richardson number.","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"The diffusion coefficient K is for every layer k geq k_h in the boundary layer calculated depending on the height z of a layer and its bulk Richardson number Ri.","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"K(z) = begincases\n K_b(z) quad textfor quad z leq f_b h \n K_b(f_b h) fraczf_b h left( 1 - fracz - f_b h(1 - f_b)h right)^2\n quad textfor quad f_b h z leq h \nendcases","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"with f_b = 01 the fraction of the boundary layer height h above which the second case guarantees a smooth transition in K to zero at z = h. K_b(z) is then defined as","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"Kb(z) = begincases\n kappa u_N sqrtCz quad textfor quad Ri_N leq 0 \n kappa u_N sqrtCz left( 1 + fracRiRi_cfraclog(zz_0)1 - RiRi_cright)^-1\n quad textfor quad Ri_N 0 \nendcases","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"C is the surface drag coefficient as computed in Bulk Richardson-based drag coefficient. The subscript N denotes the lowermost model layer k=N.","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"As for the Bulk Richardson-based drag coefficient we also simplify this calculation here by approximating log(zz_0) approx log(Zz_0) with the height Z of the lowermost layer given resolution and a reference surface temperature, for more details see description in that section.","category":"page"},{"location":"vertical_diffusion/#Vertical-diffusion-tendencies","page":"Vertical diffusion","title":"Vertical diffusion tendencies","text":"","category":"section"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"The vertical diffusion is then computed as a tendency for u v q and temperature T via the dry static energy SE = c_p T + gz, i.e.","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"fracpartial Tpartial t = frac1c_pfracpartialpartial sigma K\nfracpartial SEpartial sigma ","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"where we just fold the heat capacity c_p into the diffusion coefficient K to Kc_p. The other variables are diffused straight-forwardly as partial_t u = partial_sigma K partial_sigma u, etc.","category":"page"},{"location":"vertical_diffusion/#References","page":"Vertical diffusion","title":"References","text":"","category":"section"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"[Frierson2006]: Frierson, D. M. W., I. M. Held, and P. Zurita-Gotor, 2006: A Gray-Radiation Aquaplanet Moist GCM. Part I: Static Stability and Eddy Scale. J. Atmos. Sci., 63, 2548-2566. DOI: 10.1175/JAS3753.1.","category":"page"},{"location":"large_scale_condensation/#Large-scale-condensation","page":"Large-scale condensation","title":"Large-scale condensation","text":"","category":"section"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"Large-scale condensation in an atmospheric general circulation represents the micro-physical that kicks in when an air parcel reaches saturation. Subsequently, the water vapour inside it condenses, forms droplets around condensation nuclei, which grow, become heavy and eventually fall out as precipitation. This process is never actually representable at the resolution of global (or even regional) atmospheric models as typical cloud droplets have a size of micrometers. Atmospheric models therefore rely on large-scale quantities such as specific humidity, pressure and temperature within a given grid cell, even though there might be considerably variability of these quantities within a grid cell if the resolution was higher.","category":"page"},{"location":"large_scale_condensation/#Condensation-implementations","page":"Large-scale condensation","title":"Condensation implementations","text":"","category":"section"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"Currently implemented are","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"using InteractiveUtils # hide\nusing SpeedyWeather\nsubtypes(SpeedyWeather.AbstractCondensation)","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"which are described in the following.","category":"page"},{"location":"large_scale_condensation/#Explicit-large-scale-condensation","page":"Large-scale condensation","title":"Explicit large-scale condensation","text":"","category":"section"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"We parameterize this process of large-scale condensation when relative humidity in a grid cell reaches saturation and remove the excess humidity quickly (given time integration constraints, see below) and with an implicit (in the time integration sense) latent heat release. Vertically integrating the tendency of specific humidity due to this process is then the large-scale precipitation.","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"Immediate condensation of humidity q_i q^star at time step i given its saturation q^star humidity calculated from temperature T_i is","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"beginaligned\nq_i+1 - q_i = q^star(T_i) - q_i \nT_i+1 - T_i = -fracL_vc_p( q^star(T_i) - q_i )\nendaligned","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"This condensation is explicit in the time integration sense, meaning that we only use quantities at time step i to calculate the tendency. The latent heat release of that condensation is in the second equation. However, treating this explicitly poses the problem that because the saturation humidity is calculated from the current temperature T_i, which is increased due to the latent heat release, the humidity after this time step will be undersaturated.","category":"page"},{"location":"large_scale_condensation/#Implicit-large-scale-condensation","page":"Large-scale condensation","title":"Implicit large-scale condensation","text":"","category":"section"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"Ideally, one would want to condense towards the new saturation humidity q^star(T_i+1) at i+1 so that condensation draws the relative humidity back down to 100% not below it. Taylor expansion at i of the equation above with q^star(T_i+1) and Delta T = T_i+1 - T_i (and Delta q similarly) to first order yields","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"q_i+1 - q_i = q^star(T_i+1) - q_i = q^star(T_i) + (T_i+1 - T_i)\nfracpartial q^starpartial T (T_i) + O(Delta T^2) - q_i","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"Now we make a linear approximation to the derivative and drop the O(Delta T^2) term. Inserting the (explicit) latent heat release yields","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"Delta q = q^star(T_i) + -fracL_vc_p Delta q fracpartial q^starpartial T (T_i) - q_i","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"And solving for Delta q yields","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"left 1 + fracL_vc_p fracpartial q^starpartial T (T^i) right Delta q = q^star(T_i) - q_i","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"meaning that the implicit immediate condensation can be formulated as (see also [Frierson2006])","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"beginaligned\nq_i+1 - q_i = fracq^star(T_i) - q_i1 + fracL_vc_p fracpartial q^starpartial T(T_i) \nT_i+1 - T_i = -fracL_vc_p( q_i+1 - q_i )\nendaligned","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"With Euler forward time stepping this is great, but with our leapfrog timestepping + RAW filter this is very dispersive (see #445) although the implicit formulation is already much better. We therefore introduce a time step Delta t_c which makes the implicit condensation not immediate anymore but over several time steps Delta t of the leapfrogging.","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"beginaligned\ndelta q = fracq_i+1 - q_iDelta t = fracq^star(T_i) - q_i Delta t_c\nleft( 1 + fracL_vc_p fracpartial q^starpartial T(T_i) right) \ndelta T = fracT_i+1 - T_iDelta t = -fracL_vc_p( fracq_i+1 - q_iDelta t )\nendaligned","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"For Delta t = Delta t_c we have an immediate condensation, for n = fracDelta t_cDelta t condensation takes place over n time steps. One could tie this time scale for condensation to a physical unit, like 6 hours, but because the time step here is ideally short, but cannot be too short for numerical stability, we tie it here to the time step of the numerical integration. This also means that at higher resolution condensation is more immediate than at low resolution, but the dispersive time integration of this term is in all cases similar (and not much higher at lower resolution).","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"The above implied that condensation takes place at 100% relative humidity as we are directly comparing the specific humidity to the saturation specific humidity in q^star - q_i. However, in coarse-resolution models there is a good argument that condensation may already take place below a grid-cell average of 100% relative humidity. Given some subgrid-scale variability parts of the grid cells may condense even though the average humidity is below 100%. To change this threshold one can introduce a parameter r, e.g. r=095 for condensation to take place at 95% relative humidity, as follows","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"delta q = fracq_i+1 - q_iDelta t = fracrq^star(T_i) - q_i Delta t_c\nleft( 1 + fracL_vrc_p fracpartial q^starpartial T(T_i) right)","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"r is a linear scale and therefore can be taken out of the gradient fracpartial q^starpartial T in the denominator.","category":"page"},{"location":"large_scale_condensation/#Large-scale-precipitation","page":"Large-scale condensation","title":"Large-scale precipitation","text":"","category":"section"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"The tendencies delta q in units of kg/kg/s are vertically integrated to diagnose the large-scale precipitation P in units of meters","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"P = -int fracDelta tg rho delta q dp","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"with gravity g, water density rho and time step Delta t. P is therefore interpreted as the amount of precipitation that falls down during the time step Delta t of the time integration. Note that delta q is always negative due to the q q^star condition for saturation, hence P is positive only. It is then accumulated over several time steps, e.g. over the course of an hour to yield a typical rain rate of mm/h. The water density is taken as reference density of 1000kgm^3","category":"page"},{"location":"large_scale_condensation/#References","page":"Large-scale condensation","title":"References","text":"","category":"section"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"[Frierson2006]: Frierson, D. M. W., I. M. Held, and P. Zurita-Gotor, 2006: A Gray-Radiation Aquaplanet Moist GCM. Part I: Static Stability and Eddy Scale. J. Atmos. Sci., 63, 2548-2566, DOI:10.1175/JAS3753.1.","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 = (u v) 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_u F_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 u v\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 u v 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 (A B)_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 u v zeta 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 l m 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 0 1. 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_l m = -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^textexplicit n_l m = -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^textimplicit n_l m = 1 - 2Delta t D^textexplicit n_l m. 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 = (u v), 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 0 Delta 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 05 1. 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":"examples_2D/#Examples","page":"Examples 2D","title":"Examples 2D","text":"","category":"section"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"The following is a collection of example model setups, starting with an easy setup of the Barotropic vorticity equation and continuing with the shallow water model.","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"See also Examples 3D for examples with the primitive equation models.","category":"page"},{"location":"examples_2D/#2D-turbulence-on-a-non-rotating-sphere","page":"Examples 2D","title":"2D turbulence on a non-rotating sphere","text":"","category":"section"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"info: Setup script to copy and paste\nusing SpeedyWeather\nspectral_grid = SpectralGrid(trunc=63, nlayers=1)\nstill_earth = Earth(spectral_grid, rotation=0)\ninitial_conditions = StartWithRandomVorticity()\nmodel = BarotropicModel(spectral_grid; initial_conditions, planet=still_earth)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(20))","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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 nlayers=1 vertical levels. The SpectralGrid object will provide us with some more information","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=63, nlayers=1)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"Next step we create a planet that's like Earth but not rotating. As a convention, we always pass on the spectral grid object as the first argument to every other model component we create.","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"still_earth = Earth(spectral_grid, rotation=0)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"using Random # hide\nRandom.seed!(1234) # hide\ninitial_conditions = StartWithRandomVorticity()","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"Now we want to construct a BarotropicModel with these","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"model = BarotropicModel(spectral_grid; initial_conditions, planet=still_earth)\nnothing # hide","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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 a unicode plot (via UnicodePlots.jl) of the surface relative vorticity. This is just to get a quick idea of what was simulated. The resolution of the plot is not necessarily representative.","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"simulation = initialize!(model)\nrun!(simulation, period=Day(20))","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"Woohoo! Something is moving! You could pick up where this simulation stopped by simply doing run!(simulation, period=Day(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":"examples_2D/#Shallow-water-with-mountains","page":"Examples 2D","title":"Shallow water with mountains","text":"","category":"section"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"info: Setup script to copy and past\nusing SpeedyWeather\nspectral_grid = SpectralGrid(trunc=63, nlayers=1)\norography = NoOrography(spectral_grid)\ninitial_conditions = ZonalJet()\nmodel = ShallowWaterModel(spectral_grid; orography, initial_conditions)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(6))","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=63, nlayers=1)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"Now as a first simulation, we want to disable any orography, so we create a NoOrography","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"orography = NoOrography(spectral_grid)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"initial_conditions = ZonalJet()","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"model = ShallowWaterModel(spectral_grid; orography, initial_conditions)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(6))","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"run!(simulation, period=Day(6), output=true)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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. In general we can check this also via","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"id = model.output.id","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"So let's plot that data properly (and not just using UnicodePlots.jl). $id in the following just means that the string is interpolated to run_0001 if this is the first unnamed run in your folder.","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"using NCDatasets\nds = NCDataset(\"run_$id/output.nc\")\nds[\"vor\"]","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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\nusing CairoMakie\nheatmap(lon, lat, vor)\nsave(\"galewsky0.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"(Image: Galewsky jet)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"You see that in comparison the unicode plot heavily coarse-grains the simulation, well it's unicode after all! Here, we have unpacked the netCDF file using NCDatasets.jl and then plotted via heatmap(lon, lat, vor). While you can do that to give you more control on the plotting, SpeedyWeather.jl also defines an extension for Makie.jl, see Extensions. Because if our matrix vor here was an AbstractGrid (see RingGrids) then all its geographic information (which grid point is where) would be implicitly known from the type. From the netCDF file, however, you would need to use the longitude and latitude dimensions.","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"So we can also just do (input_as=Matrix here as all our grids use and expect a horizontal dimension flattened into a vector by default)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"vor_grid = FullGaussianGrid(vor, input_as=Matrix)\n\nusing CairoMakie # this will load the extension so that Makie can plot grids directly\nheatmap(vor_grid, title=\"Relative vorticity [1/s]\")\nsave(\"galewsky1.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"(Image: Galewsky jet pyplot1)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"Note that here you need to know which grid the data comes on (an error is thrown if FullGaussianGrid(vor) is not size compatible). By default the output will be on the FullGaussianGrid, but if you play around with other grids, you'd need to change this here, see NetCDF output and Output grid.","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"We did want to showcase the usage of NetCDF output here, but from now on we will use heatmap to plot data on our grids directly, without storing output first. So for our current simulation, that means at time = 12 days, vorticity on the grid is stored in the diagnostic variables and can be visualised with ([:, 1] is horizontal x vertical dimension, so all grid points on the first and only vertical layer)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"vor = simulation.diagnostic_variables.grid.vor_grid[:, 1]\nheatmap(vor, title=\"Relative vorticity [1/s]\")\nsave(\"galewsky2.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"(Image: Galewsky jet)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"orography = EarthOrography(spectral_grid)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"It will read the orography from file as shown (only at initialize!(model)), 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":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"model = ShallowWaterModel(spectral_grid; orography, initial_conditions)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(12), output=true)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"This time the run got a new run id, which you see in the progress bar, but can again 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":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"id = model.output.id","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"ds = NCDataset(\"run_$id/output.nc\")","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"You could plot the NetCDF output now as before, but we'll be plotting directly from the current state of the simulation","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"vor = simulation.diagnostic_variables.grid.vor_grid[:, 1] # 1 to index surface\nheatmap(vor, title=\"Relative vorticity [1/s]\")\nsave(\"galewsky3.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"(Image: Galewsky jet)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"Interesting! One can clearly see some imprint of the orography on vorticity and there is especially more vorticity in the southern hemisphere. 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":"examples_2D/#Polar-jet-streams-in-shallow-water","page":"Examples 2D","title":"Polar jet streams in shallow water","text":"","category":"section"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"Setup script to copy and paste:","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=63, nlayers=1)\n\nforcing = JetStreamForcing(spectral_grid, latitude=60)\ndrag = QuadraticDrag(spectral_grid)\n\nmodel = ShallowWaterModel(spectral_grid; drag, forcing)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(40))\nnothing # hide","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"We want to simulate polar jet streams in the shallow water model. We add a JetStreamForcing that adds momentum at 60˚N and 60˚S an 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. Then visualize zonal wind after 40 days with","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"using CairoMakie\n\nu = simulation.diagnostic_variables.grid.u_grid[:, 1]\nheatmap(u, title=\"Zonal wind [m/s]\")\nsave(\"polar_jets.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"(Image: Polar jets pyplot)","category":"page"},{"location":"examples_2D/#Gravity-waves-on-the-sphere","page":"Examples 2D","title":"Gravity waves on the sphere","text":"","category":"section"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"Setup script to copy and paste:","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"using Random # hide\nRandom.seed!(1234) # hide\nusing SpeedyWeather\nspectral_grid = SpectralGrid(trunc=127, nlayers=1)\n\n# model components\nimplicit = ImplicitShallowWater(spectral_grid, α=0.5)\norography = EarthOrography(spectral_grid, smoothing=false)\ninitial_conditions = RandomWaves(lmin=10, lmax=30) # between wavenumber 10 and 30\n\n# construct, initialize, run\nmodel = ShallowWaterModel(spectral_grid; orography, initial_conditions, implicit)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(2))\nnothing # hide","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"RandomWaves()","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"so that the amplitude A is as desired, here 2000m. Our layer thickness in meters is by default","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"model.atmosphere.layer_thickness","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"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":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"model.time_stepping.Δt_sec","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"seconds. Note that the gravity wave speed here is sqrtgH so almost 300m/s, given the speed of gravity waves we don't have to integrate for long. Visualise the dynamic layer thickness h = eta + H + H_b (interface displacement eta, layer thickness at rest H and orography H_b) with","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"using CairoMakie\n\nH = model.atmosphere.layer_thickness\nHb = model.orography.orography\nη = simulation.diagnostic_variables.grid.pres_grid\nh = @. η + H - Hb # @. to broadcast grid + scalar - grid\n\nheatmap(h, title=\"Dynamic layer thickness h\", colormap=:oslo)\nsave(\"gravity_waves.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"(Image: Gravity waves pyplot)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"Mountains like the Himalayas or the Andes are quite obvious because the atmospheric layer is much thinner there. The pressure gradient is relative to z=0 so in a fluid at rest the mountains would just \"reach into\" the fluid, thinning the layer the higher the mountain. As the atmosphere here is not at rest the layer thickness is not perfectly (anti-)correlated with orography but almost so.","category":"page"},{"location":"examples_2D/#References","page":"Examples 2D","title":"References","text":"","category":"section"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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":"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":"In a similar manner, other branches can be also 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 requires Julia v1.10 or later. The package is tested on Julia 1.10, and 1.11.","category":"page"},{"location":"installation/#Extensions","page":"Installation","title":"Extensions","text":"","category":"section"},{"location":"installation/","page":"Installation","title":"Installation","text":"SpeedyWeather.jl has a weak dependency on","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"Makie.jl to extend Makie.heatmap","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"This is an extension, meaning that this functionality is only loaded from SpeedyWeather when using Makie (or its backends CairoMakie.jl, GLMakie.jl, ...). Hence, if you want to make use of this extension (some Examples show this) you need to install Makie.jl manually.","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/#Creating-NetCDFOutput","page":"NetCDF output","title":"Creating NetCDFOutput","text":"","category":"section"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"using SpeedyWeather\nspectral_grid = SpectralGrid()\noutput = NetCDFOutput(spectral_grid)","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"With NetCDFOutput(::SpectralGrid, ...) one creates a NetCDFOutput writer with several options, which are explained in the following. By default, the NetCDFOutput is created when constructing the model, i.e.","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"model = ShallowWaterModel(spectral_grid)\nmodel.output","category":"page"},{"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, and they only differ in their default output.variables (e.g. the primitive models would by default output temperature which does not exist in the 2D models BarotropicModel or ShallowWaterModel). But any NetCDFOutput can be passed onto the model constructor with the output keyword argument.","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"output = NetCDFOutput(spectral_grid, Barotropic)\nmodel = ShallowWaterModel(spectral_grid, output=output)\nnothing # hide","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"Here, we created NetCDFOutput for the model class Barotropic (2nd positional argument, outputting only vorticity and velocity) but use it in the ShallowWaterModel. By default the NetCDFOutput is set to inactive, i.e. output.active is false. It is only turned on (and initialized) with run!(simulation, output=true). So you may change the NetCDFOutput as you like but only calling run!(simulation) will not trigger it as output=false is the default here.","category":"page"},{"location":"output/#Output-frequency","page":"NetCDF output","title":"Output frequency","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 =Hour(6)) like so","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"output = NetCDFOutput(spectral_grid, ShallowWater, output_dt=Hour(1))\nmodel = ShallowWaterModel(spectral_grid, output=output)\nmodel.output","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 output to the model constructor, otherwise it will not be part of your model and the default is used instead. Note that the choice of output_dt can affect the actual time step that is used for the model integration, which is explained in the following. Example, we run the model at a resolution of T42 and the time step is going to be","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"spectral_grid = SpectralGrid(trunc=42, nlayers=1)\ntime_stepping = Leapfrog(spectral_grid)\ntime_stepping.Δt_sec","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"seconds. Depending on the output frequency (we chose output_dt = Hour(1) above) this will be slightly adjusted during model initialization:","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"output = NetCDFOutput(spectral_grid, ShallowWater, output_dt=Hour(1))\nmodel = ShallowWaterModel(spectral_grid; time_stepping, output)\nsimulation = initialize!(model)\nmodel.time_stepping.Δt_sec","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"The shorter the output time step the more the model time step needs to be adjusted to match the desired output time step exactly. This is important so that for daily output at noon this does not slowly shift towards night over years of model integration. One can always disable this adjustment with","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"time_stepping = Leapfrog(spectral_grid, adjust_with_output=false)\ntime_stepping.Δt_sec","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"and a little info will be printed to explain that even though you wanted output_dt = Hour(1) you will not actually get this upon initialization:","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"model = ShallowWaterModel(spectral_grid; time_stepping, output)\nsimulation = initialize!(model)","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"The time axis of the NetCDF output will now look like","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"using NCDatasets\nrun!(simulation, period=Day(1), output=true)\nid = model.output.id\nds = NCDataset(\"run_$id/output.nc\")\nds[\"time\"][:]","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"which is a bit ugly, that's why adjust_with_output=true is the default. In that case we would have","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"time_stepping = Leapfrog(spectral_grid, adjust_with_output=true)\noutput = NetCDFOutput(spectral_grid, ShallowWater, output_dt=Hour(1))\nmodel = ShallowWaterModel(spectral_grid; time_stepping, output)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(1), output=true)\nid = model.output.id\nds = NCDataset(\"run_$id/output.nc\")\nds[\"time\"][:]","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"very neatly hourly output in the NetCDF file!","category":"page"},{"location":"output/#Output-grid","page":"NetCDF output","title":"Output 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 NetCDFOutput 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":"my_output_writer = NetCDFOutput(spectral_grid, 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 type of the grid type 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":"RingGrids.full_grid_type(OctahedralGaussianGrid)","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/#Output-variables","page":"NetCDF output","title":"Output variables","text":"","category":"section"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"One can easily add or remove variables from being output with the NetCDFOut writer. The following variables are predefined (note they are not exported so you have to prefix SpeedyWeather.)","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"using InteractiveUtils # hide\nsubtypes(SpeedyWeather.AbstractOutputVariable)","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"\"Defined\" here means that every such type contains information about a variables (long) name, its units, dimensions, any missing values and compression options. For HumidityOutput for example we have","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"SpeedyWeather.HumidityOutput()","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"You can choose name and unit as you like, e.g. SpeedyWeather.HumidityOutput(unit = \"1\") or change the compression options, e.g. SpeedyWeather.HumidityOutput(keepbits = 5) but more customisation is discussed in Customizing netCDF output.","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"We can add new output variables with add! ","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"output = NetCDFOutput(spectral_grid) # default variables\nadd!(output, SpeedyWeather.DivergenceOutput()) # output also divergence\noutput","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"If you didn't create a NetCDFOutput separately, you can also apply this directly to model, either add!(model, SpeedyWeather.DivergenceOutput()) or add!(model.output, args...), which technically also just forwards to add!(model.output.variables, args...). output.variables is a dictionary were the variable names (as Symbols) are used as keys, so output.variables[:div] just returns the SpeedyWeather.DivergenceOutput() we have just created using :div as key. With those keys one can also delete! a variable from netCDF output","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"delete!(output, :div)","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"If you change the name of an output variable, i.e. SpeedyWeather.DivergenceOutput(name=\"divergence\") the key would change accordingly to :divergence.","category":"page"},{"location":"output/#Output-path-and-identification","page":"NetCDF output","title":"Output path and 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 = NetCDFOutput(spectral_grid, 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 = NetCDFOutput(spectral_grid, 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 NetCDFOutput docstring, (also accessible via julia>?NetCDFOutput 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":"@doc NetCDFOutput","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/#Core.Type-Tuple{SpectralGrid}","page":"Function and type index","title":"Core.Type","text":"Generator function pulling the resolution information from spectral_grid.\n\n\n\n\n\n","category":"method"},{"location":"functions/#Core.Type-Union{Tuple{SpectralGrid}, Tuple{Orography}} where Orography<:SpeedyWeather.AbstractOrography","page":"Function and type index","title":"Core.Type","text":"Generator function pulling the resolution information from spectral_grid for all Orography <: AbstractOrography.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.AbstractDevice","page":"Function and type index","title":"SpeedyWeather.AbstractDevice","text":"abstract type AbstractDevice\n\nSupertype of all devices SpeedyWeather.jl can run on\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.AbstractLandSeaMask","page":"Function and type index","title":"SpeedyWeather.AbstractLandSeaMask","text":"Abstract super type for land-sea masks. Custom land-sea masks have to be defined as\n\nCustomMask{NF<:AbstractFloat, Grid<:AbstractGrid{NF}} <: AbstractLandSeaMask{NF, Grid}\n\nand need to have at least a field called mask::Grid that uses a Grid as defined by the spectral grid object, so of correct size and with the number format NF. All AbstractLandSeaMask have a convenient generator function to be used like mask = CustomMask(spectral_grid, option=argument), but you may add your own or customize by defining CustomMask(args...) which should return an instance of type CustomMask{NF, Grid} with parameters matching the spectral grid. Then the initialize function has to be extended for that new mask\n\ninitialize!(mask::CustomMask, model::PrimitiveEquation)\n\nwhich generally is used to tweak the mask.mask grid as you like, using any other options you have included in CustomMask as fields or anything else (preferrably read-only, because this is only to initialize the land-sea mask, nothing else) from model. You can for example read something from file, set some values manually, or use coordinates from model.geometry.\n\nThe land-sea mask grid is expected to have values between [0, 1] as we use a fractional mask, allowing for grid points being, e.g. quarter land and three quarters sea for 0.25 with 0 (=sea) and 1 (=land). The surface fluxes will weight proportionally the fluxes e.g. from sea and land surface temperatures. Note however, that the land-sea mask can declare grid points being (at least partially) ocean even though the sea surface temperatures aren't defined (=NaN) in that grid point. In that case, not flux is applied.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.AbstractOcean","page":"Function and type index","title":"SpeedyWeather.AbstractOcean","text":"Abstract super type for ocean models, which control the sea surface temperature and sea ice concentration as boundary conditions to a SpeedyWeather simulation. A new ocean model has to be defined as\n\nCustomOceanModel <: AbstractOcean\n\nand can have parameters like CustomOceanModel{T} and fields. They need to extend the following functions\n\nfunction initialize!(ocean_model::CustomOceanModel, model::PrimitiveEquation)\n # your code here to initialize the ocean model itself\n # you can use other fields from model, e.g. model.geometry\nend\n\nfunction initialize!( \n ocean::PrognosticVariablesOcean,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n ocean_model::CustomOceanModel,\n model::PrimitiveEquation,\n)\n # your code here to initialize the prognostic variables for the ocean\n # namely, ocean.sea_surface_temperature, ocean.sea_ice_concentration, e.g.\n # ocean.sea_surface_temperature .= 300 # 300K everywhere\nend\n\nfunction ocean_timestep!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n ocean_model::CustomOceanModel,\n model::PrimitiveEquation,\n)\n # your code here to change the progn.ocean.sea_surface_temperature and/or\n # progn.ocean.sea_ice_concentration on any timestep\nend\n\nTemperatures in ocean.seasurfacetemperature have units of Kelvin, or NaN for no ocean. Note that neither sea surface temperature, land-sea mask or orography have to agree. It is possible to have an ocean on top of a mountain. For an ocean grid-cell that is (partially) masked by the land-sea mask, its value will be (fractionally) ignored in the calculation of surface fluxes (potentially leading to a zero flux depending on land surface temperatures). For an ocean grid-cell that is NaN but not masked by the land-sea mask, its value is always ignored.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.AquaPlanet","page":"Function and type index","title":"SpeedyWeather.AquaPlanet","text":"AquaPlanet sea surface temperatures that are constant in time and longitude, but vary in latitude following a coslat². To be created like\n\nocean = AquaPlanet(spectral_grid, temp_equator=302, temp_poles=273)\n\nFields and options are\n\ntemp_equator::Any: [OPTION] Temperature on the Equator [K]\ntemp_poles::Any: [OPTION] Temperature at the poles [K]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.AquaPlanetMask","page":"Function and type index","title":"SpeedyWeather.AquaPlanetMask","text":"Land-sea mask with zero = sea everywhere.\n\nmask::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.BarotropicModel","page":"Function and type index","title":"SpeedyWeather.BarotropicModel","text":"The BarotropicModel contains all model components needed for the simulation of the barotropic vorticity equations. To be constructed like\n\nmodel = BarotropicModel(spectral_grid; kwargs...)\n\nwith spectral_grid::SpectralGrid used to initalize all non-default components passed on as keyword arguments, e.g. planet=Earth(spectral_grid). Fields, representing model components, are\n\nspectral_grid::SpectralGrid\ndevice_setup::Any\ngeometry::Any\nplanet::Any\natmosphere::Any\ncoriolis::Any\nforcing::Any\ndrag::Any\nparticle_advection::Any\ninitial_conditions::Any\nrandom_process::Any\ntime_stepping::Any\nspectral_transform::Any\nimplicit::Any\nhorizontal_diffusion::Any\noutput::Any\ncallbacks::Dict{Symbol, SpeedyWeather.AbstractCallback}\nfeedback::Any\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.BulkRichardsonDrag","page":"Function and type index","title":"SpeedyWeather.BulkRichardsonDrag","text":"Boundary layer drag coefficient from the bulk Richardson number, following Frierson, 2006, Journal of the Atmospheric Sciences.\n\nκ::Any: von Kármán constant [1]\nz₀::Any: roughness length [m]\nRi_c::Any: Critical Richardson number for stable mixing cutoff [1]\ndrag_max::Base.RefValue: Maximum drag coefficient, κ²/log(zₐ/z₀) for zₐ from reference temperature\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.CPU","page":"Function and type index","title":"SpeedyWeather.CPU","text":"CPU <: AbstractDevice\n\nIndicates that SpeedyWeather.jl runs on a single CPU \n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ClausiusClapeyron","page":"Function and type index","title":"SpeedyWeather.ClausiusClapeyron","text":"Parameters for computing saturation vapour pressure of water using the Clausis-Clapeyron equation,\n\ne(T) = e₀ * exp( -Lᵥ/Rᵥ * (1/T - 1/T₀)),\n\nwhere T is in Kelvin, Lᵥ the the latent heat of condensation and Rᵥ the gas constant of water vapour\n\ne₀::AbstractFloat: Saturation water vapour pressure at 0°C [Pa]\nT₀::AbstractFloat: 0°C in Kelvin\nLᵥ::AbstractFloat: Latent heat of condensation/vaporization of water [J/kg]\ncₚ::AbstractFloat: Specific heat at constant pressure [J/K/kg]\nR_vapour::AbstractFloat: Gas constant of water vapour [J/kg/K]\nR_dry::AbstractFloat: Gas constant of dry air [J/kg/K]\nLᵥ_Rᵥ::AbstractFloat: Latent heat of condensation divided by gas constant of water vapour [K]\nT₀⁻¹::AbstractFloat: Inverse of T₀, one over 0°C in Kelvin\nmol_ratio::AbstractFloat: Ratio of molecular masses [1] of water vapour over dry air (=Rdry/Rvapour).\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ClausiusClapeyron-Tuple{NF} where NF","page":"Function and type index","title":"SpeedyWeather.ClausiusClapeyron","text":"Functor: Saturation water vapour pressure as a function of temperature using the Clausius-Clapeyron equation,\n\ne(T) = e₀ * exp( -Lᵥ/Rᵥ * (1/T - 1/T₀)),\n\nwhere T is in Kelvin, Lᵥ the the latent heat of vaporization and Rᵥ the gas constant of water vapour, T₀ is 0˚C in Kelvin.\n\n\n\n\n\n","category":"method"},{"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::DateTime: current model time\nstart::DateTime: start time of simulation\nperiod::Second: period to integrate for, set in set_period!(::Clock, ::Dates.Period)\ntimestep_counter::Int64: Counting all time steps during simulation\nn_timesteps::Int64: number of time steps to integrate for, set in initialize!(::Clock, ::AbstractTimeStepper)\nΔt::Dates.Millisecond: Time step\n\n.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Clock-Tuple{SpeedyWeather.AbstractTimeStepper}","page":"Function and type index","title":"SpeedyWeather.Clock","text":"Clock(\n time_stepping::SpeedyWeather.AbstractTimeStepper;\n kwargs...\n) -> Clock\n\n\nCreate and initialize a clock from time_stepping\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.CloudTopOutput","page":"Function and type index","title":"SpeedyWeather.CloudTopOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"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. Most column vectors have nlayers entries, from [1] at the top to [end] at the lowermost model level at the planetary boundary layer.\n\nnlayers::Int64\nnbands_shortwave::Int64\nnbands_longwave::Int64\nij::Int64\njring::Int64\nlond::Any\nlatd::Any\nland_fraction::Any\norography::Any\nu::Any\nv::Any\ntemp::Any\nhumid::Any\nln_pres::Any\npres::Any\nu_tend::Any\nv_tend::Any\ntemp_tend::Any\nhumid_tend::Any\nflux_u_upward::Any\nflux_u_downward::Any\nflux_v_upward::Any\nflux_v_downward::Any\nflux_temp_upward::Any\nflux_temp_downward::Any\nflux_humid_upward::Any\nflux_humid_downward::Any\nrandom_value::Any\nboundary_layer_depth::Int64\nboundary_layer_drag::Any\nsurface_geopotential::Any\nsurface_u::Any\nsurface_v::Any\nsurface_temp::Any\nsurface_humid::Any\nsurface_wind_speed::Any\nskin_temperature_sea::Any\nskin_temperature_land::Any\nsoil_moisture_availability::Any\nsurface_air_density::Any\nsat_humid::Any\ndry_static_energy::Any\ntemp_virt::Any\ngeopot::Any\ncloud_top::Int64\nprecip_convection::Any\nprecip_large_scale::Any\ncos_zenith::Any\nalbedo::Any\noutgoing_longwave_radiation::Any\noutgoing_shortwave_radiation::Any\noptical_depth_shortwave::Any\noptical_depth_longwave::Any\na::Any\nb::Any\nc::Any\nd::Any\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ConstantOceanClimatology","page":"Function and type index","title":"SpeedyWeather.ConstantOceanClimatology","text":"Constant ocean climatology that reads monthly sea surface temperature fields from file, and interpolates them only for the initial conditions in time to be stored in the prognostic variables. It is therefore an ocean from climatology but without a seasonal cycle that is constant in time. To be created like\n\nocean = SeasonalOceanClimatology(spectral_grid)\n\nand the ocean time is set with initialize!(model, time=time). Fields and options are\n\npath::String: [OPTION] path to the folder containing the land-sea mask file, pkg path default\nfile::String: [OPTION] filename of sea surface temperatures\nvarname::String: [OPTION] Variable name in netcdf file\nfile_Grid::Type{<:AbstractGrid}: [OPTION] Grid the sea surface temperature file comes on\nmissing_value::Float64: [OPTION] The missing value in the data respresenting land\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ConvectiveHeating","page":"Function and type index","title":"SpeedyWeather.ConvectiveHeating","text":"Convective heating as defined by Lee and Kim, 2003, JAS implemented as convection parameterization. Fields are\n\nnlat::Int64\ntime_scale::Second: [OPTION] Qmax heating strength as 1K/timescale\np₀::Any: [OPTION] Pressure of maximum heating [hPa]\nσₚ::Any: [OPTION] Vertical extent of heating [hPa]\nθ₀::Any: [OPTION] Latitude of heating [˚N]\nσθ::Any: [OPTION] Latitudinal width of heating [˚]\nlat_mask::Vector\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ConvectivePrecipitationOutput","page":"Function and type index","title":"SpeedyWeather.ConvectivePrecipitationOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\nrate::SpeedyWeather.AbstractOutputVariable\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ConvectivePrecipitationRateOutput","page":"Function and type index","title":"SpeedyWeather.ConvectivePrecipitationRateOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.DeviceSetup","page":"Function and type index","title":"SpeedyWeather.DeviceSetup","text":"Holds information about the device the model is running on and workgroup size. \n\ndevice::SpeedyWeather.AbstractDevice: ::AbstractDevice, device the model is running on.\ndevice_KA::Any: ::KernelAbstractions.Device, device for use with KernelAbstractions\nn::Int64: workgroup size\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.DiagnosticVariables","page":"Function and type index","title":"SpeedyWeather.DiagnosticVariables","text":"All diagnostic variables.\n\ntrunc::Int64: Spectral resolution: Max degree of spherical harmonics (0-based)\nnlat_half::Int64: Grid resoltion: Number of latitude rings on one hemisphere (Equator incl.)\nnlayers::Int64: Number of vertical layers\nnparticles::Int64: Number of particles for particle advection\ntendencies::Tendencies: Tendencies (spectral and grid) of the prognostic variables\ngrid::GridVariables: Gridded prognostic variables\ndynamics::DynamicsVariables: Intermediate variables for the dynamical core\nphysics::PhysicsVariables: Global fields returned from physics parameterizations\nparticles::ParticleVariables{NF, ArrayType, ParticleVector, VectorType, Grid} where {NF, ArrayType, Grid, ParticleVector, VectorType}: Intermediate variables for the particle advection\ncolumn::ColumnVariables: Vertical column for the physics parameterizations\ntemp_average::Any: Average temperature of every horizontal layer [K]\nscale::Base.RefValue: Scale applied to vorticity and divergence\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.DiagnosticVariables-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.DiagnosticVariables","text":"DiagnosticVariables(\n SG::SpectralGrid;\n nbands_shortwave,\n nbands_longwave\n) -> DiagnosticVariables\n\n\nGenerator function.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.DivergenceOutput","page":"Function and type index","title":"SpeedyWeather.DivergenceOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.DryBettsMiller","page":"Function and type index","title":"SpeedyWeather.DryBettsMiller","text":"The simplified Betts-Miller convection scheme from Frierson, 2007, https://doi.org/10.1175/JAS3935.1 but with humidity set to zero. Fields and options are\n\nnlayers::Int64: number of vertical layers/levels\ntime_scale::Second: [OPTION] Relaxation time for profile adjustment\nsurface_temp::Any: [OPTION] Surface perturbation of temp to calculate the dry adiabat\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.DynamicsVariables","page":"Function and type index","title":"SpeedyWeather.DynamicsVariables","text":"Intermediate quantities for the dynamics of a given layer.\n\ntrunc::Int64\nnlat_half::Int64\nnlayers::Int64\na::Any: Multi-purpose a, 3D work array to be reused in various places\nb::Any: Multi-purpose b, 3D work array to be reused in various places\na_grid::Any: Multi-purpose a, 3D work array to be reused in various places\nb_grid::Any: Multi-purpose b, 3D work array to be reused in various places\na_2D::Any: Multi-purpose a, work array to be reused in various places\nb_2D::Any: Multi-purpose b, work array to be reused in various places\na_2D_grid::Any: Multi-purpose a, work array to be reused in various places\nb_2D_grid::Any: Multi-purpose b, work array to be reused in various places\nuv∇lnp::Any: Pressure flux (uₖ, vₖ)⋅∇ln(pₛ)\nuv∇lnp_sum_above::Any: Sum of Δσₖ-weighted uv∇lnp above\ndiv_sum_above::Any: Sum of div_weighted from top to k\ntemp_virt::Any: Virtual temperature [K], spectral for geopotential\ngeopot::Any: Geopotential [m²/s²] on full layers\nσ_tend::Any: Vertical velocity (dσ/dt), on half levels k+1/2 below, pointing to the surface (σ=1)\n∇lnp_x::Any: Zonal gradient of log surf pressure\n∇lnp_y::Any: Meridional gradient of log surf pressure\nu_mean_grid::Any: Vertical average of zonal velocity [m/s]\nv_mean_grid::Any: Vertical average of meridional velocity [m/s]\ndiv_mean_grid::Any: Vertical average of divergence [1/s], grid\ndiv_mean::Any: Vertical average of divergence [1/s], spectral\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.DynamicsVariables-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.DynamicsVariables","text":"DynamicsVariables(\n SG::SpectralGrid\n) -> DynamicsVariables{<:AbstractFloat, <:AbstractArray, <:AbstractArray, <:AbstractArray, <:AbstractArray, <:AbstractArray}\n\n\nGenerator function.\n\n\n\n\n\n","category":"method"},{"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::AbstractFloat: angular frequency of Earth's rotation [rad/s]\ngravity::AbstractFloat: gravitational acceleration [m/s^2]\ndaily_cycle::Bool: switch on/off daily cycle\nlength_of_day::Second: Seconds in a daily rotation\nseasonal_cycle::Bool: switch on/off seasonal cycle\nlength_of_year::Second: Seconds in an orbit around the sun\nequinox::DateTime: time of spring equinox (year irrelevant)\naxial_tilt::AbstractFloat: angle [˚] rotation axis tilt wrt to orbit\nsolar_constant::AbstractFloat: Total solar irradiance at the distance of 1 AU [W/m²]\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 <: AbstractAtmosphere, with the following physical/chemical characteristics. Keyword arguments are\n\nmol_mass_dry_air::AbstractFloat: molar mass of dry air [g/mol]\nmol_mass_vapour::AbstractFloat: molar mass of water vapour [g/mol]\nheat_capacity::AbstractFloat: specific heat at constant pressure cₚ [J/K/kg]\nR_gas::AbstractFloat: universal gas constant [J/K/mol]\nR_dry::AbstractFloat: specific gas constant for dry air [J/kg/K]\nR_vapour::AbstractFloat: specific gas constant for water vapour [J/kg/K]\nmol_ratio::AbstractFloat: Ratio of gas constants: dry air / water vapour, often called ε [1]\nμ_virt_temp::AbstractFloat: Virtual temperature Tᵥ calculation, Tᵥ = T(1 + μ*q), humidity q, absolute tempereature T\nκ::AbstractFloat: = R_dry/cₚ, gas const for air over heat capacity\nwater_density::AbstractFloat: water density [kg/m³]\nlatent_heat_condensation::AbstractFloat: latent heat of condensation [J/kg] for consistency with specific humidity [kg/kg]\nlatent_heat_sublimation::AbstractFloat: latent heat of sublimation [J/kg]\nstefan_boltzmann::AbstractFloat: stefan-Boltzmann constant [W/m²/K⁴]\npres_ref::AbstractFloat: surface reference pressure [Pa]\ntemp_ref::AbstractFloat: surface reference temperature [K]\nmoist_lapse_rate::AbstractFloat: reference moist-adiabatic temperature lapse rate [K/m]\ndry_lapse_rate::AbstractFloat: reference dry-adiabatic temperature lapse rate [K/m]\nlayer_thickness::AbstractFloat: layer thickness for the shallow water model [m]\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{<: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_fraction::Float64: fraction of highest wavenumbers to smooth\norography::AbstractGrid{NF} where NF<:AbstractFloat: height [m] on grid-point space.\ngeopot_surf::LowerTriangularArray{Complex{NF}, 1, Array{Complex{NF}, 1}} where NF<:AbstractFloat: surface geopotential, height*gravity [m²/s²]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Feedback","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?, default is isinteractive(), true in interactive REPL mode\ndebug::Bool: check for NaRs in the prognostic variables\noutput::Bool: write a progress.txt file? State synced with NetCDFOutput.output\nid::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.GPU","page":"Function and type index","title":"SpeedyWeather.GPU","text":"GPU <: AbstractDevice\n\nIndicates that SpeedyWeather.jl runs on a single GPU\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\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\nnlayers::Int64: number of vertical levels\nnpoints::Int64: total number of horizontal grid points\nradius::AbstractFloat: Planet's radius [m]\ncolat::Vector{Float64}: array of colatitudes in radians (0...π)\nlat::Vector{NF} where NF<:AbstractFloat: array of latitudes in radians (π...-π)\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 (0˚...360˚) for each grid point in ring order\nlatds::Vector{NF} where NF<:AbstractFloat: latitude (-90˚...˚90) for each grid point in ring order\nlons::Vector{NF} where NF<:AbstractFloat: longitude (0...2π) for each grid point in ring order\nlats::Vector{NF} where NF<:AbstractFloat: latitude (-π/2...π/2) 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\nfull_to_half_interpolation::Vector{NF} where NF<:AbstractFloat: Full to half levels interpolation\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Geometry-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.Geometry","text":"Geometry(SG::SpectralGrid) -> Geometry\n\n\nGenerator function for Geometry struct based on spectral_grid.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.GlobalSurfaceTemperatureCallback","page":"Function and type index","title":"SpeedyWeather.GlobalSurfaceTemperatureCallback","text":"Callback that records the global mean surface temperature on every time step\n\ntimestep_counter::Int64\ntemp::Vector\n\n.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.GridVariables","page":"Function and type index","title":"SpeedyWeather.GridVariables","text":"Transformed prognostic variables (and u, v, temp_virt) into grid-point space.\n\nnlat_half::Int64\nnlayers::Int64\nvor_grid::Any: Relative vorticity of the horizontal wind [1/s]\ndiv_grid::Any: Divergence of the horizontal wind [1/s]\ntemp_grid::Any: Absolute temperature [K]\ntemp_virt_grid::Any: Virtual tempereature [K]\nhumid_grid::Any: Specific_humidity [kg/kg]\nu_grid::Any: Zonal velocity [m/s]\nv_grid::Any: Meridional velocity [m/s]\npres_grid::Any: Logarithm of surface pressure [Pa]\nrandom_pattern::Any: Random pattern controlled by random process [1]\ntemp_grid_prev::Any: Absolute temperature [K] at previous time step\nhumid_grid_prev::Any: Specific humidity [kg/kg] at previous time step\nu_grid_prev::Any: Zonal velocity [m/s] at previous time step\nv_grid_prev::Any: Meridional velocity [m/s] at previous time step\npres_grid_prev::Any: Logarithm of surface pressure [Pa] at previous time step\n\n.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.GridVariables-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.GridVariables","text":"GridVariables(\n SG::SpectralGrid\n) -> GridVariables{<:AbstractFloat, <:AbstractArray, <:AbstractArray, <:AbstractArray}\n\n\nGenerator function.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.HeldSuarez","page":"Function and type index","title":"SpeedyWeather.HeldSuarez","text":"Temperature relaxation from Held and Suarez, 1996 BAMS\n\nnlat::Int64: number of latitude rings\nnlayers::Int64: number of vertical levels\nσb::AbstractFloat: sigma coordinate below which faster surface relaxation is applied\nrelax_time_slow::Second: time scale for slow global relaxation\nrelax_time_fast::Second: time scale for faster tropical surface relaxation\nTmin::AbstractFloat: minimum equilibrium temperature [K]\nTmax::AbstractFloat: maximum equilibrium temperature [K]\nΔTy::AbstractFloat: meridional temperature gradient [K]\nΔθz::AbstractFloat: 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...) -> HeldSuarez\n\n\ncreate a HeldSuarez temperature relaxation with arrays allocated given spectral_grid\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.HumidityOutput","page":"Function and type index","title":"SpeedyWeather.HumidityOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.HyperDiffusion","page":"Function and type index","title":"SpeedyWeather.HyperDiffusion","text":"Horizontal hyper diffusion of vor, div, temp, humid; implicitly in spectral space with a power of the Laplacian (default = 4) and the strength controlled by time_scale (default = 1 hour). For vorticity and divergence, by default, the time_scale (=1/strength of diffusion) is reduced with increasing resolution through resolution_scaling and the power is linearly decreased in the vertical above the tapering_σ sigma level to power_stratosphere (default 2). \n\nFor the BarotropicModel and ShallowWaterModel no tapering or scaling is applied. Fields and options are\n\ntrunc::Int64: spectral resolution\nnlayers::Int64: number of vertical levels\npower::Any: [OPTION] power of Laplacian\ntime_scale::Second: [OPTION] diffusion time scale\ntime_scale_div::Second: [OPTION] diffusion time scale for temperature and humidity\nresolution_scaling::Any: [OPTION] stronger diffusion with resolution? 0: constant with trunc, 1: (inverse) linear with trunc, etc\npower_stratosphere::Any: [OPTION] different power for tropopause/stratosphere\ntapering_σ::Any: [OPTION] linearly scale towards power_stratosphere above this σ\nexpl::Any\nimpl::Any\nexpl_div::Any\nimpl_div::Any\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) -> HyperDiffusion{<:AbstractFloat}\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.ImplicitCondensation","page":"Function and type index","title":"SpeedyWeather.ImplicitCondensation","text":"Large scale condensation as with implicit precipitation.\n\nrelative_humidity_threshold::AbstractFloat: Relative humidity threshold [1 = 100%] to trigger condensation\ntime_scale::AbstractFloat: Time scale in multiples of time step Δt, the larger the less immediate\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ImplicitPrimitiveEquation","page":"Function and type index","title":"SpeedyWeather.ImplicitPrimitiveEquation","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\nnlayers::Int64: number of vertical layers\nα::AbstractFloat: 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 on first time step\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.ImplicitPrimitiveEquation-Tuple{SpectralGrid, Vararg{Any}}","page":"Function and type index","title":"SpeedyWeather.ImplicitPrimitiveEquation","text":"ImplicitPrimitiveEquation(\n spectral_grid::SpectralGrid,\n kwargs...\n) -> ImplicitPrimitiveEquation\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α::AbstractFloat: [OPTION] coefficient for semi-implicit computations to filter gravity waves, 0.5 <= α <= 1\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) -> ImplicitShallowWater\n\n\nGenerator using the resolution from spectral_grid.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.InterfaceDisplacementOutput","page":"Function and type index","title":"SpeedyWeather.InterfaceDisplacementOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"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(\n SG::SpectralGrid;\n kwargs...\n) -> JablonowskiRelaxation\n\n\ncreate a JablonowskiRelaxation temperature relaxation with arrays allocated given spectral_grid\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.JablonowskiTemperature","page":"Function and type index","title":"SpeedyWeather.JablonowskiTemperature","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\nσ_tropopause::Float64: Sigma coordinates of the tropopause [1]\nu₀::Float64: max amplitude of zonal wind [m/s]\nΔT::Float64: temperature difference used for stratospheric lapse rate [K], Jablonowski uses ΔT = 4.8e5 [K]\nTmin::Float64: minimum temperature [K] of profile\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.JeevanjeeRadiation","page":"Function and type index","title":"SpeedyWeather.JeevanjeeRadiation","text":"Temperature flux longwave radiation from Jeevanjee and Romps, 2018, following Seeley and Wordsworth, 2023, eq (1)\n\ndF/dT = α*(T_t - T)\n\nwith F the upward temperature flux between two layers with temperature difference dT, α = 0.025 W/m²/K² and T_t = 200K a prescribed tropopause temperature. Flux into the lowermost layer is 0. Flux out of uppermost layer also 0, but dT/dt = (T_t - T)/τ is added to relax the uppermost layer towards the tropopause temperature T_t with time scale τ = 24h (Seeley and Wordsworth, 2023 use 6h, which is unstable a low resolutions here). Fields are\n\nα::Any: Radiative forcing constant (W/m²/K²)\ntemp_tropopause::Any: Tropopause temperature [K]\ntime_scale::Second: Tropopause relaxation time scale to temp_tropopause\n\n\n\n\n\n","category":"type"},{"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\nnlayers::Int64: Number of vertical layers\nlatitude::Any: jet latitude [˚N]\nwidth::Any: jet width [˚], default ≈ 19.29˚\nsigma::Any: sigma level [1], vertical location of jet\nspeed::Any: jet speed scale [m/s]\ntime_scale::Second: time scale [days]\namplitude::Vector: precomputed amplitude vector [m/s²]\ntapering::Vector: precomputed vertical tapering\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{<:AbstractGrid}: Grid the land-sea mask file comes on\nmask::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.LargeScalePrecipitationOutput","page":"Function and type index","title":"SpeedyWeather.LargeScalePrecipitationOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\nrate::SpeedyWeather.AbstractOutputVariable\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.LargeScalePrecipitationRateOutput","page":"Function and type index","title":"SpeedyWeather.LargeScalePrecipitationRateOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"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)\nnsteps::Int64: Number of timesteps stored simultaneously in prognostic variables\nΔt_at_T31::Second: Time step in minutes for T31, scale linearly to trunc\nradius::AbstractFloat: Radius of sphere [m], used for scaling\nadjust_with_output::Bool: Adjust ΔtatT31 with the outputdt to reach outputdt exactly in integer time steps\nrobert_filter::AbstractFloat: Robert (1966) time filter coefficeint to suppress comput. mode\nwilliams_filter::AbstractFloat: Williams time filter (Amezcua 2011) coefficient for 3rd order acc\nΔt_millisec::Dates.Millisecond: time step Δt [ms] at specified resolution\nΔt_sec::AbstractFloat: time step Δt [s] at specified resolution\nΔt::AbstractFloat: time step Δt [s/m] at specified resolution, scaled by 1/radius\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...) -> Leapfrog\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\nnlayers::Int64\nσb::AbstractFloat\ntime_scale::Second\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...) -> LinearDrag\n\n\nGenerator function using nlayers from SG::SpectralGrid\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.MeridionalVelocityOutput","page":"Function and type index","title":"SpeedyWeather.MeridionalVelocityOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.NetCDFOutput","page":"Function and type index","title":"SpeedyWeather.NetCDFOutput","text":"NetCDFOutput(\n S::SpectralGrid;\n ...\n) -> NetCDFOutput{_A, _B, Interpolator} where {_A, _B, Interpolator<:(AnvilInterpolator{Float32})}\nNetCDFOutput(\n S::SpectralGrid,\n Model::Type{<:AbstractModel};\n output_Grid,\n nlat_half,\n output_NF,\n output_dt,\n kwargs...\n) -> NetCDFOutput{_A, _B, Interpolator} where {_A, _B, Interpolator<:(AnvilInterpolator{Float32})}\n\n\nConstructor for NetCDFOutput based on S::SpectralGrid and optionally the Model type (e.g. ShallowWater, PrimitiveWet) as second positional argument. The output grid is optionally determined by keyword arguments output_Grid (its type, full grid required), nlat_half (resolution) and output_NF (number format). By default, uses the full grid equivalent of the grid and resolution used in SpectralGrid S.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.NetCDFOutput-2","page":"Function and type index","title":"SpeedyWeather.NetCDFOutput","text":"Output writer for a netCDF file with (re-)gridded variables. Interpolates non-rectangular grids. Fields are\n\nactive::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::DateTime\noutput_dt::Second: [OPTION] output frequency, time step\nvariables::Dict{Symbol, SpeedyWeather.AbstractOutputVariable}: [OPTION] dictionary of variables to output, e.g. u, v, vor, div, pres, temp, humid\noutput_every_n_steps::Int64\ntimestep_counter::Int64\noutput_counter::Int64\nnetcdf_file::Union{Nothing, NCDatasets.NCDataset}\ninterpolator::Any\ngrid2D::Any\ngrid3D::Any\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.NoCallback","page":"Function and type index","title":"SpeedyWeather.NoCallback","text":"Dummy callback that doesn't do anything.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.NoConvection","page":"Function and type index","title":"SpeedyWeather.NoConvection","text":"Dummy type to disable convection.\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::AbstractGrid{NF} where NF<:AbstractFloat: height [m] on grid-point space.\ngeopot_surf::LowerTriangularArray{Complex{NF}, 1, Array{Complex{NF}, 1}} where NF<:AbstractFloat: surface geopotential, height*gravity [m²/s²]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.NoOutputVariable","page":"Function and type index","title":"SpeedyWeather.NoOutputVariable","text":"Dummy output variable that doesn't do anything.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.NoRandomProcess","page":"Function and type index","title":"SpeedyWeather.NoRandomProcess","text":"Dummy type for no random process.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.NoSurfacePerturbation","page":"Function and type index","title":"SpeedyWeather.NoSurfacePerturbation","text":"Returns the surface temperature and humidity without perturbation from the lowermost layer.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.NoSurfacePerturbation-Tuple{ColumnVariables, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.NoSurfacePerturbation","text":"Returns the surface temperature and humidity without perturbation from the lowermost layer. Used as a functor.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.OrographyOutput","page":"Function and type index","title":"SpeedyWeather.OrographyOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.OutgoingLongwaveRadiationOutput","page":"Function and type index","title":"SpeedyWeather.OutgoingLongwaveRadiationOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.OutgoingShortwaveRadiationOutput","page":"Function and type index","title":"SpeedyWeather.OutgoingShortwaveRadiationOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Particle","page":"Function and type index","title":"SpeedyWeather.Particle","text":"Particle with location lon (longitude), lat (latitude) and σ (vertical coordinate). Longitude is assumed to be in [0,360˚E), latitude in [-90˚,90˚N] and σ in [0,1] but not strictly enforced at creation, see mod(::Particle) and ismod(::Particle). A particle is either active or inactive, determined by the Boolean in it's 2nd type parameter. By default, a particle is active, of number format DEFAULT_NF and at 0˚N, 0˚E, σ=0.\n\nlon::AbstractFloat: longitude in [0,360˚]\nlat::AbstractFloat: latitude [-90˚,90˚]\nσ::AbstractFloat: vertical sigma coordinate [0 (top) to 1 (surface)]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ParticleTracker","page":"Function and type index","title":"SpeedyWeather.ParticleTracker","text":"A ParticleTracker is implemented as a callback to output the trajectories of particles from particle advection. To be added like\n\nadd!(model.callbacks, ParticleTracker(spectral_grid; kwargs...))\n\nOutput done via netCDF. Fields and options are\n\nschedule::Schedule: [OPTION] when to schedule particle tracking\nfile_name::String: [OPTION] File name for netCDF file\ncompression_level::Int64: [OPTION] lossless compression level; 1=low but fast, 9=high but slow\nshuffle::Bool: [OPTION] shuffle/bittranspose filter for compression\nkeepbits::Int64: [OPTION] mantissa bits to keep, (14, 15, 16) means at least (2km, 1km, 500m) accurate locations\nnparticles::Int64: Number of particles to track\nnetcdf_file::Union{Nothing, NCDatasets.NCDataset}: The netcdf file to be written into, will be created at initialization\nlon::Vector\nlat::Vector\nσ::Vector\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ParticleVariables","page":"Function and type index","title":"SpeedyWeather.ParticleVariables","text":"Diagnostic variables for the particle advection\n\nnparticles::Int64: Number of particles\nnlat_half::Int64: Number of latitudes on one hemisphere (Eq. incld.), resolution parameter of Grid\nlocations::Any: Work array: particle locations\nu::Any: Work array: velocity u\nv::Any: Work array: velocity v\nσ_tend::Any: Work array: velocity w = dσ/dt\ninterpolator::AnvilInterpolator{NF, Grid} where {NF, Grid}: Interpolator to interpolate velocity fields onto particle positions\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ParticleVariables-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.ParticleVariables","text":"ParticleVariables(\n SG::SpectralGrid\n) -> ParticleVariables{var\"#s252\", var\"#s251\", var\"#s146\", _A, <:AbstractGrid} where {var\"#s252\"<:AbstractFloat, var\"#s251\"<:AbstractArray, var\"#s146\"<:AbstractArray, _A}\n\n\nGenerator function.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.PhysicsVariables","page":"Function and type index","title":"SpeedyWeather.PhysicsVariables","text":"Diagnostic variables of the physical parameterizations.\n\nnlat_half::Int64\nprecip_large_scale::Any: Accumulated large-scale precipitation [m]\nprecip_convection::Any: Accumulated large-scale precipitation [m]\ncloud_top::Any: Cloud top [m]\nsoil_moisture_availability::Any: Availability of soil moisture to evaporation [1]\nsurface_flux_heat::Any: Surface flux of heat [W/m^2]\nsurface_flux_humid::Any: Surface flux of humidity [?]\noutgoing_shortwave_radiation::Any: Outgoing shortwave radiation [W/m^2]\noutgoing_longwave_radiation::Any: Outgoing longwave radiation [W/m^2]\ncos_zenith::Any: Cosine of solar zenith angle [1]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.PhysicsVariables-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.PhysicsVariables","text":"PhysicsVariables(\n SG::SpectralGrid\n) -> PhysicsVariables{<:AbstractFloat, <:AbstractArray, <:AbstractArray}\n\n\nGenerator function.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.PrimitiveDryModel","page":"Function and type index","title":"SpeedyWeather.PrimitiveDryModel","text":"The PrimitiveDryModel contains all model components (themselves structs) needed for the simulation of the primitive equations without humidity. To be constructed like\n\nmodel = PrimitiveDryModel(spectral_grid; kwargs...)\n\nwith spectral_grid::SpectralGrid used to initalize all non-default components passed on as keyword arguments, e.g. planet=Earth(spectral_grid). Fields, representing model components, are\n\nspectral_grid::SpectralGrid\ndevice_setup::Any\ndynamics::Bool\ngeometry::Any\nplanet::Any\natmosphere::Any\ncoriolis::Any\ngeopotential::Any\nadiabatic_conversion::Any\nparticle_advection::Any\ninitial_conditions::Any\nrandom_process::Any\nforcing::Any\ndrag::Any\norography::Any\nland_sea_mask::Any\nocean::Any\nland::Any\nsolar_zenith::Any\nalbedo::Any\nphysics::Bool\nboundary_layer_drag::Any\ntemperature_relaxation::Any\nvertical_diffusion::Any\nsurface_thermodynamics::Any\nsurface_wind::Any\nsurface_heat_flux::Any\nconvection::Any\noptical_depth::Any\nshortwave_radiation::Any\nlongwave_radiation::Any\nstochastic_physics::Any\ntime_stepping::Any\nspectral_transform::Any\nimplicit::Any\nhorizontal_diffusion::Any\nvertical_advection::Any\noutput::Any\ncallbacks::Dict{Symbol, SpeedyWeather.AbstractCallback}\nfeedback::Any\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.PrimitiveWetModel","page":"Function and type index","title":"SpeedyWeather.PrimitiveWetModel","text":"The PrimitiveWetModel contains all model components (themselves structs) needed for the simulation of the primitive equations with humidity. To be constructed like\n\nmodel = PrimitiveWetModel(spectral_grid; kwargs...)\n\nwith spectral_grid::SpectralGrid used to initalize all non-default components passed on as keyword arguments, e.g. planet=Earth(spectral_grid). Fields, representing model components, are\n\nspectral_grid::SpectralGrid\ndevice_setup::Any\ndynamics::Bool\ngeometry::Any\nplanet::Any\natmosphere::Any\ncoriolis::Any\ngeopotential::Any\nadiabatic_conversion::Any\nparticle_advection::Any\ninitial_conditions::Any\nrandom_process::Any\nforcing::Any\ndrag::Any\norography::Any\nland_sea_mask::Any\nocean::Any\nland::Any\nsolar_zenith::Any\nalbedo::Any\nsoil::Any\nvegetation::Any\nphysics::Bool\nclausius_clapeyron::Any\nboundary_layer_drag::Any\ntemperature_relaxation::Any\nvertical_diffusion::Any\nsurface_thermodynamics::Any\nsurface_wind::Any\nsurface_heat_flux::Any\nsurface_evaporation::Any\nlarge_scale_condensation::Any\nconvection::Any\noptical_depth::Any\nshortwave_radiation::Any\nlongwave_radiation::Any\nstochastic_physics::Any\ntime_stepping::Any\nspectral_transform::Any\nimplicit::Any\nhorizontal_diffusion::Any\nvertical_advection::Any\nhole_filling::Any\noutput::Any\ncallbacks::Dict{Symbol, SpeedyWeather.AbstractCallback}\nfeedback::Any\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.PrognosticVariables-Tuple{SpectralGrid, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.PrognosticVariables","text":"PrognosticVariables(\n SG::SpectralGrid,\n model::AbstractModel\n) -> PrognosticVariables{var\"#s252\", var\"#s251\", _A, <:AbstractArray, <:AbstractArray, <:AbstractArray, <:AbstractArray} where {var\"#s252\"<:AbstractFloat, var\"#s251\"<:AbstractArray, _A}\n\n\nGenerator function.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.PrognosticVariables-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.PrognosticVariables","text":"PrognosticVariables(\n SG::SpectralGrid;\n nsteps\n) -> PrognosticVariables{var\"#s252\", var\"#s251\", _A, <:AbstractArray, <:AbstractArray, <:AbstractArray, <:AbstractArray} where {var\"#s252\"<:AbstractFloat, var\"#s251\"<:AbstractArray, _A}\n\n\nGenerator function.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.RandomPatternOutput","page":"Function and type index","title":"SpeedyWeather.RandomPatternOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\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.RossbyHaurwitzWave","page":"Function and type index","title":"SpeedyWeather.RossbyHaurwitzWave","text":"Rossby-Haurwitz wave initial conditions as in Williamson et al. 1992, J Computational Physics with an additional cut-off amplitude c to filter out tiny harmonics in the vorticity field. Parameters are \n\nm::Int64\nω::Float64\nK::Float64\nc::Float64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Schedule","page":"Function and type index","title":"SpeedyWeather.Schedule","text":"A schedule for callbacks, to execute them periodically after a given period has passed (first timestep excluded) or on/after specific times (initial time excluded). For two consecutive time steps i, i+1, an event is scheduled at i+1 when it occurs in (i,i+1]. Similarly, a periodic schedule of period p will be executed at start+p, but p is rounded to match the multiple of the model timestep. Periodic schedules and event schedules can be combined, executing at both. A Schedule is supposed to be added into callbacks as fields\n\nBase.@kwdef struct MyCallback\n schedule::Schedule = Schedule(every=Day(1))\n other_fields\nend\n\nsee also initialize!(::Schedule,::Clock) and isscheduled(::Schedule,::Clock). Fields\n\nevery::Second: [OPTION] Execute every time period, first timestep excluded. Default=never.\ntimes::Vector{DateTime}: [OPTION] Events scheduled at times\nschedule::BitVector: Actual schedule, true=execute this timestep, false=don't\nsteps::Int64: Number of scheduled executions\ncounter::Int64: Count up the number of executions\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Schedule-Tuple{Vararg{DateTime}}","page":"Function and type index","title":"SpeedyWeather.Schedule","text":"Schedule(times::DateTime...) -> Schedule\n\n\nA Schedule based on DateTime arguments, For two consecutive time steps i, i+1, an event is scheduled at i+1 when it occurs in (i,i+1].\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SeaSurfaceTemperatureOutput","page":"Function and type index","title":"SpeedyWeather.SeaSurfaceTemperatureOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SeasonalOceanClimatology","page":"Function and type index","title":"SpeedyWeather.SeasonalOceanClimatology","text":"Seasonal ocean climatology that reads monthly sea surface temperature fields from file, and interpolates them in time regularly (default every 3 days) to be stored in the prognostic variables. Fields and options are\n\nnlat_half::Int64: number of latitudes on one hemisphere, Equator included\npath::String: [OPTION] Path to the folder containing the sea surface temperatures, pkg path default\nfile::String: [OPTION] Filename of sea surface temperatures\nvarname::String: [OPTION] Variable name in netcdf file\nfile_Grid::Type{<:AbstractGrid}: [OPTION] Grid the sea surface temperature file comes on\nmissing_value::Any: [OPTION] The missing value in the data respresenting land\nmonthly_temperature::Vector{Grid} where {NF, Grid<:AbstractGrid{NF}}: Monthly sea surface temperatures [K], interpolated onto Grid\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ShallowWaterModel","page":"Function and type index","title":"SpeedyWeather.ShallowWaterModel","text":"The ShallowWaterModel contains all model components needed for the simulation of the shallow water equations. To be constructed like\n\nmodel = ShallowWaterModel(spectral_grid; kwargs...)\n\nwith spectral_grid::SpectralGrid used to initalize all non-default components passed on as keyword arguments, e.g. planet=Earth(spectral_grid). Fields, representing model components, are\n\nspectral_grid::SpectralGrid\ndevice_setup::Any\ngeometry::Any\nplanet::Any\natmosphere::Any\ncoriolis::Any\norography::Any\nforcing::Any\ndrag::Any\nparticle_advection::Any\ninitial_conditions::Any\nrandom_process::Any\ntime_stepping::Any\nspectral_transform::Any\nimplicit::Any\nhorizontal_diffusion::Any\noutput::Any\ncallbacks::Dict{Symbol, SpeedyWeather.AbstractCallback}\nfeedback::Any\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SimplifiedBettsMiller","page":"Function and type index","title":"SpeedyWeather.SimplifiedBettsMiller","text":"The simplified Betts-Miller convection scheme from Frierson, 2007, https://doi.org/10.1175/JAS3935.1. This implements the qref-formulation in their paper. Fields and options are\n\nnlayers::Int64: number of vertical layers\ntime_scale::Second: [OPTION] Relaxation time for profile adjustment\nrelative_humidity::Any: [OPTION] Relative humidity for reference profile\nsurface_temp_humid::Any: [OPTION] Surface perturbation of temp, humid to calculate the moist pseudo adiabat\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::AbstractModel: all parameters, constant at runtime\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SinSolarDeclination","page":"Function and type index","title":"SpeedyWeather.SinSolarDeclination","text":"Coefficients to calculate the solar declination angle δ [radians] based on a simple sine function, with Earth's axial tilt as amplitude, equinox as phase shift.\n\naxial_tilt::Any\nequinox::DateTime\nlength_of_year::Second\nlength_of_day::Second\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SinSolarDeclination-Tuple{NF} where NF","page":"Function and type index","title":"SpeedyWeather.SinSolarDeclination","text":"SinSolarDeclination functor, computing the solar declination angle of angular fraction of year g [radians] using the coefficients of the SinSolarDeclination struct.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SinSolarDeclination-Tuple{SpectralGrid, SpeedyWeather.AbstractPlanet}","page":"Function and type index","title":"SpeedyWeather.SinSolarDeclination","text":"Generator function using the planet's orbital parameters to adapt the solar declination calculation.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SinSolarDeclination-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.SinSolarDeclination","text":"Generator function pulling the number format NF from a SpectralGrid.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SolarDeclination","page":"Function and type index","title":"SpeedyWeather.SolarDeclination","text":"Coefficients to calculate the solar declination angle δ from\n\nδ = 0.006918 - 0.399912*cos(g) + 0.070257*sin(g)\n - 0.006758*cos(2g) + 0.000907*sin(2g)\n - 0.002697*cos(3g) + 0.001480*sin(3g)\n\nwith g the angular fraction of the year in radians. Following Spencer 1971, Fourier series representation of the position of the sun. Search 2(5):172.\n\na::AbstractFloat\ns1::AbstractFloat\nc1::AbstractFloat\ns2::AbstractFloat\nc2::AbstractFloat\ns3::AbstractFloat\nc3::AbstractFloat\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SolarDeclination-Tuple{Any}","page":"Function and type index","title":"SpeedyWeather.SolarDeclination","text":"SolarDeclination functor, computing the solar declination angle of angular fraction of year g [radians] using the coefficients of the SolarDeclination struct.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SolarDeclination-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.SolarDeclination","text":"Generator function pulling the number format NF from a SpectralGrid.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SolarTimeCorrection","page":"Function and type index","title":"SpeedyWeather.SolarTimeCorrection","text":"Coefficients for the solar time correction (also called Equation of time) which adjusts the solar hour to an oscillation of sunrise/set by about +-16min throughout the year.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SolarTimeCorrection-Tuple{Any}","page":"Function and type index","title":"SpeedyWeather.SolarTimeCorrection","text":"Functor that returns the time correction for a angular fraction of the year g [radians], so that g=0 for Jan-01 and g=2π for Dec-31.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SolarZenith","page":"Function and type index","title":"SpeedyWeather.SolarZenith","text":"Solar zenith angle varying with daily and seasonal cycle.\n\nlength_of_day::Second\nlength_of_year::Second\nequation_of_time::Bool\nseasonal_cycle::Bool\nsolar_declination::SpeedyWeather.SinSolarDeclination{NF} where NF<:AbstractFloat\ntime_correction::SpeedyWeather.SolarTimeCorrection\ninitial_time::Base.RefValue{DateTime}\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SolarZenithSeason","page":"Function and type index","title":"SpeedyWeather.SolarZenithSeason","text":"Solar zenith angle varying with seasonal cycle only.\n\nlength_of_day::Second\nlength_of_year::Second\nseasonal_cycle::Bool\nsolar_declination::SpeedyWeather.SinSolarDeclination{NF} where NF<:AbstractFloat\ninitial_time::Base.RefValue{DateTime}\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SpectralAR1Process","page":"Function and type index","title":"SpeedyWeather.SpectralAR1Process","text":"First-order auto-regressive random process (AR1) in spectral space, evolving wavenumbers with respectice time_scales and standard_deviations independently. Transformed after every time step to grid space with a clamp applied to limit extrema. For reproducability seed can be provided and an independent random_number_generator is used that is reseeded on every initialize!. Fields are \n\ntrunc::Int64\ntime_scale::Second: [OPTION] Time scale of the AR1 process\nwavenumber::Int64: [OPTION] Wavenumber of the AR1 process\nstandard_deviation::Any: [OPTION] Standard deviation of the AR1 process\nclamp::Tuple{NF, NF} where NF: [OPTION] Range to clamp values into after every transform into grid space\nseed::Int64: [OPTION] Random number generator seed, 0=randomly seed from Julia's GLOBAL_RNG\nrandom_number_generator::Random.Xoshiro: Independent random number generator for this random process\nautoregressive_factor::Base.RefValue: Precomputed auto-regressive factor [1], function of time scale and model time step\nnoise_factors::Any: Precomputed noise factors [1] for every total wavenumber l\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SpectralFilter","page":"Function and type index","title":"SpeedyWeather.SpectralFilter","text":"Spectral filter for horizontal diffusion. Fields are \n\ntrunc::Int64: spectral resolution\nnlayers::Int64: number of vertical levels\nshift::Any: [OPTION] shift diffusion to higher (positive shift) or lower (neg) wavenumbers, relative to trunc\nscale::Any: [OPTION] Scale-selectiveness, steepness of the sigmoid, higher is more selective\ntime_scale::Second: [OPTION] diffusion time scale\ntime_scale_div::Second: [OPTION] stronger diffusion time scale for divergence\nresolution_scaling::Any: [OPTION] resolution scaling to shorten time_scale with trunc\npower::Any: [OPTION] power of the tanh function\npower_div::Any: [OPTION] power of the tanh function for divergence\nexpl::Any\nimpl::Any\nexpl_div::Any\nimpl_div::Any\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SpectralFilter-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.SpectralFilter","text":"SpectralFilter(\n spectral_grid::SpectralGrid;\n kwargs...\n) -> SpectralFilter{<:AbstractFloat}\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.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}: [OPTION] number format used throughout the model\ndevice::SpeedyWeather.AbstractDevice: [OPTION] device archictecture to run on\nArrayType::Type{<:AbstractArray}: [OPTION] array type to use for all variables\ntrunc::Int64: [OPTION] horizontal resolution as the maximum degree of spherical harmonics\nGrid::Type{<:AbstractGrid}: [OPTION] horizontal grid used for calculations in grid-point space\ndealiasing::Float64: [OPTION] how to match spectral with grid resolution: dealiasing factor, 1=linear, 2=quadratic, 3=cubic grid\nradius::Float64: [OPTION] radius of the sphere [m]\nnparticles::Int64: [OPTION] number of particles for particle advection [1]\nnlat_half::Int64: number of latitude rings on one hemisphere (Equator incl)\nnlat::Int64: number of latitude rings on both hemispheres\nnpoints::Int64: total number of grid points in the horizontal\nnlayers::Int64: [OPTION] number of vertical levels\nvertical_coordinates::SpeedyWeather.VerticalCoordinates: [OPTION] coordinates used to discretize the vertical\nVectorType::Type{<:AbstractVector}\nMatrixType::Type{<:AbstractMatrix}\nSpectralVariable2D::Type{<:AbstractArray}\nSpectralVariable3D::Type{<:AbstractArray}\nSpectralVariable4D::Type{<:AbstractArray}\nGridVariable2D::Type{<:AbstractArray}\nGridVariable3D::Type{<:AbstractArray}\nGridVariable4D::Type{<:AbstractArray}\nParticleVector::Type{<:AbstractArray}\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.SpeedyTransforms.SpectralTransform-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.SpeedyTransforms.SpectralTransform","text":"SpectralTransform(\n spectral_grid::SpectralGrid;\n one_more_degree,\n kwargs...\n) -> SpectralTransform{NF, _A, _B, _C, _D, _E, LowerTriangularArray{NF1, 1, _A1}, LowerTriangularArray{NF2, 2, _A2}} where {NF<:AbstractFloat, _A, _B, _C, _D, _E, NF1<:AbstractFloat, _A1, NF2<:AbstractFloat, _A2}\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.SurfaceEvaporation","page":"Function and type index","title":"SpeedyWeather.SurfaceEvaporation","text":"Surface evaporation following a bulk formula with wind from model.surface_wind \n\nuse_boundary_layer_drag::Bool: Use column.boundarylayerdrag coefficient\nmoisture_exchange_land::AbstractFloat: Otherwise, use the following drag coefficient for evaporation over land\nmoisture_exchange_sea::AbstractFloat: Or drag coefficient for evaporation over sea\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SurfaceFluxHeatOutput","page":"Function and type index","title":"SpeedyWeather.SurfaceFluxHeatOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SurfaceFluxHumidOutput","page":"Function and type index","title":"SpeedyWeather.SurfaceFluxHumidOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SurfacePressureOutput","page":"Function and type index","title":"SpeedyWeather.SurfacePressureOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.TemperatureOutput","page":"Function and type index","title":"SpeedyWeather.TemperatureOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Tendencies","page":"Function and type index","title":"SpeedyWeather.Tendencies","text":"Tendencies of the prognostic variables in spectral and grid-point space\n\ntrunc::Int64\nnlat_half::Int64\nnlayers::Int64\nvor_tend::Any: Vorticity of horizontal wind field [1/s]\ndiv_tend::Any: Divergence of horizontal wind field [1/s]\ntemp_tend::Any: Absolute temperature [K]\nhumid_tend::Any: Specific humidity [kg/kg]\nu_tend::Any: Zonal velocity [m/s]\nv_tend::Any: Meridional velocity [m/s]\npres_tend::Any: Logarithm of surface pressure [Pa]\nu_tend_grid::Any: Zonal velocity [m/s], grid\nv_tend_grid::Any: Meridinoal velocity [m/s], grid\ntemp_tend_grid::Any: Absolute temperature [K], grid\nhumid_tend_grid::Any: Specific humidity [kg/kg], grid\npres_tend_grid::Any: Logarith of surface pressure [Pa], grid\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Tendencies-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.Tendencies","text":"Tendencies(\n SG::SpectralGrid\n) -> Tendencies{<:AbstractFloat, <:AbstractArray, <:AbstractArray, <:AbstractArray, <:AbstractArray, <:AbstractArray}\n\n\nGenerator function.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.TetensEquation","page":"Function and type index","title":"SpeedyWeather.TetensEquation","text":"Parameters for computing saturation vapour pressure of water using the Tetens' equation,\n\neᵢ(T) = e₀ * exp(Cᵢ * (T - T₀) / (T + Tᵢ)),\n\nwhere T is in Kelvin and i = 1, 2 for saturation above and below freezing, respectively. From Tetens (1930), and Murray (1967) for below freezing.\n\ne₀::AbstractFloat: Saturation water vapour pressure at 0°C [Pa]\nT₀::AbstractFloat: 0°C in Kelvin\nT₁::AbstractFloat: Tetens denominator (water) [˚C]\nT₂::AbstractFloat: Tetens denominator following Murray (1967, below freezing) [˚C]\nC₁::AbstractFloat: Tetens numerator scaling [1], above freezing\nC₂::AbstractFloat: Tetens numerator scaling [1], below freezing\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.TetensEquation-Tuple{NF} where NF","page":"Function and type index","title":"SpeedyWeather.TetensEquation","text":"Functor: Saturation water vapour pressure as a function of temperature using the Tetens equation,\n\neᵢ(T) = e₀ * exp(Cᵢ * (T - T₀) / (T - Tᵢ)),\n\nwhere T is in Kelvin and i = 1, 2 for saturation above and below freezing, respectively.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.UniformCooling","page":"Function and type index","title":"SpeedyWeather.UniformCooling","text":"Uniform cooling following Paulius and Garner, 2006. JAS. https://doi.org/10.1175/JAS3705.1 imposing a default temperature tendency of -1.5K/day (=1K/16hours for a time_scale of 16 hours) on every level except for the stratosphere (diagnosed as temp < temp_min) where a relaxation term with time_scale_stratosphere towards temp_stratosphere is applied.\n\ndT/dt = -1.5K/day for T > 207.5K else (200K-T) / 5 days\n\nFields are\n\ntime_scale::Second: [OPTION] time scale of cooling, default = -1.5K/day = -1K/16hrs\ntemp_min::Any: [OPTION] temperature [K] below which stratospheric relaxation is applied\ntemp_stratosphere::Any: [OPTION] target temperature [K] of stratospheric relaxation\ntime_scale_stratosphere::Second: [OPTION] time scale of stratospheric relaxation\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.VorticityOutput","page":"Function and type index","title":"SpeedyWeather.VorticityOutput","text":"Defines netCDF output of vorticity. Fields are\n\nname::String: [Required] short name of variable (unique) used in netCDF file and key for dictionary\nunit::String: [Required] unit of variable\nlong_name::String: [Required] long name of variable used in netCDF file\ndims_xyzt::NTuple{4, Bool}: [Required] NetCDF dimensions the variable uses, lon, lat, layer, time\nmissing_value::Float64: [Optional] missing value for the variable, if not specified uses NaN\ncompression_level::Int64: [Optional] compression level of the lossless compressor, 1=lowest/fastest, 9=highest/slowest, 3=default\nshuffle::Bool: [Optional] bitshuffle the data for compression, false = default\nkeepbits::Int64: [Optional] number of mantissa bits to keep for compression (default: 15)\n\nCustom variable output defined similarly with required fields marked, optional fields otherwise use variable-independent defaults. Initialize with VorticityOutput() and non-default fields can always be passed on as keyword arguments, e.g. VorticityOutput(long_name=\"relative vorticity\", compression_level=0).\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ZonalJet","page":"Function and type index","title":"SpeedyWeather.ZonalJet","text":"A struct that contains all parameters for the Galewsky et al, 2004 zonal jet intitial conditions for the ShallowWaterModel. 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::AbstractGrid{NF} where NF<:AbstractFloat: height [m] on grid-point space.\ngeopot_surf::LowerTriangularArray{Complex{NF}, 1, Array{Complex{NF}, 1}} where NF<:AbstractFloat: surface geopotential, height*gravity [m²/s²]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ZonalVelocityOutput","page":"Function and type index","title":"SpeedyWeather.ZonalVelocityOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"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\n\n\n\n\n","category":"type"},{"location":"functions/#Base.copy!-Tuple{PrognosticVariables, PrognosticVariables}","page":"Function and type index","title":"Base.copy!","text":"copy!(\n progn_new::PrognosticVariables,\n progn_old::PrognosticVariables\n) -> PrognosticVariables\n\n\nCopies entries of progn_old into progn_new.\n\n\n\n\n\n","category":"method"},{"location":"functions/#Base.delete!-Tuple{NetCDFOutput, Vararg{Union{String, Symbol}}}","page":"Function and type index","title":"Base.delete!","text":"delete!(\n output::NetCDFOutput,\n keys::Union{String, Symbol}...\n) -> NetCDFOutput\n\n\nDelete output variables from output by their (short name) (Symbol or String), corresponding to the keys in the dictionary.\n\n\n\n\n\n","category":"method"},{"location":"functions/#Base.fill!-Tuple{Tendencies, Any, Type{<:Barotropic}}","page":"Function and type index","title":"Base.fill!","text":"fill!(\n tendencies::Tendencies,\n x,\n _::Type{<:Barotropic}\n) -> Tendencies\n\n\nSet the tendencies for the barotropic model to x.\n\n\n\n\n\n","category":"method"},{"location":"functions/#Base.fill!-Tuple{Tendencies, Any, Type{<:PrimitiveDry}}","page":"Function and type index","title":"Base.fill!","text":"fill!(\n tendencies::Tendencies,\n x,\n _::Type{<:PrimitiveDry}\n) -> Tendencies\n\n\nSet the tendencies for the primitive dry model to x.\n\n\n\n\n\n","category":"method"},{"location":"functions/#Base.fill!-Tuple{Tendencies, Any, Type{<:PrimitiveWet}}","page":"Function and type index","title":"Base.fill!","text":"fill!(\n tendencies::Tendencies,\n x,\n _::Type{<:PrimitiveWet}\n) -> Tendencies\n\n\nSet the tendencies for the primitive wet model to x.\n\n\n\n\n\n","category":"method"},{"location":"functions/#Base.fill!-Tuple{Tendencies, Any, Type{<:ShallowWater}}","page":"Function and type index","title":"Base.fill!","text":"fill!(\n tendencies::Tendencies,\n x,\n _::Type{<:ShallowWater}\n) -> Tendencies\n\n\nSet the tendencies for the shallow-water model to x.\n\n\n\n\n\n","category":"method"},{"location":"functions/#Base.mod-Tuple{P} where P<:Particle","page":"Function and type index","title":"Base.mod","text":"mod(p::Particle) -> Any\n\n\nModulo operator for particle locations to map them back into [0,360˚E) and [-90˚,90˚N], in the horizontal and to clamp vertical σ coordinates into [0,1].\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.CallbackDict-Tuple{Vararg{Pair{Symbol, <:SpeedyWeather.AbstractCallback}}}","page":"Function and type index","title":"SpeedyWeather.CallbackDict","text":"CallbackDict(\n pairs::Pair{Symbol, <:SpeedyWeather.AbstractCallback}...\n) -> Dict{Symbol, SpeedyWeather.AbstractCallback}\n\n\nCreate Callback dictionary like normal dictionaries.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.CallbackDict-Tuple{}","page":"Function and type index","title":"SpeedyWeather.CallbackDict","text":"CallbackDict(\n\n) -> Dict{Symbol, SpeedyWeather.AbstractCallback}\n\n\nEmpty Callback dictionary generator.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.DeviceArray-Tuple{CPU, Any}","page":"Function and type index","title":"SpeedyWeather.DeviceArray","text":"DeviceArray(_::CPU, x) -> Any\n\n\nAdapts x to an Array when device::CPU is used. Define for CPU for compatibility with adapt to CuArrays etc. Uses adapt, thus also can return SubArrays etc.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.Device_KernelAbstractions-Tuple{CPU}","page":"Function and type index","title":"SpeedyWeather.Device_KernelAbstractions","text":"Device_KernelAbstractions(\n _::CPU\n) -> Type{KernelAbstractions.CPU}\n\n\nReturn used device for use with KernelAbstractions\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SpeedyTransforms.transform!-Tuple{DiagnosticVariables, PrognosticVariables, Integer, Barotropic}","page":"Function and type index","title":"SpeedyWeather.SpeedyTransforms.transform!","text":"transform!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Integer,\n model::Barotropic;\n kwargs...\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.transform!-Tuple{DiagnosticVariables, PrognosticVariables, Integer, NoRandomProcess, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.SpeedyTransforms.transform!","text":"transform!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Integer,\n random_process::NoRandomProcess,\n spectral_transform::SpectralTransform\n)\n\n\nNoRandomProcess does not need to transform any random pattern from spectral to grid space.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SpeedyTransforms.transform!-Tuple{DiagnosticVariables, PrognosticVariables, Integer, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.SpeedyTransforms.transform!","text":"transform!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Integer,\n model::PrimitiveEquation;\n initialize\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.transform!-Tuple{DiagnosticVariables, PrognosticVariables, Integer, ShallowWater}","page":"Function and type index","title":"SpeedyWeather.SpeedyTransforms.transform!","text":"transform!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Integer,\n model::ShallowWater;\n kwargs...\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.SpeedyTransforms.transform!-Tuple{DiagnosticVariables, PrognosticVariables, Integer, SpeedyWeather.AbstractRandomProcess, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.SpeedyTransforms.transform!","text":"transform!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Integer,\n random_process::SpeedyWeather.AbstractRandomProcess,\n spectral_transform::SpectralTransform\n)\n\n\nGeneral transform for random processes <: AbstractRandomProcess. Takes the spectral random_pattern in the prognostic variables and transforms it to spectral space in diagn.grid.random_pattern.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.WhichZenith-Tuple{SpectralGrid, SpeedyWeather.AbstractPlanet}","page":"Function and type index","title":"SpeedyWeather.WhichZenith","text":"WhichZenith(\n SG::SpectralGrid,\n P::SpeedyWeather.AbstractPlanet;\n kwargs...\n) -> Union{SolarZenith, SolarZenithSeason}\n\n\nChooses from SolarZenith (daily and seasonal cycle) or SolarZenithSeason given the parameters in model.planet. In both cases the seasonal cycle can be disabled, calculating the solar declination from the initial time instead of current time.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.activate-Union{Tuple{Particle{NF}}, Tuple{NF}} where NF","page":"Function and type index","title":"SpeedyWeather.activate","text":"activate(\n p::Particle{NF}\n) -> Particle{_A, true} where _A<:AbstractFloat\n\n\nActivate particle. Active particles can move.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.active-Union{Tuple{Particle{NF, isactive}}, Tuple{isactive}, Tuple{NF}} where {NF, isactive}","page":"Function and type index","title":"SpeedyWeather.active","text":"active(_::Particle{NF, isactive}) -> Any\n\n\nCheck whether particle is active.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.add!-Tuple{AbstractModel, Vararg{Pair{Symbol, <:SpeedyWeather.AbstractCallback}}}","page":"Function and type index","title":"SpeedyWeather.add!","text":"add!(\n model::AbstractModel,\n key_callbacks::Pair{Symbol, <:SpeedyWeather.AbstractCallback}...\n) -> Any\n\n\nAdd a or several callbacks to a model::AbstractModel. To be used like\n\nadd!(model, :my_callback => callback)\nadd!(model, :my_callback1 => callback, :my_callback2 => other_callback)\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.add!-Tuple{AbstractModel, Vararg{SpeedyWeather.AbstractCallback}}","page":"Function and type index","title":"SpeedyWeather.add!","text":"add!(\n model::AbstractModel,\n callbacks::SpeedyWeather.AbstractCallback...\n)\n\n\nAdd a or several callbacks to a mdoel without specifying the key which is randomly created like callback_????. To be used like\n\nadd!(model.callbacks, callback)\nadd!(model.callbacks, callback1, callback2).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.add!-Tuple{AbstractModel, Vararg{SpeedyWeather.AbstractOutputVariable}}","page":"Function and type index","title":"SpeedyWeather.add!","text":"add!(\n model::AbstractModel,\n outputvariables::SpeedyWeather.AbstractOutputVariable...\n)\n\n\nAdd outputvariables to the dictionary in output::NetCDFOutput of model, i.e. at model.output.variables.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.add!-Tuple{Dict{Symbol, SpeedyWeather.AbstractCallback}, Vararg{Pair{Symbol, <:SpeedyWeather.AbstractCallback}}}","page":"Function and type index","title":"SpeedyWeather.add!","text":"add!(\n D::Dict{Symbol, SpeedyWeather.AbstractCallback},\n key_callbacks::Pair{Symbol, <:SpeedyWeather.AbstractCallback}...\n)\n\n\nAdd a or several callbacks to a Dict{String, AbstractCallback} dictionary. To be used like\n\nadd!(model.callbacks, :my_callback => callback)\nadd!(model.callbacks, :my_callback1 => callback, :my_callback2 => other_callback)\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.add!-Tuple{Dict{Symbol, SpeedyWeather.AbstractCallback}, Vararg{SpeedyWeather.AbstractCallback}}","page":"Function and type index","title":"SpeedyWeather.add!","text":"add!(\n D::Dict{Symbol, SpeedyWeather.AbstractCallback},\n callbacks::SpeedyWeather.AbstractCallback...;\n verbose\n)\n\n\nAdd a or several callbacks to a Dict{Symbol, AbstractCallback} dictionary without specifying the key which is randomly created like callback_????. To be used like\n\nadd!(model.callbacks, callback)\nadd!(model.callbacks, callback1, callback2).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.add!-Tuple{Dict{Symbol, SpeedyWeather.AbstractOutputVariable}, Vararg{SpeedyWeather.AbstractOutputVariable}}","page":"Function and type index","title":"SpeedyWeather.add!","text":"add!(\n D::Dict{Symbol, SpeedyWeather.AbstractOutputVariable},\n outputvariables::SpeedyWeather.AbstractOutputVariable...\n) -> Dict{Symbol, SpeedyWeather.AbstractOutputVariable}\n\n\nAdd outputvariables to a dictionary defining the variables subject to NetCDF output.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.add!-Tuple{NetCDFOutput, Vararg{SpeedyWeather.AbstractOutputVariable}}","page":"Function and type index","title":"SpeedyWeather.add!","text":"add!(\n output::NetCDFOutput,\n outputvariables::SpeedyWeather.AbstractOutputVariable...\n) -> NetCDFOutput\n\n\nAdd outputvariables to the dictionary in output::NetCDFOutput, i.e. at output.variables.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.add_default!-Tuple{Dict{Symbol, SpeedyWeather.AbstractOutputVariable}, Type{<:Barotropic}}","page":"Function and type index","title":"SpeedyWeather.add_default!","text":"add_default!(\n output_variables::Dict{Symbol, SpeedyWeather.AbstractOutputVariable},\n Model::Type{<:Barotropic}\n) -> Dict{Symbol, SpeedyWeather.AbstractOutputVariable}\n\n\nAdd default variables to output for a Barotropic model: Vorticity, zonal and meridional velocity.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.add_default!-Tuple{Dict{Symbol, SpeedyWeather.AbstractOutputVariable}, Type{<:PrimitiveDry}}","page":"Function and type index","title":"SpeedyWeather.add_default!","text":"add_default!(\n variables::Dict{Symbol, SpeedyWeather.AbstractOutputVariable},\n Model::Type{<:PrimitiveDry}\n) -> Dict{Symbol, SpeedyWeather.AbstractOutputVariable}\n\n\nAdd default variables to output for a PrimitiveDry model, same as for a Barotropic model but also the surface pressure and temperature.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.add_default!-Tuple{Dict{Symbol, SpeedyWeather.AbstractOutputVariable}, Type{<:PrimitiveWet}}","page":"Function and type index","title":"SpeedyWeather.add_default!","text":"add_default!(\n variables::Dict{Symbol, SpeedyWeather.AbstractOutputVariable},\n Model::Type{<:PrimitiveWet}\n) -> Dict{Symbol, SpeedyWeather.AbstractOutputVariable}\n\n\nAdd default variables to output for a PrimitiveWet model, same as for a PrimitiveDry model but also the specific humidity.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.add_default!-Tuple{Dict{Symbol, SpeedyWeather.AbstractOutputVariable}, Type{<:ShallowWater}}","page":"Function and type index","title":"SpeedyWeather.add_default!","text":"add_default!(\n variables::Dict{Symbol, SpeedyWeather.AbstractOutputVariable},\n Model::Type{<:ShallowWater}\n) -> Dict{Symbol, SpeedyWeather.AbstractOutputVariable}\n\n\nAdd default variables to output for a ShallowWater model, same as for a Barotropic model but also the interface displacement.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.bernoulli_potential!-Tuple{DiagnosticVariables, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.bernoulli_potential!","text":"bernoulli_potential!(\n diagn::DiagnosticVariables,\n S::SpectralTransform\n)\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.bulk_richardson!-Tuple{ColumnVariables, SpeedyWeather.AbstractAtmosphere}","page":"Function and type index","title":"SpeedyWeather.bulk_richardson!","text":"bulk_richardson!(\n column::ColumnVariables,\n atmosphere::SpeedyWeather.AbstractAtmosphere\n) -> Any\n\n\nCalculate the bulk richardson number following Frierson, 2007. For vertical stability in the boundary layer.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.bulk_richardson_surface-Tuple{ColumnVariables, SpeedyWeather.AbstractAtmosphere}","page":"Function and type index","title":"SpeedyWeather.bulk_richardson_surface","text":"bulk_richardson_surface(\n column::ColumnVariables,\n atmosphere::SpeedyWeather.AbstractAtmosphere\n) -> Any\n\n\nCalculate the bulk richardson number following Frierson, 2007. For vertical stability in the boundary layer.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.callback!-Tuple{GlobalSurfaceTemperatureCallback, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.callback!","text":"callback!(\n callback::GlobalSurfaceTemperatureCallback,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n) -> Any\n\n\nPulls the average temperature from the lowermost layer and stores it in the next element of the callback.temp vector.\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.convection!-Union{Tuple{NF}, Tuple{ColumnVariables{NF}, DryBettsMiller, Geometry, SpeedyWeather.AbstractAtmosphere, PrimitiveEquation}} where NF","page":"Function and type index","title":"SpeedyWeather.convection!","text":"convection!(\n column::ColumnVariables{NF},\n DBM::DryBettsMiller,\n geometry::Geometry,\n atmosphere::SpeedyWeather.AbstractAtmosphere,\n model::PrimitiveEquation\n)\n\n\ncalculates temperature tendency for the dry convection scheme following the simplified Betts-Miller convection from Frierson 2007 but with zero humidity. Starts with a first-guess relaxation to determine the convective criterion, then adjusts the reference profiles for thermodynamic consistency (e.g. in dry convection the humidity profile is non-precipitating), and relaxes current vertical profiles to the adjusted references.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.convection!-Union{Tuple{NF}, Tuple{ColumnVariables{NF}, SimplifiedBettsMiller, SpeedyWeather.AbstractClausiusClapeyron, Geometry, SpeedyWeather.AbstractPlanet, SpeedyWeather.AbstractAtmosphere, SpeedyWeather.AbstractTimeStepper, PrimitiveWet}} where NF","page":"Function and type index","title":"SpeedyWeather.convection!","text":"convection!(\n column::ColumnVariables{NF},\n SBM::SimplifiedBettsMiller,\n clausius_clapeyron::SpeedyWeather.AbstractClausiusClapeyron,\n geometry::Geometry,\n planet::SpeedyWeather.AbstractPlanet,\n atmosphere::SpeedyWeather.AbstractAtmosphere,\n time_stepping::SpeedyWeather.AbstractTimeStepper,\n model::PrimitiveWet\n) -> Union{Nothing, Int64}\n\n\ncalculates temperature and humidity tendencies for the convection scheme following the simplified Betts-Miller convection. Starts with a first-guess relaxation to determine the convective criteria (none, dry/shallow or deep), then adjusts reference profiles for thermodynamic consistency (e.g. in dry convection the humidity profile is non-precipitating), and relaxes current vertical profiles to the adjusted references.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.coriolis-Tuple{Grid} where Grid<:AbstractGridArray","page":"Function and type index","title":"SpeedyWeather.coriolis","text":"coriolis(grid::AbstractGridArray; kwargs...) -> 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, Vararg{Integer}}} where Grid<:AbstractGridArray","page":"Function and type index","title":"SpeedyWeather.coriolis","text":"coriolis(grid::AbstractGridArray; kwargs...) -> 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.cos_zenith!-Union{Tuple{NF}, Tuple{AbstractGrid{NF}, SolarZenith, DateTime, SpeedyWeather.AbstractGeometry}} where NF","page":"Function and type index","title":"SpeedyWeather.cos_zenith!","text":"cos_zenith!(\n cos_zenith::AbstractGridArray{NF, 1, Array{NF, 1}},\n S::SolarZenith,\n time::DateTime,\n geometry::SpeedyWeather.AbstractGeometry\n)\n\n\nCalculate cos of solar zenith angle with a daily cycle at time time. Seasonal cycle or time correction may be disabled, depending on parameters in SolarZenith.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.cos_zenith!-Union{Tuple{NF}, Tuple{AbstractGrid{NF}, SolarZenithSeason, DateTime, SpeedyWeather.AbstractGeometry}} where NF","page":"Function and type index","title":"SpeedyWeather.cos_zenith!","text":"cos_zenith!(\n cos_zenith::AbstractGridArray{NF, 1, Array{NF, 1}},\n S::SolarZenithSeason,\n time::DateTime,\n geometry::SpeedyWeather.AbstractGeometry\n)\n\n\nCalculate cos of solar zenith angle as daily average at time time. Seasonal cycle or time correction may be disabled, depending on parameters in SolarZenithSeason.\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.deactivate-Union{Tuple{Particle{NF}}, Tuple{NF}} where NF","page":"Function and type index","title":"SpeedyWeather.deactivate","text":"deactivate(\n p::Particle{NF}\n) -> Particle{_A, false} where _A<:AbstractFloat\n\n\nDeactivate particle. Inactive particles cannot move.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.default_array_type-Tuple{SpeedyWeather.AbstractDevice}","page":"Function and type index","title":"SpeedyWeather.default_array_type","text":"default_array_type(\n device::SpeedyWeather.AbstractDevice\n) -> Type{Array}\n\n\nDefault array type on device.\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(\n nlayers::Integer\n) -> Vector{Float64}\n\n\nVertical sigma coordinates defined by their nlayers+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. Default levels are equally spaced from 0 to 1 (including).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.drag!-Tuple{DiagnosticVariables, QuadraticDrag}","page":"Function and type index","title":"SpeedyWeather.drag!","text":"drag!(diagn::DiagnosticVariables, drag::QuadraticDrag)\n\n\nQuadratic drag for the momentum equations.\n\nF = -c_D/H*|(u, v)|*(u, v)\n\nwith cD the non-dimensional drag coefficient as defined in drag::QuadraticDrag. cD and layer thickness H are precomputed in initialize!(::QuadraticDrag, ::AbstractModel) and scaled by the radius as are the momentum equations.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.dry_adiabat!-Tuple{AbstractVector, AbstractVector, Real, AbstractVector, SpeedyWeather.AbstractAtmosphere}","page":"Function and type index","title":"SpeedyWeather.dry_adiabat!","text":"dry_adiabat!(\n temp_ref_profile::AbstractVector,\n temp_environment::AbstractVector,\n temp_parcel::Real,\n σ::AbstractVector,\n atmosphere::SpeedyWeather.AbstractAtmosphere\n) -> Int64\n\n\nCalculates the moist pseudo adiabat given temperature and humidity of surface parcel. Follows the dry adiabat till condensation and then continues on the pseudo moist-adiabat with immediate condensation to the level of zero buoyancy. Levels above are skipped, set to NaN instead and should be skipped in the relaxation.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.dry_static_energy!-Tuple{ColumnVariables, SpeedyWeather.AbstractAtmosphere}","page":"Function and type index","title":"SpeedyWeather.dry_static_energy!","text":"dry_static_energy!(\n column::ColumnVariables,\n atmosphere::SpeedyWeather.AbstractAtmosphere\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!-Tuple{DiagnosticVariables, PrognosticVariables, Integer, Barotropic}","page":"Function and type index","title":"SpeedyWeather.dynamics_tendencies!","text":"dynamics_tendencies!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Integer,\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{DiagnosticVariables, PrognosticVariables, Integer, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.dynamics_tendencies!","text":"dynamics_tendencies!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Integer,\n model::PrimitiveEquation\n)\n\n\nCalculate all tendencies for the PrimitiveEquation model (wet or dry).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.dynamics_tendencies!-Tuple{DiagnosticVariables, PrognosticVariables, Integer, ShallowWater}","page":"Function and type index","title":"SpeedyWeather.dynamics_tendencies!","text":"dynamics_tendencies!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Integer,\n model::ShallowWater\n)\n\n\nCalculate all tendencies for the ShallowWaterModel.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.finalize!-Tuple{Feedback}","page":"Function and type index","title":"SpeedyWeather.finalize!","text":"finalize!(F::Feedback)\n\n\nFinalises the progress meter and the progress txt file.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.first_timesteps!-Tuple{PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.first_timesteps!","text":"first_timesteps!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\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!-Tuple{LowerTriangularArray, AbstractGridArray, DiagnosticVariables, Geometry, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.flux_divergence!","text":"flux_divergence!(\n A_tend::LowerTriangularArray,\n A_grid::AbstractGridArray,\n diagn::DiagnosticVariables,\n G::Geometry,\n S::SpectralTransform;\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, SpeedyWeather.AbstractPlanet, SpeedyWeather.AbstractAtmosphere}","page":"Function and type index","title":"SpeedyWeather.fluxes_to_tendencies!","text":"fluxes_to_tendencies!(\n column::ColumnVariables,\n geometry::Geometry,\n planet::SpeedyWeather.AbstractPlanet,\n atmosphere::SpeedyWeather.AbstractAtmosphere\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{DiagnosticVariables, JetStreamForcing}","page":"Function and type index","title":"SpeedyWeather.forcing!","text":"forcing!(\n diagn::DiagnosticVariables,\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, ::AbstractModel).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.geopotential!","page":"Function and type index","title":"SpeedyWeather.geopotential!","text":"geopotential!(\n geopot::AbstractVector,\n temp::AbstractVector,\n G::Geopotential\n)\ngeopotential!(\n geopot::AbstractVector,\n temp::AbstractVector,\n G::Geopotential,\n geopot_surf::Real\n)\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":"function"},{"location":"functions/#SpeedyWeather.geopotential!-Tuple{DiagnosticVariables, Geopotential, SpeedyWeather.AbstractOrography}","page":"Function and type index","title":"SpeedyWeather.geopotential!","text":"geopotential!(\n diagn::DiagnosticVariables,\n geopotential::Geopotential,\n orography::SpeedyWeather.AbstractOrography\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{DiagnosticVariables, LowerTriangularArray, SpeedyWeather.AbstractPlanet}","page":"Function and type index","title":"SpeedyWeather.geopotential!","text":"geopotential!(\n diagn::DiagnosticVariables,\n pres::LowerTriangularArray,\n planet::SpeedyWeather.AbstractPlanet\n)\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.get_column!-Tuple{ColumnVariables, DiagnosticVariables, PrognosticVariables, Int64, PrimitiveEquation}","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, PrognosticVariables, Integer, Integer, Geometry, SpeedyWeather.AbstractPlanet, SpeedyWeather.AbstractOrography, SpeedyWeather.AbstractLandSeaMask, SpeedyWeather.AbstractAlbedo, SpeedyWeather.AbstractImplicit}","page":"Function and type index","title":"SpeedyWeather.get_column!","text":"get_column!(\n C::ColumnVariables,\n D::DiagnosticVariables,\n P::PrognosticVariables,\n ij::Integer,\n jring::Integer,\n geometry::Geometry,\n planet::SpeedyWeather.AbstractPlanet,\n orography::SpeedyWeather.AbstractOrography,\n land_sea_mask::SpeedyWeather.AbstractLandSeaMask,\n albedo::SpeedyWeather.AbstractAlbedo,\n implicit::SpeedyWeather.AbstractImplicit\n) -> Any\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{SpeedyWeather.AbstractOutput}","page":"Function and type index","title":"SpeedyWeather.get_full_output_file_path","text":"get_full_output_file_path(\n output::SpeedyWeather.AbstractOutput\n) -> 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, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.get_thermodynamics!","text":"get_thermodynamics!(\n column::ColumnVariables,\n model::PrimitiveEquation\n)\n\n\nCalculate geopotentiala and dry static energy for the primitive equation model.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.get_Δt_millisec","page":"Function and type index","title":"SpeedyWeather.get_Δt_millisec","text":"get_Δt_millisec(\n Δt_at_T31::Dates.TimePeriod,\n trunc,\n radius,\n adjust_with_output::Bool\n) -> Any\nget_Δt_millisec(\n Δt_at_T31::Dates.TimePeriod,\n trunc,\n radius,\n adjust_with_output::Bool,\n output_dt::Dates.TimePeriod\n) -> Any\n\n\nComputes the time step in [ms]. Δt_at_T31 is always scaled with the resolution trunc of the model. In case adjust_Δt_with_output is true, the Δt_at_T31 is additionally adjusted to the closest divisor of output_dt so that the output time axis is keeping output_dt exactly.\n\n\n\n\n\n","category":"function"},{"location":"functions/#SpeedyWeather.grad-Union{Tuple{NF}, Tuple{ClausiusClapeyron{NF}, NF}} where NF","page":"Function and type index","title":"SpeedyWeather.grad","text":"grad(CC::ClausiusClapeyron{NF}, temp_kelvin) -> Any\n\n\nGradient of Clausius-Clapeyron wrt to temperature, evaluated at temp_kelvin.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.grad-Union{Tuple{NF}, Tuple{TetensEquation{NF}, NF}} where NF","page":"Function and type index","title":"SpeedyWeather.grad","text":"grad(\n TetensCoefficients::TetensEquation{NF},\n temp_kelvin\n) -> Any\n\n\nGradient of the Tetens equation wrt to temperature, evaluated at temp_kelvin.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.grad_saturation_humidity-Union{Tuple{NF}, Tuple{ClausiusClapeyron{NF}, NF, NF}} where NF","page":"Function and type index","title":"SpeedyWeather.grad_saturation_humidity","text":"grad_saturation_humidity(\n CC::ClausiusClapeyron{NF},\n temp_kelvin,\n pres\n) -> Any\n\n\nGradient of Clausius-Clapeyron wrt to temperature, evaluated at temp_kelvin.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.has-Tuple{Type{<:AbstractModel}, Symbol}","page":"Function and type index","title":"SpeedyWeather.has","text":"has(Model::Type{<:AbstractModel}, var_name::Symbol) -> Any\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::DiagnosticVariables,\n progn::PrognosticVariables,\n diffusion::SpeedyWeather.AbstractHorizontalDiffusion,\n model::ShallowWater\n) -> Any\nhorizontal_diffusion!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n diffusion::SpeedyWeather.AbstractHorizontalDiffusion,\n model::ShallowWater,\n lf::Integer\n) -> Any\n\n\nApply horizontal diffusion to vorticity and divergence in the ShallowWaterModel.\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 diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n diffusion::SpeedyWeather.AbstractHorizontalDiffusion,\n model::Barotropic\n) -> Any\nhorizontal_diffusion!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n diffusion::SpeedyWeather.AbstractHorizontalDiffusion,\n model::Barotropic,\n lf::Integer\n) -> Any\n\n\nApply horizontal diffusion to vorticity in the BarotropicModel.\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::DiagnosticVariables,\n progn::PrognosticVariables,\n diffusion::SpeedyWeather.AbstractHorizontalDiffusion,\n model::PrimitiveEquation\n) -> Any\nhorizontal_diffusion!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n diffusion::SpeedyWeather.AbstractHorizontalDiffusion,\n model::PrimitiveEquation,\n lf::Integer\n) -> Any\n\n\nApply horizontal diffusion applied to vorticity, divergence, temperature, and humidity (PrimitiveWet only) in the PrimitiveEquation models.\n\n\n\n\n\n","category":"function"},{"location":"functions/#SpeedyWeather.horizontal_diffusion!-Tuple{LowerTriangularArray, LowerTriangularArray, AbstractMatrix, AbstractMatrix}","page":"Function and type index","title":"SpeedyWeather.horizontal_diffusion!","text":"horizontal_diffusion!(\n tendency::LowerTriangularArray,\n var::LowerTriangularArray,\n expl::AbstractMatrix,\n impl::AbstractMatrix\n)\n\n\nApply horizontal diffusion to a 2D field var 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 var as well as its tendency tendency.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.implicit_correction!-Tuple{DiagnosticVariables, ImplicitPrimitiveEquation, PrognosticVariables}","page":"Function and type index","title":"SpeedyWeather.implicit_correction!","text":"implicit_correction!(\n diagn::DiagnosticVariables,\n implicit::ImplicitPrimitiveEquation,\n progn::PrognosticVariables\n)\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!-Tuple{DiagnosticVariables, PrognosticVariables, ImplicitShallowWater}","page":"Function and type index","title":"SpeedyWeather.implicit_correction!","text":"implicit_correction!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\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!-Tuple{AquaPlanetMask, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n land_sea_mask::AquaPlanetMask,\n model::PrimitiveEquation\n)\n\n\nSets all grid points to 0 = sea.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{Barotropic}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n model::Barotropic;\n time\n) -> Simulation{Model} where Model<:Barotropic\n\n\nCalls all initialize! functions for most fields, representing 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{Clock, SpeedyWeather.AbstractTimeStepper}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n clock::Clock,\n time_stepping::SpeedyWeather.AbstractTimeStepper\n) -> 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{EarthOrography, SpeedyWeather.AbstractPlanet, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n orog::EarthOrography,\n P::SpeedyWeather.AbstractPlanet,\n S::SpectralTransform\n)\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, Clock, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n feedback::Feedback,\n clock::Clock,\n model::AbstractModel\n) -> ProgressMeter.Progress\n\n\nInitializes the a Feedback struct.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{Geopotential, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n geopotential::Geopotential,\n model::PrimitiveEquation\n)\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.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, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(diffusion::HyperDiffusion, model::AbstractModel)\n\n\nPrecomputes the hyper diffusion terms in diffusion 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.AbstractGeometry, SpeedyWeather.AbstractTimeStepper}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n diffusion::HyperDiffusion,\n G::SpeedyWeather.AbstractGeometry,\n L::SpeedyWeather.AbstractTimeStepper\n)\n\n\nPrecomputes the hyper diffusion terms for all layers based on the model time step in L, the vertical level sigma level in G.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{ImplicitPrimitiveEquation, Real, DiagnosticVariables, SpeedyWeather.AbstractGeometry, SpeedyWeather.AbstractGeopotential, SpeedyWeather.AbstractAtmosphere, SpeedyWeather.AbstractAdiabaticConversion}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n implicit::ImplicitPrimitiveEquation,\n dt::Real,\n diagn::DiagnosticVariables,\n geometry::SpeedyWeather.AbstractGeometry,\n geopotential::SpeedyWeather.AbstractGeopotential,\n atmosphere::SpeedyWeather.AbstractAtmosphere,\n adiabatic_conversion::SpeedyWeather.AbstractAdiabaticConversion\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, SpeedyWeather.AbstractPlanet, SpeedyWeather.AbstractAtmosphere}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n implicit::ImplicitShallowWater,\n dt::Real,\n planet::SpeedyWeather.AbstractPlanet,\n atmosphere::SpeedyWeather.AbstractAtmosphere\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{LandSeaMask, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n land_sea_mask::LandSeaMask,\n model::PrimitiveEquation\n) -> AbstractGrid{NF} where NF<:AbstractFloat\n\n\nReads a high-resolution land-sea mask from file and interpolates (grid-call average) onto the model grid for a fractional sea mask.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{Leapfrog, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(L::Leapfrog, model::AbstractModel)\n\n\nInitialize leapfrogging L by recalculating the timestep given the output time step output_dt from model.output. Recalculating will slightly adjust the time step to be a divisor such that an integer number of time steps matches exactly with the output time step.\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{PrimitiveDry}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n model::PrimitiveDry;\n time\n) -> Simulation{Model} where Model<:PrimitiveDry\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!(\n model::PrimitiveWet;\n time\n) -> Simulation{Model} where Model<:PrimitiveWet\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, PressureOnOrography, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn::PrognosticVariables,\n _::PressureOnOrography,\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.initialize!-Tuple{PrognosticVariables, RossbyHaurwitzWave, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn::PrognosticVariables,\n initial_conditions::RossbyHaurwitzWave,\n model::AbstractModel\n)\n\n\nRossby-Haurwitz wave initial conditions as in Williamson et al. 1992, J Computational Physics with an additional cut-off amplitude c to filter out tiny harmonics in the vorticity field.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{PrognosticVariables, StartFromFile, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn_new::PrognosticVariables,\n initial_conditions::StartFromFile,\n model::AbstractModel\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, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn::PrognosticVariables,\n initial_conditions::ZonalJet,\n model::AbstractModel\n)\n\n\nInitial conditions from Galewsky, 2004, Tellus\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{Schedule, Clock}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(scheduler::Schedule, clock::Clock) -> Schedule\n\n\nInitialize a Schedule with a Clock (which is assumed to have been initialized). Takes both scheduler.every and scheduler.times into account, such that both periodic and events can be scheduled simulataneously. But execution will happen only once if they coincide on a given time step.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{ShallowWater}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n model::ShallowWater;\n time\n) -> Simulation{Model} where Model<:ShallowWater\n\n\nCalls all initialize! functions for most components (=fields) of model, except for model.output and model.feedback which are always initialized 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{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)\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{Interpolator}, Tuple{Grid3D}, Tuple{Grid2D}, Tuple{NetCDFOutput{Grid2D, Grid3D, Interpolator}, SpeedyWeather.AbstractFeedback, PrognosticVariables, DiagnosticVariables, AbstractModel}} where {Grid2D, Grid3D, Interpolator}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n output::NetCDFOutput{Grid2D, Grid3D, Interpolator},\n feedback::SpeedyWeather.AbstractFeedback,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\nInitialize NetCDF output by creating a netCDF file and storing the initial conditions of diagn (and progn). To be called just before the first timesteps.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Union{Tuple{NF}, Tuple{Array{Particle{NF}, 1}, PrognosticVariables, DiagnosticVariables, ParticleAdvection2D}} where NF","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n particles::Array{Particle{NF}, 1},\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n particle_advection::ParticleAdvection2D\n) -> Any\n\n\nInitialize particle advection time integration: Store u,v interpolated initial conditions in diagn.particles.u and .v to be used when particle advection actually executed for first time.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Union{Tuple{NF}, Tuple{GlobalSurfaceTemperatureCallback{NF}, PrognosticVariables, DiagnosticVariables, AbstractModel}} where NF","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n callback::GlobalSurfaceTemperatureCallback{NF},\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n) -> Int64\n\n\nInitializes callback.temp vector that records the global mean surface temperature on every time step. Allocates vector of correct length (number of elements = total time steps plus one) and stores the global surface temperature of the initial conditions\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Union{Tuple{NF}, Tuple{PrognosticVariables{NF}, JablonowskiTemperature, PrimitiveEquation}} where NF","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn::PrognosticVariables{NF},\n initial_conditions::JablonowskiTemperature,\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{PrognosticVariables{NF}, RandomWaves, ShallowWater}} where NF","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn::PrognosticVariables{NF},\n initial_conditions::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}, StartWithRandomVorticity, Barotropic}} where NF","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn::PrognosticVariables{NF},\n initial_conditions::StartWithRandomVorticity,\n model::Barotropic\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}, ZonalWind, PrimitiveEquation}} where NF","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn::PrognosticVariables{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{P}, Tuple{Vector{P}, AbstractModel}} where P<:Particle","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n particles::Array{P<:Particle, 1},\n model::AbstractModel\n)\n\n\nInitialize particle locations uniformly in latitude, longitude and in the vertical σ coordinates. This uses a cosin-distribution in latitude for an equal-area uniformity.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.isdecreasing-Tuple{AbstractVector}","page":"Function and type index","title":"SpeedyWeather.isdecreasing","text":"isdecreasing(v::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(v::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.ismod-Tuple{Particle}","page":"Function and type index","title":"SpeedyWeather.ismod","text":"ismod(p::Particle) -> Bool\n\n\nCheck that a particle is in longitude [0,360˚E), latitude [-90˚,90˚N], and σ in [0,1].\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.isscheduled-Tuple{Schedule, Clock}","page":"Function and type index","title":"SpeedyWeather.isscheduled","text":"isscheduled(S::Schedule, clock::Clock) -> Bool\n\n\nEvaluate whether (e.g. a callback) should be scheduled at the timestep given in clock. Returns true for scheduled executions, false for no execution on this time step.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.large_scale_condensation!-Tuple{ColumnVariables, ImplicitCondensation, SpeedyWeather.AbstractClausiusClapeyron, Geometry, SpeedyWeather.AbstractPlanet, SpeedyWeather.AbstractAtmosphere, SpeedyWeather.AbstractTimeStepper}","page":"Function and type index","title":"SpeedyWeather.large_scale_condensation!","text":"large_scale_condensation!(\n column::ColumnVariables,\n scheme::ImplicitCondensation,\n clausius_clapeyron::SpeedyWeather.AbstractClausiusClapeyron,\n geometry::Geometry,\n planet::SpeedyWeather.AbstractPlanet,\n atmosphere::SpeedyWeather.AbstractAtmosphere,\n time_stepping::SpeedyWeather.AbstractTimeStepper\n)\n\n\nLarge-scale condensation for a column by relaxation back to 100% relative humidity. Calculates the tendencies for specific humidity and temperature from latent heat release 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!(\n device_setup::SpeedyWeather.DeviceSetup,\n kernel!,\n ndrange,\n kernel_args...\n)\n\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{LowerTriangularArray, LowerTriangularArray, LowerTriangularArray, Real, Int64, Leapfrog{NF}}} where NF","page":"Function and type index","title":"SpeedyWeather.leapfrog!","text":"leapfrog!(\n A_old::LowerTriangularArray,\n A_new::LowerTriangularArray,\n tendency::LowerTriangularArray,\n dt::Real,\n lf::Int64,\n L::Leapfrog{NF}\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{DiagnosticVariables, PrognosticVariables, Int64, SpeedyWeather.AbstractAtmosphere, ImplicitPrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.linear_pressure_gradient!","text":"linear_pressure_gradient!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Int64,\n atmosphere::SpeedyWeather.AbstractAtmosphere,\n implicit::ImplicitPrimitiveEquation\n)\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{DiagnosticVariables, PrognosticVariables, Integer, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.linear_virtual_temperature!","text":"linear_virtual_temperature!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Integer,\n model::PrimitiveEquation\n)\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, Integer, PrimitiveDry}","page":"Function and type index","title":"SpeedyWeather.linear_virtual_temperature!","text":"linear_virtual_temperature!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n progn::SpeedyWeather.PrognosticLayerTimesteps,\n lf::Integer,\n model::PrimitiveDry\n)\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}, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.load_trajectory","text":"load_trajectory(\n var_name::Union{String, Symbol},\n model::AbstractModel\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.AbstractClausiusClapeyron}","page":"Function and type index","title":"SpeedyWeather.moist_static_energy!","text":"moist_static_energy!(\n column::ColumnVariables,\n clausius_clapeyron::SpeedyWeather.AbstractClausiusClapeyron\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.move-Union{Tuple{NF}, Tuple{Particle{NF, false}, Vararg{Any}}} where NF","page":"Function and type index","title":"SpeedyWeather.move","text":"move(\n p::Particle{NF, false},\n args...\n) -> Particle{NF, false} where NF\n\n\nInactive particles are not moved.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.move-Union{Tuple{NF}, Tuple{Particle{NF, true}, Any, Any, Any}} where NF","page":"Function and type index","title":"SpeedyWeather.move","text":"move(\n p::Particle{NF, true},\n dlon,\n dlat,\n dσ\n) -> Particle{_A, true} where _A<:AbstractFloat\n\n\nMove a particle with increments (dlon, dlat, dσ) in those respective coordinates. Only active particles are moved.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.move-Union{Tuple{NF}, Tuple{Particle{NF, true}, Any, Any}} where NF","page":"Function and type index","title":"SpeedyWeather.move","text":"move(\n p::Particle{NF, true},\n dlon,\n dlat\n) -> Particle{_A, true} where _A<:AbstractFloat\n\n\nMove a particle with increments (dlon, dlat) in 2D. No movement in vertical σ. Only active particles are moved.\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.output!-Tuple{NetCDFOutput, DateTime}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(output::NetCDFOutput, time::DateTime)\n\n\nWrite the current time time::DateTime to the netCDF file in output.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, Dict{Symbol, SpeedyWeather.AbstractOutputVariable}, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n output_variables::Dict{Symbol, SpeedyWeather.AbstractOutputVariable},\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\nLoop over every variable in output.variables to call the respective output! method to write into the output.netcdf_file.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\nWrites the variables from progn or diagn of time step i at time time into output.netcdf_file. Simply escapes for no netcdf output or if output shouldn't be written on this time step. Interpolates onto output grid and resolution as specified in output, 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.output!-Tuple{NetCDFOutput, SpeedyWeather.CloudTopOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.CloudTopOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.ConvectivePrecipitationOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.ConvectivePrecipitationOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.DivergenceOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.DivergenceOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.HumidityOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.HumidityOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.InterfaceDisplacementOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.InterfaceDisplacementOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.LargeScalePrecipitationOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.LargeScalePrecipitationOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.MeridionalVelocityOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.MeridionalVelocityOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.OrographyOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.OrographyOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.OutgoingLongwaveRadiationOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.OutgoingLongwaveRadiationOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.OutgoingShortwaveRadiationOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.OutgoingShortwaveRadiationOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.RandomPatternOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.RandomPatternOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.SeaSurfaceTemperatureOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.SeaSurfaceTemperatureOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.SurfaceFluxHeatOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.SurfaceFluxHeatOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.SurfaceFluxHumidOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.SurfaceFluxHumidOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.SurfacePressureOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.SurfacePressureOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.TemperatureOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.TemperatureOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.VorticityOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.VorticityOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\nOutput the vorticity field vor from diagn.grid into the netCDF file output.netcdf_file. Interpolates the vorticity field onto the output grid and resolution as specified in output. Method required for all output variables <: AbstractOutputVariable with dispatch over the second argument.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.ZonalVelocityOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.ZonalVelocityOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for ZonalVelocityOutput to write the zonal velocity field u from diagn.grid, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.parameterization_tendencies!-Tuple{ColumnVariables, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.parameterization_tendencies!","text":"parameterization_tendencies!(\n column::ColumnVariables,\n model::PrimitiveEquation\n)\n\n\nCalls for column one physics parameterization after another and convert fluxes to tendencies.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.parameterization_tendencies!-Tuple{DiagnosticVariables, PrognosticVariables, DateTime, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.parameterization_tendencies!","text":"parameterization_tendencies!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n time::DateTime,\n model::PrimitiveEquation\n)\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.physics_tendencies_only!-Tuple{DiagnosticVariables, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.physics_tendencies_only!","text":"physics_tendencies_only!(\n diagn::DiagnosticVariables,\n model::PrimitiveEquation\n)\n\n\nFor dynamics=false, after calling parameterization_tendencies! call this function to transform the physics tendencies from grid-point to spectral space including the necessary coslat⁻¹ scaling.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.pressure_gradient_flux!-Tuple{DiagnosticVariables, PrognosticVariables, Integer, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.pressure_gradient_flux!","text":"pressure_gradient_flux!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Integer,\n S::SpectralTransform\n)\n\n\nCompute the gradient ∇lnps of the logarithm of surface pressure, followed by its flux, (u,v) * ∇lnps.\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.pseudo_adiabat!-Union{Tuple{NF}, Tuple{AbstractVector, NF, Real, AbstractVector, AbstractVector, Real, AbstractVector, SpeedyWeather.AbstractClausiusClapeyron}} where NF","page":"Function and type index","title":"SpeedyWeather.pseudo_adiabat!","text":"pseudo_adiabat!(\n temp_ref_profile::AbstractVector,\n temp_parcel,\n humid_parcel::Real,\n temp_virt_environment::AbstractVector,\n geopot::AbstractVector,\n pres::Real,\n σ::AbstractVector,\n clausius_clapeyron::SpeedyWeather.AbstractClausiusClapeyron\n) -> Int64\n\n\nCalculates the moist pseudo adiabat given temperature and humidity of surface parcel. Follows the dry adiabat till condensation and then continues on the pseudo moist-adiabat with immediate condensation to the level of zero buoyancy. Levels above are skipped, set to NaN instead and should be skipped in the relaxation.\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> using SpeedyWeather: readable_secs\n\njulia> readable_secs(12345)\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.AbstractSimulation}","page":"Function and type index","title":"SpeedyWeather.run!","text":"run!(\n simulation::SpeedyWeather.AbstractSimulation;\n period,\n output\n) -> Union{UnicodePlots.Plot{T, Val{true}} where T<:UnicodePlots.HeatmapCanvas, UnicodePlots.Plot{T, Val{false}} where T<:UnicodePlots.HeatmapCanvas}\n\n\nRun a SpeedyWeather.jl simulation. The simulation.model is assumed to be initialized.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.saturation_humidity!-Tuple{ColumnVariables, SpeedyWeather.AbstractClausiusClapeyron}","page":"Function and type index","title":"SpeedyWeather.saturation_humidity!","text":"saturation_humidity!(\n column::ColumnVariables,\n clausius_clapeyron::SpeedyWeather.AbstractClausiusClapeyron\n)\n\n\nCompute the saturation water vapour pressure [Pa], the saturation humidity [kg/kg] and the relative humidity following clausius_clapeyron.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.saturation_humidity-Union{Tuple{NF}, Tuple{NF, NF, SpeedyWeather.AbstractClausiusClapeyron}} where NF","page":"Function and type index","title":"SpeedyWeather.saturation_humidity","text":"saturation_humidity(\n temp_kelvin,\n pres,\n clausius_clapeyron::SpeedyWeather.AbstractClausiusClapeyron\n) -> Any\n\n\nSaturation humidity [kg/kg] from temperature [K], pressure [Pa] via\n\nsat_vap_pres = clausius_clapeyron(temperature)\nsaturation humidity = mol_ratio * sat_vap_pres / pressure\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.saturation_humidity-Union{Tuple{NF}, Tuple{NF, NF}} where NF","page":"Function and type index","title":"SpeedyWeather.saturation_humidity","text":"saturation_humidity(sat_vap_pres, pres; mol_ratio) -> Any\n\n\nSaturation humidity from saturation vapour pressure and pressure via\n\nqsat = mol_ratio*sat_vap_pres/pres\n\nwith both pressures in same units and qsat in kg/kg.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.scale!-Tuple{DiagnosticVariables, Symbol, Real}","page":"Function and type index","title":"SpeedyWeather.scale!","text":"scale!(\n diagn::DiagnosticVariables,\n var::Symbol,\n scale::Real\n) -> Any\n\n\nScale the variable var inside diagn with scalar scale.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.scale!-Tuple{PrognosticVariables, DiagnosticVariables, Real}","page":"Function and type index","title":"SpeedyWeather.scale!","text":"scale!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n scale::Real\n) -> 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!-Tuple{PrognosticVariables, Symbol, Real}","page":"Function and type index","title":"SpeedyWeather.scale!","text":"scale!(progn::PrognosticVariables, var::Symbol, scale::Real)\n\n\nScale the variable var inside progn with scalar scale.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set!-Tuple{AbstractModel}","page":"Function and type index","title":"SpeedyWeather.set!","text":"set!(\n model::AbstractModel;\n orography,\n land_sea_mask,\n albedo,\n kwargs...\n) -> Bool\n\n\nSets a boundary condition fields for model. The input can be a function, RingGrid, LowerTriangularMatrix, or scalar as for other set! functions. If the keyword add==true the input is added to the exisiting field instead.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set!-Tuple{PrognosticVariables, Geometry}","page":"Function and type index","title":"SpeedyWeather.set!","text":"set!(\n progn::PrognosticVariables,\n geometry::Geometry;\n u,\n v,\n vor,\n div,\n temp,\n humid,\n pres,\n sea_surface_temperature,\n sea_ice_concentration,\n land_surface_temperature,\n snow_depth,\n soil_moisture_layer1,\n soil_moisture_layer2,\n lf,\n add,\n spectral_transform,\n coslat_scaling_included\n)\n\n\nSets new values for the keyword arguments (velocities, vorticity, divergence, etc..) into the prognostic variable struct progn at timestep index lf. If add==true they are added to the current value instead. If a SpectralTransform S is provided, it is used when needed to set the variable, otherwise it is recomputed. In case u and v are provied, actually the divergence and vorticity are set and coslat_scaling_included specficies whether or not the 1/cos(lat) scaling is already included in the arrays or not (default: false)\n\nThe input may be:\n\nA function or callable object f(lond, latd, σ) -> value (multilevel variables) \nA function or callable object f(lond, latd) -> value (surface level variables)\nAn instance of AbstractGridArray \nAn instance of LowerTriangularArray \nA scalar <: Number (interpreted as a constant field in grid space)\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set!-Tuple{SpeedyWeather.AbstractSimulation}","page":"Function and type index","title":"SpeedyWeather.set!","text":"set!(S::SpeedyWeather.AbstractSimulation; kwargs...) -> Any\n\n\nSets properties of the simuluation S. Convenience wrapper to call the other concrete set! methods. All kwargs are forwarded to these methods, which are documented seperately. See their documentation for possible kwargs. \n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_period!-Tuple{Clock, Dates.Period}","page":"Function and type index","title":"SpeedyWeather.set_period!","text":"set_period!(clock::Clock, period::Dates.Period) -> Second\n\n\nSet the period of the clock to a new value. Converts any Dates.Period input to Second.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_period!-Tuple{Clock, Real}","page":"Function and type index","title":"SpeedyWeather.set_period!","text":"set_period!(clock::Clock, period::Real) -> Any\n\n\nSet the period of the clock to a new value. Converts any ::Real input to Day.\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(nlayers::Integer, σ_half::AbstractVector) -> Bool\n\n\nCheck that nlayers and σ_half match.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.solar_hour_angle-Union{Tuple{T}, Tuple{Type{T}, DateTime, Any, Second}} where T","page":"Function and type index","title":"SpeedyWeather.solar_hour_angle","text":"solar_hour_angle(\n _::Type{T},\n time::DateTime,\n λ,\n length_of_day::Second\n) -> Any\n\n\nFraction of day as angle in radians [0...2π]. TODO: Takes length of day as argument, but a call to Dates.Time() currently have this hardcoded anyway.\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.surface_pressure_tendency!-Tuple{DiagnosticVariables, 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{DiagnosticVariables, ImplicitPrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.temperature_anomaly!","text":"temperature_anomaly!(\n diagn::DiagnosticVariables,\n implicit::ImplicitPrimitiveEquation\n)\n\n\nConvert 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{DiagnosticVariables, LowerTriangularArray, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.temperature_average!","text":"temperature_average!(\n diagn::DiagnosticVariables,\n temp::LowerTriangularArray,\n S::SpectralTransform\n)\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, HeldSuarez, SpeedyWeather.AbstractAtmosphere}","page":"Function and type index","title":"SpeedyWeather.temperature_relaxation!","text":"temperature_relaxation!(\n column::ColumnVariables,\n scheme::HeldSuarez,\n atmosphere::SpeedyWeather.AbstractAtmosphere\n)\n\n\nApply temperature relaxation following Held and Suarez 1996, BAMS.\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_tendency!-Tuple{DiagnosticVariables, SpeedyWeather.AbstractAdiabaticConversion, SpeedyWeather.AbstractAtmosphere, ImplicitPrimitiveEquation, Geometry, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.temperature_tendency!","text":"temperature_tendency!(\n diagn::DiagnosticVariables,\n adiabatic_conversion::SpeedyWeather.AbstractAdiabaticConversion,\n atmosphere::SpeedyWeather.AbstractAtmosphere,\n implicit::ImplicitPrimitiveEquation,\n G::Geometry,\n S::SpectralTransform\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.time_stepping!-Tuple{PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.time_stepping!","text":"time_stepping!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n) -> Union{UnicodePlots.Plot{T, Val{true}} where T<:UnicodePlots.HeatmapCanvas, UnicodePlots.Plot{T, Val{false}} where T<:UnicodePlots.HeatmapCanvas}\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) -> Union{Nothing, Bool}\ntimestep!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n dt::Real,\n model::Barotropic,\n lf1::Integer\n) -> Union{Nothing, Bool}\ntimestep!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n dt::Real,\n model::Barotropic,\n lf1::Integer,\n lf2::Integer\n) -> Union{Nothing, Bool}\n\n\nCalculate a single time step for the barotropic model.\n\n\n\n\n\n","category":"function"},{"location":"functions/#SpeedyWeather.timestep!-2","page":"Function and type index","title":"SpeedyWeather.timestep!","text":"timestep!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n dt::Real,\n model::PrimitiveEquation\n) -> Union{Nothing, Bool}\ntimestep!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n dt::Real,\n model::PrimitiveEquation,\n lf1::Integer\n) -> Union{Nothing, Bool}\ntimestep!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n dt::Real,\n model::PrimitiveEquation,\n lf1::Integer,\n lf2::Integer\n) -> Union{Nothing, Bool}\n\n\nCalculate a single time step for the model<:PrimitiveEquation\n\n\n\n\n\n","category":"function"},{"location":"functions/#SpeedyWeather.timestep!-3","page":"Function and type index","title":"SpeedyWeather.timestep!","text":"timestep!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n dt::Real,\n model::ShallowWater\n) -> Union{Nothing, Bool}\ntimestep!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n dt::Real,\n model::ShallowWater,\n lf1::Integer\n) -> Union{Nothing, Bool}\ntimestep!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n dt::Real,\n model::ShallowWater,\n lf1::Integer,\n lf2::Integer\n) -> Union{Nothing, Bool}\n\n\nCalculate a single time step for the model <: ShallowWater.\n\n\n\n\n\n","category":"function"},{"location":"functions/#SpeedyWeather.tree-Tuple{AbstractModel}","page":"Function and type index","title":"SpeedyWeather.tree","text":"tree(M::AbstractModel; modules, with_size, kwargs...)\n\n\nCreate a tree of fields inside a model and fields within these fields as long as they are defined within the modules argument (default SpeedyWeather). Other keyword arguments are max_level::Integer=10, with_types::Bool=false or `with_size::Bool=false.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.tree-Tuple{Any}","page":"Function and type index","title":"SpeedyWeather.tree","text":"tree(S; modules, with_size, kwargs...)\n\n\nCreate a tree of fields inside S and fields within these fields as long as they are defined within the modules argument (default SpeedyWeather). Other keyword arguments are max_level::Integer=10, with_types::Bool=false or `with_size::Bool=false.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.tree-Union{Tuple{Simulation{M}}, Tuple{M}} where M","page":"Function and type index","title":"SpeedyWeather.tree","text":"tree(S::Simulation{M}; modules, with_size, kwargs...)\n\n\nCreate a tree of fields inside a Simulation instance and fields within these fields as long as they are defined within the modules argument (default SpeedyWeather). Other keyword arguments are max_level::Integer=10, with_types::Bool=false or `with_size::Bool=false.\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{DiagnosticVariables}","page":"Function and type index","title":"SpeedyWeather.unscale!","text":"unscale!(diagn::DiagnosticVariables) -> Int64\n\n\nUndo the radius-scaling of vorticity and divergence from scale!(diagn, scale::Real).\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!-Tuple{DiagnosticVariables, PrognosticVariables, Integer, Geometry}","page":"Function and type index","title":"SpeedyWeather.vertical_integration!","text":"vertical_integration!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Integer,\n geometry::Geometry\n)\n\n\nCalculates the vertically averaged (weighted by the thickness of the σ level) velocities (*coslat) and divergence. E.g.\n\nu_mean = ∑_k=1^nlayers Δσ_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.vertical_interpolate!-Tuple{Vector, Vector, Geometry}","page":"Function and type index","title":"SpeedyWeather.vertical_interpolate!","text":"vertical_interpolate!(\n A_half::Vector,\n A_full::Vector,\n G::Geometry\n)\n\n\nGiven a vector in column defined at full levels, do a linear interpolation in log(σ) to calculate its values at half-levels, skipping top (k=1/2), extrapolating to bottom (k=nlayers+1/2).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.virtual_temperature!-Tuple{DiagnosticVariables, PrimitiveDry}","page":"Function and type index","title":"SpeedyWeather.virtual_temperature!","text":"virtual_temperature!(\n diagn::DiagnosticVariables,\n model::PrimitiveDry\n)\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.virtual_temperature!-Tuple{DiagnosticVariables, PrimitiveWet}","page":"Function and type index","title":"SpeedyWeather.virtual_temperature!","text":"virtual_temperature!(\n diagn::DiagnosticVariables,\n model::PrimitiveWet\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.volume_flux_divergence!-Tuple{DiagnosticVariables, SpeedyWeather.AbstractOrography, SpeedyWeather.AbstractAtmosphere, SpeedyWeather.AbstractGeometry, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.volume_flux_divergence!","text":"volume_flux_divergence!(\n diagn::DiagnosticVariables,\n orog::SpeedyWeather.AbstractOrography,\n atmosphere::SpeedyWeather.AbstractAtmosphere,\n G::SpeedyWeather.AbstractGeometry,\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{DiagnosticVariables, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.vordiv_tendencies!","text":"vordiv_tendencies!(\n diagn::DiagnosticVariables,\n model::PrimitiveEquation\n)\n\n\nFunction barrier to unpack model.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.vordiv_tendencies!-Tuple{DiagnosticVariables, SpeedyWeather.AbstractCoriolis, SpeedyWeather.AbstractAtmosphere, SpeedyWeather.AbstractGeometry, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.vordiv_tendencies!","text":"vordiv_tendencies!(\n diagn::DiagnosticVariables,\n coriolis::SpeedyWeather.AbstractCoriolis,\n atmosphere::SpeedyWeather.AbstractAtmosphere,\n geometry::SpeedyWeather.AbstractGeometry,\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.vorticity_flux!-Tuple{DiagnosticVariables, Barotropic}","page":"Function and type index","title":"SpeedyWeather.vorticity_flux!","text":"vorticity_flux!(\n diagn::DiagnosticVariables,\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{DiagnosticVariables, ShallowWater}","page":"Function and type index","title":"SpeedyWeather.vorticity_flux!","text":"vorticity_flux!(\n diagn::DiagnosticVariables,\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{DiagnosticVariables, SpeedyWeather.AbstractCoriolis, Geometry, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.vorticity_flux_curldiv!","text":"vorticity_flux_curldiv!(\n diagn::DiagnosticVariables,\n coriolis::SpeedyWeather.AbstractCoriolis,\n geometry::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(\n device::SpeedyWeather.AbstractDevice\n) -> Int64\n\n\nReturns a workgroup size depending on device. 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, SpeedyWeather.AbstractPlanet, Integer}","page":"Function and type index","title":"SpeedyWeather.write_column_tendencies!","text":"write_column_tendencies!(\n diagn::DiagnosticVariables,\n column::ColumnVariables,\n planet::SpeedyWeather.AbstractPlanet,\n ij::Integer\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_restart_file-Union{Tuple{T}, Tuple{SpeedyWeather.AbstractOutput, PrognosticVariables{T}}} where T","page":"Function and type index","title":"SpeedyWeather.write_restart_file","text":"write_restart_file(\n output::SpeedyWeather.AbstractOutput,\n progn::PrognosticVariables{T}\n) -> Any\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.year_angle-Union{Tuple{T}, Tuple{Type{T}, DateTime, Second, Second}} where T","page":"Function and type index","title":"SpeedyWeather.year_angle","text":"year_angle(\n _::Type{T},\n time::DateTime,\n length_of_day::Second,\n length_of_year::Second\n) -> Any\n\n\nFraction of year as angle in radians [0...2π]. TODO: Takes length of day/year as argument, but calls to Dates.Time(), Dates.dayofyear() currently have these hardcoded.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.σ_interpolation_weights-Tuple{AbstractVector, AbstractVector}","page":"Function and type index","title":"SpeedyWeather.σ_interpolation_weights","text":"σ_interpolation_weights(\n σ_levels_full::AbstractVector,\n σ_levels_half::AbstractVector\n) -> Any\n\n\nInterpolation weights for full to half level interpolation on sigma coordinates. Following Fortran SPEEDY documentation eq. (1).\n\n\n\n\n\n","category":"method"},{"location":"particles/#Particle-advection","page":"Particle advection","title":"Particle advection","text":"","category":"section"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"All SpeedyWeather.jl models support particle advection. Particles are objects without mass or volume at a location mathbfx = (lambda theta sigma) (longitude lambda, latitude theta, vertical sigma coordinate sigma, see Sigma coordinates) that are moved with the wind mathbfu(mathbfx) at that location. The location of the p-th particle changes as follows","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"fracd mathbfx_pd t = mathbfu(mathbfx_p)","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"This equation applies in 2D, i.e. mathbfx = (lambda theta) and mathbfu = (u v) or in 3D, but at the moment only 2D advection is supported. In the Primitive equation model the vertical layer on which the advection takes place has to be specified. It is therefore not advected with the vertical velocity but maintains a constant pressure ratio compared to the surface pressure (sigma is constant).","category":"page"},{"location":"particles/#Discretization-of-particle-advection","page":"Particle advection","title":"Discretization of particle advection","text":"","category":"section"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"The particle advection equation has to be discretized to be numerically solved. While the particle location can generally be anywhere on the sphere, the velocity mathbfu is only available on the discrete grid points of the simulation, such that mathbfu(mathbfx_p) requires an interpolation in order to obtain a velocity at the particles location mathbfx_p to move it around. Dropping the subscript p in favour a subscript i denoting the time step, with Euler forward the equation can be discretized as ","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"mathbfx_i+1 = mathbfx_i + Delta tmathbfu_i (mathbfx_i)","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"Meaning we have used the velocity field at both departure time i and departure location mathbfx_i to update a particle's location which makes this scheme first order accurate. But only a single interpolation of the velocity field, which, in fact, is one per dimension, is necessary. Note that the time step Delta t here and the time step to solve the dynamics do not have to be identical. We could use a larger time step for the particle advection then to solve the dynamics inside the model, and because the stability criteria for these equations are different, one is encouraged to do so. Also because the particles are considered passive, meaning that their location does not influence the other prognostic variables.","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"We can write down a more accurate scheme at the cost of a second interpolation step. The Heun method, also called predictor-corrector is 2nd order accurate and uses an average of the velocity at departure time i and location mathbfx_i and at a (predicted meaning preliminary) arrival point x^star_i+1 and arrival time i+1.","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"beginaligned\nmathbfx^star_i+1 = mathbfx_i + Delta tmathbfu_i (mathbfx_i) \nmathbfx_i+1 = mathbfx_i + fracDelta t2left(\n mathbfu_i (mathbfx_i) + mathbfu_i+1 (mathbfx^star_i+1)right)\nendaligned","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"Because we don't have mathbfu_i+1 available at time i, we perform this integration retrospectively, i.e. if the other model dynamics have reached time i+1 then we let the particle advection catch up by integrating them from i to i+1. This, however, requires some storage of the velocity mathbfu_i at the previous advection time step. Remember that this does not need to be the time step for the momentum equations and could be much further in the past. We could either store mathbfu_i as a grid-point field or only its interpolated values. In the case of fewer particles than grid points the latter is more efficient and this is also what we do in SpeedyWeather. Let square brackets denote an interpolation then we perform the interpolation mathbfu_i mathbfx_i that's required to step from i to i+1 already on the time step that goes from i-1 to i. ","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"beginaligned\nmathbfx^star_i+1 = mathbfx_i + Delta tmathbfu_i (mathbfx_i) \nmathbfx_i+1 = mathbfx_i + fracDelta t2left(\n mathbfu_i (mathbfx_i) + mathbfu_i+1 mathbfx^star_i+1right) \nmathbfu_i+1 (mathbfx_i+1) = mathbfu_i+1 mathbfx_i+1\nendaligned","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"Denoted here as the last line with the left-hand side becoming the last term of the first line in the next time step i+1 to i+2. Now it becomes clearer that there are two interpolations required on every time step.","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"We use for horizontal coordinates degrees, such that we need to scale the time step Delta with frac3602pi R (radius R) for advection in latitude and with frac3602pi R cos(theta) for advection in longitude (because the distance between meridians decreases towards the poles). We move the division by the radius conveniently into the time step as are also the momentum equations scaled with the radius, see Radius scaling.","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"Technically, a particle moved with a given velocity follows a great circle in spherical coordinates. This means that","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"theta_i+1 approx theta_i + fracDelta tR frac3602pi v_i","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"becomes a bad approximation when the time step and or the velocity are large. However, for simplicity and to avoid the calculation of the great circle we currently do use this to move particles with a given velocity. We essentially assume a local cartesian coordinate system instead of the geodesics in spherical coordinates. However, for typical time steps of 1 hour and velocities not exceeding 100 m/s the error is not catastrophic and can be reduced with a shorter time step. We may switch to great circle calculations in future versions.","category":"page"},{"location":"particles/#Create-a-particle","page":"Particle advection","title":"Create a particle","text":"","category":"section"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"So much about the theory","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"A Particle at location 10˚E and 30˚N (and sigma = 0) can be created as follows,","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"using SpeedyWeather\np = Particle(lon=10, lat=30, σ=0)\np = Particle(lon=10, lat=30)\np = Particle(10, 30, 0)\np = Particle(10, 30)","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"All of the above are equivalent. Unless a keyword argument is used, longitude is the first argument, followed by latitude (necessary), followed by sigma (can be omitted). Longitudes can be -180˚E to 180˚E or 0 to 360˚E, latitudes have to be -90˚N to 90˚N. You can create a particle with coordinates outside of these ranges (and no error or warning is thrown) but during particle advection they will be wrapped into [0, 360˚E] and [-90˚N, 90˚N], using the mod(::Particle) function, which is similar to the modulo operator but with the second argument hardcoded to the coordinate ranges from above, e.g.","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"mod(Particle(lon=-30, lat=0))","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"which also takes into account pole crossings which adds 180˚ in longitude","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"mod(Particle(lon=0, lat=100))","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"as if the particle has moved across the pole. That way all real values for longitude and latitude are wrapped into the reference range [0, 360˚E] and [-90˚N, 90˚N].","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"info: Particles are immutable\nParticles are implemented as immutable struct, meaning you cannot change their position by particle.lon = value. You have to think of them as integers or floats instead. If you have a particle p and you want to change its position to the Equator for example you need to create a new one new_particle = Particle(p.lon, 0, p.σ).","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"By default Float32 is used, but providing coordinates in Float64 will promote the type accordingly. Also by default, particles are active which is indicated by the 2nd parametric type of Particle, a boolean. Active particles are moved following the equation above, but inactive particles are not. You can activate or deactivate a particle like so","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"deactivate(p)","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"and so","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"activate(p)","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"or check its activity by active(::Particle) returning true or false. The zero-element of the Particle type is","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"zero(Particle)","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"and you can also create a random particle which uses a raised cosine distribution in latitude for an equal area-weighted uniform distribution over the sphere","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"rand(Particle{Float32}) # specify number format\nrand(Particle{Float32, true}) # and active/inactive\nrand(Particle) # or not (defaults used instead)","category":"page"},{"location":"particles/#Advecting-particles","page":"Particle advection","title":"Advecting particles","text":"","category":"section"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"The Particle type can be used inside vectors, e.g.","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"zeros(Particle{Float32}, 3)\nrand(Particle{Float64}, 5)","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"which is how particles are represented inside a SpeedyWeather Simulation. Note that we have not specified whether the particles inside these vectors are active (e.g. Particle{Float32, true}) or inactive (e.g. Particle{Float64, false}) because that would generally force all particles in these vectors to be either active or inactive as specified such that","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"v = zeros(Particle{Float32, false}, 3)\nv[1] = Particle(lon = 134.0, lat = 23) # conversion to inactive Particle{Float32, false}\nv","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"would not just convert from Float64 to Float32 but also from an active to an inactive particle. In SpeedyWeather all particles can be activated or deactivated at any time.","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"First, you create a SpectralGrid with the nparticles keyword","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"spectral_grid = SpectralGrid(nparticles = 3)","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"Then the particles live as Vector{Particle} inside the prognostic variables","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"model = BarotropicModel(spectral_grid)\nsimulation = initialize!(model)\nsimulation.prognostic_variables.particles","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"Which are placed in random locations (using rand) initially. In order to change these (e.g. to set the initial conditions) you do","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"simulation.prognostic_variables.particles[1] = Particle(lon=-120, lat=45)\nsimulation.prognostic_variables.particles","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"which sets the first particle (you can think of the index as the particle identification) to some specified location, or you could deactivate a particle with","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"first_particle = simulation.prognostic_variables.particles[1]\nsimulation.prognostic_variables.particles[1] = deactivate(first_particle)\nsimulation.prognostic_variables.particles","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"To actually advect these particles inside a SpeedyWeather simulation we have to create a ParticalAdvection2D instance that lets you control the time step used for particle advection and which vertical layer to use in the 3D models.","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"particle_advection = ParticleAdvection2D(spectral_grid, layer = 1)","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"we choose the first (=top-most) layer although this is the default anyway. Now we can advect our three particles we have defined above","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"model = BarotropicModel(spectral_grid; particle_advection)\nsimulation = initialize!(model)\nsimulation.prognostic_variables.particles","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"Which are the initial conditions for our three particles. After 10 days of simulation they have changed","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"run!(simulation, period=Day(10))\nsimulation.prognostic_variables.particles","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"Woohoo! We just advected some particles. This is probably not as exciting as actually tracking the particles over the globe and being able to visualise their trajectory which we will do in the next section","category":"page"},{"location":"particles/#Tracking-particles","page":"Particle advection","title":"Tracking particles","text":"","category":"section"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"A ParticleTracker is implemented as a callback, see Callbacks, outputting the particle locations via netCDF. We can create it like","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(nparticles = 100, nlayers=1)\nparticle_tracker = ParticleTracker(spectral_grid, schedule=Schedule(every=Hour(3)))","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"which would output every 3 hours (the default). This output frequency might be slightly adjusted depending on the time step of the dynamics to output every n time steps (an @info is thrown if that is the case), see Schedules. Further options on compression are available as keyword arguments ParticleTracker(spectral_grid, keepbits=15) for example. The callback is then added after the model is created","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"particle_advection = ParticleAdvection2D(spectral_grid)\nmodel = ShallowWaterModel(spectral_grid; particle_advection)\nadd!(model.callbacks, particle_tracker)","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"which will give it a random key too in case you need to remove it again (more on this in Callbacks). If you now run the simulation the particle tracker is called on particle_tracker.every_n_timesteps and it continuously writes into particle_tracker.netcdf_file which is placed in the run folder similar to other NetCDF output. For example, the run id can be obtained after the simulation by model.output.id.","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"simulation = initialize!(model)\nrun!(simulation, period=Day(10))\nmodel.output.id","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"so that you can read the netCDF file with","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"using NCDatasets\nrun_id = \"run_$(model.output.id)\" # create a run_???? string with output id\npath = joinpath(run_id, particle_tracker.file_name) # by default \"run_????/particles.nc\"\nds = NCDataset(path)\nds[\"lon\"]\nds[\"lat\"]","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"where the last two lines are lazy loading a matrix with each row a particle and each column a time step. You may do ds[\"lon\"][:,:] to obtain the full Matrix. We had specified spectral_grid.nparticles above and we will have time steps in this file depending on the period the simulation ran for and the particle_tracker.Δt output frequency. We can visualise the particles' trajectories with","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"lon = ds[\"lon\"][:,:]\nlat = ds[\"lat\"][:,:]\nnparticles = size(lon,1)\n\nusing CairoMakie\nfig = lines(lon[1, :], lat[1, :]) # first particle only\n[lines!(fig.axis, lon[i,:], lat[i,:]) for i in 2:nparticles] # add lines for other particles\n\n# display updated figure\nfig\nsave(\"particles.png\", fig) # hide\nnothing # hide","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"(Image: Particle trajectories)","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"Instead of providing a polished example with a nice projection we decided to keep it simple here because this is probably how you will first look at your data too. As you can see, some particles in the Northern Hemisphere have been advected with a zonal jet and perform some wavy motions as the jet does too. However, there are also some horizontal lines which are automatically plotted when a particles travels across the prime meridian 0˚E = 360˚E. Ideally you would want to use a more advanced projection and plot the particle trajectories as geodetics. ","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"With GeoMakie.jl you can do","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"using GeoMakie, CairoMakie\n\nfig = Figure()\nga = GeoAxis(fig[1, 1]; dest = \"+proj=ortho +lon_0=45 +lat_0=45\")\n[lines!(ga, lon[i,:], lat[i,:]) for i in 1:nparticles]\nfig\nsave(\"particles_geomakie.png\", fig) # hide\nnothing # hide","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"(Image: Particle advection)","category":"page"},{"location":"forcing_drag/#Custom-forcing-and-drag","page":"Forcing and drag","title":"Custom forcing and drag","text":"","category":"section"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"beginaligned\nfracpartial zetapartial t + nabla cdot (mathbfu(zeta + f)) =\nS - rzeta - nunabla^4zeta \nS_l m^i = A(1-exp(-2tfracDelta ttau))Q^i_l m + exp(-tfracdttau)S_l m^i-1 \nendaligned","category":"page"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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 -1 1 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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"using SpeedyWeather\n\n@kwdef struct StochasticStirring{NF} <: SpeedyWeather.AbstractForcing\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::Second = Day(2)\n\n \"Stirring strength A [1/s²]\"\n strength::NF = 2e-11\n\n \"Stirring latitude [˚N]\"\n latitude::NF = 45\n\n \"Stirring width [˚]\"\n width::NF = 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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"So, first the scalar parameters, are added as fields of type NF (you could harcode Float64 too) 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 @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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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 (it's position) and a width which are therefore also added to the definition of StochasticStirring.","category":"page"},{"location":"forcing_drag/#Custom-forcing:-generator-function","page":"Forcing and drag","title":"Custom forcing: generator function","text":"","category":"section"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"Cool. Now you could create our new StochasticStirring forcing with StochasticStirring{Float64}(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 and in double precision Float64. 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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"function StochasticStirring(SG::SpectralGrid; kwargs...)\n (; trunc, nlat) = SG\n return StochasticStirring{SG.NF}(; trunc, nlat, kwargs...)\nend","category":"page"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"Which allows us to do","category":"page"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"spectral_grid = SpectralGrid(trunc=42, nlayers=1)\nstochastic_stirring = StochasticStirring(spectral_grid, latitude=30, decorrelation_time=Day(5))","category":"page"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/#Custom-forcing:-initialize","page":"Forcing and drag","title":"Custom forcing: initialize","text":"","category":"section"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"function SpeedyWeather.initialize!( forcing::StochasticStirring,\n model::AbstractModel)\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.value # in 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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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 AbstractModel 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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/#Custom-forcing:-forcing!-function","page":"Forcing and drag","title":"Custom forcing: forcing! function","text":"","category":"section"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"function SpeedyWeather.forcing!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n forcing::StochasticStirring,\n lf::Integer,\n model::AbstractModel,\n)\n # function barrier only\n forcing!(diagn, forcing, model.spectral_transform)\nend","category":"page"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"The function signature (types and number of its arguments) has to be as outlined above. The first argument has to be of type DiagnosticVariables as the diagnostic variables, are the ones you want to change (likely the tendencies within) to apply a forcing. But technically you can change anything else too, although the results may be unexpected. The diagnostic variables contain the current model state in grid-point space and the tendencies (in grid and spectral space). The second argument has to be of type PrognosticVariables because, in general, the forcing may use (information from) the prognostic variables in spectral space, which includes in progn.clock.time the current time for time-dependent forcing. But all prognostic variables should be considered read-only. The third argument has to be of the type of our new custom forcing, here StochasticStirring, so that multiple dispatch calls the correct method of forcing!. The forth argument is of type AbstractModel, so that the forcing can also make use of anything inside model, e.g. model.geometry or model.planet etc. But you can be more restrictive to define a forcing only for the BarotropicModel for example, use modelBarotropic in that case. Or you could define two methods, one for Barotropic one for all other models with AbstractModel (not Barotropic as a more specific method is prioritised with multiple dispatch). The 5th argument is the leapfrog index lf which after the first time step will be lf=2 to denote that tendencies are evaluated at the current time not at the previous time (how leapfrogging works). Unless you want to read the prognostic variables, for which you need to know whether to read lf=1 or lf=2, you can ignore this (but need to include it as argument).","category":"page"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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 and clarity reasons: model might have abstract fields which the compiler cannot optimize for, but unpacking them makes that possible. And it also tells you more clearly what a function depends on. So we define the actual forcing! function that's then called as follows","category":"page"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"function forcing!(\n diagn::DiagnosticVariables,\n forcing::StochasticStirring{NF},\n spectral_transform::SpectralTransform\n) 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.a_grid # use scratch array \"a\"\n transform!(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 transform!(vor_tend, S_grid, spectral_transform)\n\n return nothing\nend","category":"page"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"Now this is actually where we implement the equation we started from in Custom forcing and drag 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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/#Custom-forcing:-model-construction","page":"Forcing and drag","title":"Custom forcing: model construction","text":"","category":"section"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"Now that we have defined a new forcing, as well as how to initialize it and what it is supposed to execute on every time step, we also want to use it. We generally follow other Examples, start with the SpectralGrid and use that to get an instance of StochasticStirring. 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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"spectral_grid = SpectralGrid(trunc=85, nlayers=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)\n\n# visualisation\nusing CairoMakie\nvor = simulation.diagnostic_variables.grid.vor_grid[:, 1]\nheatmap(vor, title=\"Stochastically stirred vorticity\")\nsave(\"stochastic_stirring.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"(Image: Stochastic stirring)","category":"page"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/#Custom-drag","page":"Forcing and drag","title":"Custom drag","text":"","category":"section"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"From the barotropic vorticity equation in Custom forcing and drag 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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"struct MyDrag <: SpeedyWeather.AbstractDrag\n # parameters and arrays\nend","category":"page"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"In general, these are the fields you can write into for new terms","category":"page"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"These space restrictions exist because of the way how SpeedyWeather transforms between spaces to obtain tendencies.","category":"page"},{"location":"initial_conditions/#Initial-conditions","page":"Initial conditions","title":"Initial conditions","text":"","category":"section"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"The following showcases some examples of how to set the initial conditions for the prognostic variables in SpeedyWeather.jl. In essence there are three ways to do this","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"Change the arrays in simulation.prognostic_variables\nUse the set! function\nSet the initial_conditions component of a model","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"where 1 is a rather low-level and largely requires you to directly set the complex coefficients of the spherical harmonics (advanced!). So the set! function builds a convenient interface around 1 such that you don't have to know about details of grid or spectral space. 3 then collects method 1 or 2 (or a combination of both) into a single struct to \"save\" some initial conditions for one or several variables. This lets you use predefined (inside SpeedyWeather or externally) initial conditions as easy as initial_conditions = RossbyHaurwitzWave(). Let us illustrate this with some examples where we will refer back those methods simply as 1, 2, 3.","category":"page"},{"location":"initial_conditions/#Rossby-Haurwitz-wave-in-a-BarotropicModel","page":"Initial conditions","title":"Rossby-Haurwitz wave in a BarotropicModel","text":"","category":"section"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"We define a BarotropicModel of some resolution but keep all its components as default","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=63, nlayers=1)\nmodel = BarotropicModel(spectral_grid)\nsimulation = initialize!(model)","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"Now simulation.prognostic_variables contains already some initial conditions as defined by model.initial_conditions (that's method 3). Regardless of what those are, we can still mutate them before starting a simulation, but if you (re-)initialize the model, the initial conditions will be set back to what is defined in model.initial_conditions. We will illustrate the set! function now that conveniently lets you (re)set the current state of the prognostic variables:","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"The Rossby-Haurwitz wave[Williamson92] is defined as an initial condition for vorticity zeta (which is the sole prognostic variable in the barotropic vorticity model) as","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"ζ(λ θ) = 2ω*sin(θ) - K*sin(θ)*cos(θ)^m*(m^2 + 3m + 2)*cos(m*λ)","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"with longitude lambda and latitude theta. The parameters are zonal wavenumber (order) m = 4, frequencies omega = 7848e-6s^-1 K = 7848e-6s^-1. Now setting these initial conditions is as simple as","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"m = 4\nω = 7.848e-6\nK = 7.848e-6\n\nζ(λ, θ, σ) = 2ω*sind(θ) - K*sind(θ)*cosd(θ)^m*(m^2 + 3m + 2)*cosd(m*λ)\nset!(simulation, vor=ζ)","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"with only two difference from the mathematical notation. (1) SpeedyWeather's coordinates are in degrees, so we replaced sin cos with sind and cosd; and (2) To generalise to vertical coordinates, the function ζ(λ, θ, σ) takes exactly three arguments, with σ denoting the vertical Sigma coordinates. This is important so that we can use the same definition of initial conditions for the 2D barotropic vorticity model also for the 3D primitive equations.","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"One may filter out low values of spectral vorticity with some cut-off amplitude c = 10^-10, just to illustrate how you would do this (example for method 1)","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"c = 1e-10 # cut-off amplitude\n\n# 1 = first leapfrog timestep of spectral vorticity\nvor = simulation.prognostic_variables.vor[1]\nlow_values = abs.(vor) .< c\nvor[low_values] .= 0\nnothing # hide","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"which is just treating vor as an array of something and tweaking the values within!","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"Let us illustrate these initial conditions. set! will set the initial conditions in spectral space, taking care of the transform from the equation defined in grid coordinates. So to show vorticity again in grid space we transform back","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"# [1] for first leapfrog time step, [:, 1] for all values on first layer\nvor = simulation.prognostic_variables.vor[1][:, 1]\nvor_grid = transform(vor)\n\nusing CairoMakie\nheatmap(vor_grid, title=\"Relative vorticity [1/s] of Rossby-Haurwitz wave\")\n\nsave(\"haurwitz.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"(Image: Rossby-Haurwitz wave)","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"That's the Rossby-Haurwitz wave! This wave is supposed to travel (without changing its shape) eastward around the globe, so let us run a simulation for some days","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"run!(simulation, period=Day(3))\n\n# a running simulation always transforms spectral variables\n# so we don't have to do the transform manually but just pull \n# layer 1 (there's only 1) from the diagnostic variables\nvor = simulation.diagnostic_variables.grid.vor_grid[:, 1]\n\nheatmap(vor, title=\"Relative vorticity [1/s], Rossby Haurwitz wave after 3 days\")\nsave(\"haurwitz_day10.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"(Image: Rossby-Haurwitz wave after day 10)","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"Looks like before, but shifted eastward! That's the Rossby-Haurwitz wave. The set! function accepts not just a function as outlined above, but also scalars, like set!(simulation, div=0) (which is always the case in the BarotropicModel) or grids, or LowerTriangularArrays representing variables in the spectral space. See ?set!, the set! docstring for more details.","category":"page"},{"location":"initial_conditions/#Rossby-Haurwitz-wave-in-a-ShallowWater-model","page":"Initial conditions","title":"Rossby-Haurwitz wave in a ShallowWater model","text":"","category":"section"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"For the shallow water model Williamson et al., 1992[Williamson92] also give initial conditions for the prognostic variable height h = h_0 + eta (equivalent to geopotential). The layer thickness is h_0 and eta is the interface displacement of that layer. SpeedyWeather however, uses as prognostic variable eta for which the initial conditions are","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"beginalign\nη(λ θ) = fracR^2g left( A(θ) + B(θ) cos(mλ) + C(θ) cos(2mλ) right) \n\nA(θ) = fracω(2Ω + ω)2cos(θ)^2 + frac14K^2cos(θ)^2mleft((m+1)cos(θ)^2 + (2m^2 - m - 2) - frac2m^2cos(θ)^2right) \n\nB(θ) = frac2(Ω + ω)K(m+1)(m+2) cos(θ)^mleft((m^2 + 2m + 2) - (m+1)^2cos(θ)^2right) \n\nC(θ) = frac14K^2 cos(θ)^2mleft((m+1)cos(θ)^2 - (m + 2)right)\n\nendalign","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"Where R is the radius of the planet on which we consider the Rossby-Haurwitz wave, this value can be found in model.spectral_grid.radius and Ω and g are the rotation and the gravity constant of the planet, which can be found in model.planet.rotation and model.planet.gravity.","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"The interface displacement eta in SpeedyWeather's ShallowWaterModel is stored in the variable pres in analogy to the actual pressure in the PrimitiveEquation model. So we can set eta using set!(simulation, pres=η) for an appropriate implementation of the above equations, similar to how ζ(λ, θ, σ) = is defined above. However, we also already defined RossbyHaurwitzWave to do exactly that. With the following we can do a test run of the Rossby-Haurwitz wave in the shallow water model without any influences from orography.","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"spectral_grid = SpectralGrid(trunc=63, nlayers=1)\ninitial_conditions = RossbyHaurwitzWave()\norography = NoOrography(spectral_grid)\nmodel = ShallowWaterModel(; spectral_grid, initial_conditions, orography)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(8))\n\nvor = simulation.diagnostic_variables.grid.vor_grid[:, 1]\nheatmap(vor, title=\"Relative vorticity [1/s], shallow water Rossby Haurwitz wave after 8 days\")\nsave(\"haurwitz_sw.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"There is a noticable difference from the result in the barotropic model, where the wave moves around the globe keeping its shape. Here, it deforms around the poles and the vorticity patches develop an internal structure.","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"(Image: Rossby-Haurwitz wave in the shallow water model)","category":"page"},{"location":"initial_conditions/#Rossby-Haurwitz-wave-in-primitive-equations","page":"Initial conditions","title":"Rossby-Haurwitz wave in primitive equations","text":"","category":"section"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"You can use set! or the predefined RossbyHaurwitzWave also in other models. For the BarotropicModel or the PrimitiveDryModel (or Wet) the definition for eta is skipped. The barotropic model does not have a pressure variable, the primitive equation model uses the logarithm of surface pressure, which is incompatible with eta being defined as an interface displacement. So here, the RossbyHaurwitzWave only includes relative vorticity in the initial conditions.","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"The following shows how you can use RossbyHaurwitzWave in a PrimitiveDryModel (or Wet) but you probably also want to set initial conditions for temperature and pressure to not start at zero Kelvin and zero pressure. Also no orography, and let's switch off all physics parameterizations with physics=false.","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"spectral_grid = SpectralGrid(trunc=42, nlayers=8)\ninitial_conditions = InitialConditions(\n vordiv=RossbyHaurwitzWave(),\n temp=JablonowskiTemperature(),\n pres=PressureOnOrography())\n\norography = NoOrography(spectral_grid)\ntime_stepping = Leapfrog(spectral_grid, Δt_at_T31=Minute(30)) # 30min timestep scaled linearly\n\nmodel = PrimitiveDryModel(spectral_grid; time_stepping, initial_conditions, orography, physics=false)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(5))\nnothing # hide","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"Note that we chose a lower resolution here (T42) as we are simulating 8 vertical layers now too. Let us visualise the surface vorticity ([:, 8] is the lowermost layer)","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"vor = simulation.diagnostic_variables.grid.vor_grid[:, 8]\nheatmap(vor, title=\"Relative vorticity [1/s], primitive Rossby-Haurwitz wave\")\n\nsave(\"haurwitz_primitive.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"(Image: Rossby-Haurwitz wave in primitive equations)","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"As you can see the actual Rossby-Haurwitz wave is not as stable anymore (because those initial conditions are not a stable solution of the primitive equations) and so the 3-day integration looks already different from the barotropic model!","category":"page"},{"location":"initial_conditions/#References","page":"Initial conditions","title":"References","text":"","category":"section"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"[Williamson92]: DL Williamson, JB Drake, JJ Hack, R Jakob, PN Swarztrauber, 1992. A standard test set for numerical approximations to the shallow water equations in spherical geometry, Journal of Computational Physics, 102, 1, DOI: 10.1016/S0021-9991(05)80016-6","category":"page"},{"location":"how_to_run_speedy/#How-to-run-SpeedyWeather.jl","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather.jl","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","text":"In the following we will describe these steps in more detail. If you want to start with some examples first, see Examples 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","title":"SpectralGrid","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","text":"The default SpectralGrid is","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"using SpeedyWeather\nspectral_grid = SpectralGrid()","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","text":"spectral_grid = SpectralGrid(trunc=85)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","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","title":"Vertical coordinates and resolution","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"The number of vertical layers or levels (we use both terms often interchangeably) is determined through the nlayers 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","title":"How to run SpeedyWeather","text":"spectral_grid = SpectralGrid(nlayers=1)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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 0 1","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"vertical_coordinates = SigmaCoordinates(0:0.2:1)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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","title":"Creating model components","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","text":"model = ShallowWaterModel(spectral_grid)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","text":"model.time_stepping","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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. All time inputs are supposed to be given with the help of Dates (e.g. Minute(), Hour(), ...). But remember that (almost) every model component depends on a SpectralGrid as first argument.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"spectral_grid = SpectralGrid(trunc=63, nlayers=1)\ntime_stepping = Leapfrog(spectral_grid, Δt_at_T31=Minute(15))","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","text":"model = ShallowWaterModel(spectral_grid; time_stepping)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"This logic continues for all model components, see 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","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","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","title":"Creating a model","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"SpeedyWeather.jl currently includes 4 different models:","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"BarotropicModel for the 2D barotropic vorticity equation,\nShallowWaterModel for the 2D shallow water equations,\nPrimitiveDryModel for the 3D primitive equations without humidity, and\nPrimitiveWetModel for the 3D primitive equations with humidity.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"Overall, all above-mentioned models 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","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","text":"or model = ShallowWaterModel(spectral_grid; ...), etc.","category":"page"},{"location":"how_to_run_speedy/#initialize","page":"How to run SpeedyWeather","title":"Model initialization","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","text":"simulation = initialize!(model)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"and we have initialized the ShallowWaterModel we have defined earlier. As initialize!(model) also initializes the prognostic (and diagnostic) variables, it also initializes the clock in simulation.prognostic_variables.clock. To initialize with a specific time, do","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"simulation = initialize!(model, time=DateTime(2020,5,1))\nsimulation.prognostic_variables.clock.time","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"to set the time to 1st May, 2020 (but you can also do that manually). This time is used by components that depend on time, e.g. the solar zenith angle calculation.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"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 like so","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"simulation.model.output.output_dt = Second(3600)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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!(::AbstractModel), but you can also change them now, before the model runs ","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"simulation.prognostic_variables.vor[1][1, 1] = 0","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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","title":"Run a simulation","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","text":"run!(simulation)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"By default this runs for 10 days without output and returns a unicode plot of surface relative vorticity (see Shallow water with mountains for more on this). Now period and output are the only options to change, so with","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"model.output.id = \"test\" # hide\nrun!(simulation, period=Day(5), output=true)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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":"Spectral transforms","title":"SpeedyTransforms","text":"","category":"section"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","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/#Notation:-Spectral-resolution","page":"Spectral transforms","title":"Notation: Spectral resolution","text":"","category":"section"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"There are different ways to describe the spectral resolution, the truncation wavenumber (e.g. T31), the maximum degree l and order m of the spherical harmonics (e.g. l_max=31, m_max = 31), or the size of the lower triangular matrix, e.g. 32x32. In this example, they are all equivalent. We often use the truncation, i.e. T31, for brevity but sometimes it is important to describe degree and order independently (see for example One more degree for spectral fields). Note also how truncation, degree and order are 0-based, but matrix sizes are 1-based. ","category":"page"},{"location":"speedytransforms/#Example-transform","page":"Spectral transforms","title":"Example transform","text":"","category":"section"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","text":"using SpeedyWeather.RingGrids\nusing SpeedyWeather.LowerTriangularMatrices\nusing SpeedyWeather.SpeedyTransforms","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"As an example, we want to transform the l=m=1 spherical harmonic from spectral space in alms to grid-point space. Note, the +1 on both degree (first index) and order (second index) for 0-based harmonics versus 1-based matrix indexing, see Size of LowerTriangularArray. Create a LowerTriangularMatrix for T5 resolution, i.e. 6x6 matrix size","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"alms = zeros(LowerTriangularMatrix{ComplexF64}, 6, 6) # spectral coefficients T5\nalms[2, 2] = 1 # only l=1, m=1 harmonic\nalms","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"Now transform is the function that takes spectral coefficients alms and converts them a grid-point space map (or vice versa)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"map = transform(alms)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"By default, the transforms 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":"Spectral transforms","title":"Spectral transforms","text":"import SpeedyWeather.RingGrids: plot # not necessary when `using SpeedyWeather`\nplot(map)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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 transform","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"alms2 = transform(map)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","text":"alms ≈ alms2","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Transform onto another grid","text":"","category":"section"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","text":"map = transform(alms, Grid=HEALPixGrid)\nplot(map)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"which, if transformed back, however, can yield a larger transform error as discussed above","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"transform(map)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"On such a coarse grid the transform error (absolute and relative) is about 10^-2, this decreases for higher resolution. The transform function 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":"Spectral transforms","title":"Spectral transforms","text":"spectral_truncation(alms, 2)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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 you want to transform onto, use the keyword dealiasing (default: 2 for quadratic, see Matching spectral and grid resolution). But you can also provide a SpectralTransform instance to reuse a precomputed spectral transform. More on that now.","category":"page"},{"location":"speedytransforms/#SpectralTransform","page":"Spectral transforms","title":"The SpectralTransform struct","text":"","category":"section"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"The function transform only with arguments as shown above, will 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":"Spectral transforms","title":"Spectral transforms","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(NF=Float32, trunc=5, Grid=OctahedralGaussianGrid, dealiasing=3)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","text":"S = SpectralTransform(spectral_grid)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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.","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"Passing on S the SpectralTransform now allows us to transform directly on the grid defined therein. Note that we recreate alms to be of size 7x6 instead of 6x6 for T5 spectral resolution because SpeedyWeather uses internally One more degree for spectral fields meaning also that's the default when creating a SpectralTransform from a SpectralGrid. But results don't change if the last degree (row) contains only zeros.","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"alms = zeros(LowerTriangularMatrix{ComplexF64}, 7, 6) # spectral coefficients\nalms[2, 2] = 1 # only l=1, m=1 harmonic\n\nmap = transform(alms, S)\nplot(map)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","text":"alms2 = transform(map, S)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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). While 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":"Spectral transforms","title":"Spectral transforms","text":"spectral_truncation(alms2, 5, 5)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","text":"S = SpectralTransform(spectral_grid, one_more_degree=false)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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/#SpectralTransform-generators","page":"Spectral transforms","title":"SpectralTransform generators","text":"","category":"section"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"While you can always create a SpectralTransform from a SpectralGrid (which defines both spectral and grid space) there are other constructors/generators available:","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"SpectralTransform(alms)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"Now we have defined the resolution of the spectral space through alms but create a SpectralTransform by making assumption about the grid space. E.g. Grid=FullGaussianGrid by default, dealiasing=2 and nlat_half correspondingly. But you can also pass them on as keyword arguments, for example","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"SpectralTransform(alms, Grid=OctahedralClenshawGrid, nlat_half=24)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"Only note that you don't want to specify both nlat_half and dealiasing as you would otherwise overspecify the grid resolution (dealiasing will be ignored in that case). This also works starting from the grid space","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"grid = rand(FullClenshawGrid, 12)\nSpectralTransform(grid)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"where you can also provide spectral resolution trunc or dealiasing. You can also provide both a grid and a lower triangular matrix to describe both spaces","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"SpectralTransform(grid, alms)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"and you will precompute the transform between them. For more generators see the docstrings at ?SpectralTransform.","category":"page"},{"location":"speedytransforms/#Power-spectrum","page":"Spectral transforms","title":"Power spectrum","text":"","category":"section"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","text":"alms = randn(LowerTriangularMatrix{Complex{Float32}}, 32, 32) # hide\nspectral_truncation!(alms, 10) # hide\nmap = transform(alms, Grid=FullClenshawGrid) # hide\nm = Matrix(map) # hide\nm","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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 (in analogy to the Gaussian grid based on the Gaussian quadrature). Note that for the spectral transform this should not include the poles, so the 96x47 matrix size here corresponds to 23 latitudes north and south of the Equator respectively plus the equator (=47).","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"We now wrap this matrix into a FullClenshawGrid (input_as=Matrix is required because all grids organise their data as vectors, see Creating data on a RingGrid) therefore to associate it with the necessary grid information like its coordinates","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"map = FullClenshawGrid(m, input_as=Matrix)\n\nusing CairoMakie\nheatmap(map)\nsave(\"random_pattern.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"(Image: Random pattern)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"Now we transform into spectral space and call power_spectrum(::LowerTriangularMatrix)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"alms = transform(map)\npower = power_spectrum(alms)\nnothing # hide","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Example: Creating k^n-distributed noise","text":"","category":"section"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","text":"k = 1:32\nA = randn(Complex{Float32}, 32, 32)\nA .*= k.^-2\nalms = LowerTriangularArray(A)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"We first create a Julia Matrix so that the matrix-vector broadcasting .*= k is correctly applied across dimensions of A and then convert to a LowerTriangularMatrix.","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","text":"map = transform(alms)\n\nusing CairoMakie\nheatmap(map, title=\"k⁻²-distributed noise\")\nsave(\"random_noise.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"(Image: Random noise)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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/#Precomputed-polynomials-and-allocated-memory","page":"Spectral transforms","title":"Precomputed polynomials and allocated memory","text":"","category":"section"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"info: Reuse `SpectralTransform`s wherever possible\nDepending on horizontal and vertical resolution of spectral and grid space, a SpectralTransform can be become very large in memory. Also the recomputation of the polynomials and the planning of the FFTs are expensive compared to the actual transform itself. Therefore reuse a SpectralTransform wherever possible.","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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. It is therefore advised to reuse a precomputed SpectralTransform object wherever possible. This prevents transforms to allocate large memory which would otherwise be garbage collected again after the transform.","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"You get information about the size of that memory (both polynomials and required scratch memory) in the terminal \"show\" of a SpectralTransform object, e.g. at T127 resolution with 8 layers these are","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"spectral_grid = SpectralGrid(trunc=127, nlayers=8)\nSpectralTransform(spectral_grid)","category":"page"},{"location":"speedytransforms/#Batched-Transforms","page":"Spectral transforms","title":"Batched Transforms","text":"","category":"section"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"SpeedyTransforms also supports batched transforms. With batched input data the transform is performed along the leading dimension, and all further dimensions are interpreted as batch dimensions. Take for example ","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"alms = randn(LowerTriangularMatrix{Complex{Float32}}, 32, 32, 5) \ngrids = transform(alms)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"In this case we first randomly generated five (32x32) LowerTriangularMatrix that hold the coefficients and then transformed all five matrices batched to the grid space with the transform command, yielding 5 RingGrids with each 48-rings. ","category":"page"},{"location":"speedytransforms/#Batched-power-spectra","page":"Spectral transforms","title":"Batched power spectra","text":"","category":"section"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"SpeedyTransforms also supports power spectra calculated over any additional dimensions to the leading spherical harmonic dimension (it is unravelled as a vector so the first only, not the first two...). But the power spectrum is always calculated along that first spherical-harmonic dimension. For example","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"alms = randn(LowerTriangularMatrix{Complex{Float32}}, 5, 5, 2) \npower_spectrum(alms)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"returns the power spectrum for [..., 1] in the first column and [..., 2] in the second. This avoids to loop over these additional dimensions, but the result would be the same:","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"power_spectrum(alms[:, 1])","category":"page"},{"location":"speedytransforms/#Functions-and-type-index","page":"Spectral transforms","title":"Functions and type index","text":"","category":"section"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"Modules = [SpeedyWeather.SpeedyTransforms]","category":"page"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.AbstractLegendreShortcut","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.AbstractLegendreShortcut","text":"Legendre shortcut is the truncation of the loop over the order m of the spherical harmonics in the Legendre transform. For reduced grids with as few as 4 longitudes around the poles (HEALPix grids) or 20 (octahedral grids) the higher wavenumbers in large orders m do not project (significantly) onto such few longitudes. For performance reasons the loop over m can therefore but truncated early. A Legendre shortcut <: AbstractLegendreShortcut is implemented as a functor that returns the 0-based maximum order m to retain per latitude ring, i.e. to be used for m in 0:mmax_truncation.\n\nNew shortcuts can be added by defining struct LegendreShortcutNew <: AbstractLegendreShortcut end and the functor method LegendreShortcutNew(nlon::Integer, lat::Real) = ..., with nlon the number of longitude points on that ring, and latd its latitude in degrees (-90˚ to 90˚N). Many implementations may not use the latitude latd but it is included for compatibility. If unused set to default value to 0. Also define short_name(::Type{<:LegendreShortcutNew}) = \"new\".\n\nImplementions are LegendreShortcutLinear, LegendreShortcutQuadratic, LegendreShortcutCubic, LegendreShortcutLinQuadCosLat² and LegendreShortcutLinCubCoslat.\n\n\n\n\n\n","category":"type"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.AssociatedLegendrePolArray","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.AssociatedLegendrePolArray","text":"AssociatedLegendrePolArray{T, N, M, V} <: AbstractArray{T,N}\n\nType that wraps around a LowerTriangularArray{T,M,V} but is a subtype of AbstractArray{T,M+1}. This enables easier use with AssociatedLegendrePolynomials.jl which otherwise couldn't use the \"matrix-style\" (l, m) indexing of LowerTriangularArray. This type however doesn't support any other operations than indexing and is purerly intended for internal purposes. \n\ndata::LowerTriangularArray{T, M, V} where {T, M, V}\n\n\n\n\n\n","category":"type"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.AssociatedLegendrePolMatrix","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.AssociatedLegendrePolMatrix","text":"2-dimensional AssociatedLegendrePolArray of type Twith its non-zero entries unravelled into aVector{T}`\n\n\n\n\n\n","category":"type"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.LegendreShortcutCubic","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.LegendreShortcutCubic","text":"LegendreShortcutCubic(nlon::Integer) -> Any\nLegendreShortcutCubic(nlon::Integer, latd::Real) -> Any\n\n\nCubic Legendre shortcut, truncates the Legendre loop over order m to nlon/4.\n\n\n\n\n\n","category":"type"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.LegendreShortcutLinCubCoslat-Tuple{Integer, Real}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.LegendreShortcutLinCubCoslat","text":"LegendreShortcutLinCubCoslat(\n nlon::Integer,\n latd::Real\n) -> Any\n\n\nLinear-Cubic Legendre shortcut, truncates the Legendre loop over order m to nlon/(2 + 2cosd(latd)).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.LegendreShortcutLinQuadCoslat²-Tuple{Integer, Real}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.LegendreShortcutLinQuadCoslat²","text":"LegendreShortcutLinQuadCoslat²(\n nlon::Integer,\n latd::Real\n) -> Any\n\n\nLinear-Quadratic Legendre shortcut, truncates the Legendre loop over order m to nlon/(2 + cosd(latd)^2).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.LegendreShortcutLinear","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.LegendreShortcutLinear","text":"LegendreShortcutLinear(nlon::Integer) -> Any\nLegendreShortcutLinear(nlon::Integer, latd::Real) -> Any\n\n\nLinear Legendre shortcut, truncates the Legendre loop over order m to nlon/2.\n\n\n\n\n\n","category":"type"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.LegendreShortcutQuadratic","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.LegendreShortcutQuadratic","text":"LegendreShortcutQuadratic(nlon::Integer) -> Any\nLegendreShortcutQuadratic(nlon::Integer, latd::Real) -> Any\n\n\nQuadratic Legendre shortcut, truncates the Legendre loop over order m to nlon/3.\n\n\n\n\n\n","category":"type"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.SpectralTransform","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.SpectralTransform","text":"SpectralTransform struct that contains all parameters and precomputed arrays to perform a spectral transform. Fields are\n\nGrid::Type{<:AbstractGridArray}\nnlat_half::Int64\nnlayers::Int64\nlmax::Int64\nmmax::Int64\nnfreq_max::Int64\nLegendreShortcut::Type{<:SpeedyWeather.SpeedyTransforms.AbstractLegendreShortcut}\nmmax_truncation::Vector{Int64}\nnlon_max::Int64\nnlons::Vector{Int64}\nnlat::Int64\ncoslat::Any\ncoslat⁻¹::Any\nlon_offsets::Any\nnorm_sphere::Any\nrfft_plans::Vector{AbstractFFTs.Plan}\nbrfft_plans::Vector{AbstractFFTs.Plan}\nrfft_plans_1D::Vector{AbstractFFTs.Plan}\nbrfft_plans_1D::Vector{AbstractFFTs.Plan}\nlegendre_polynomials::Any\nscratch_memory_north::Any\nscratch_memory_south::Any\nscratch_memory_grid::Any\nscratch_memory_spec::Any\nscratch_memory_column_north::Any\nscratch_memory_column_south::Any\njm_index_size::Int64\nkjm_indices::Any\nsolid_angles::Any\ngrad_y1::Any\ngrad_y2::Any\ngrad_y_vordiv1::Any\ngrad_y_vordiv2::Any\nvordiv_to_uv_x::Any\nvordiv_to_uv1::Any\nvordiv_to_uv2::Any\neigenvalues::Any\neigenvalues⁻¹::Any\n\n\n\n\n\n","category":"type"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.SpectralTransform-Union{Tuple{AbstractGridArray{NF, N, ArrayType}}, Tuple{ArrayType}, Tuple{N}, Tuple{NF}} where {NF, N, ArrayType}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.SpectralTransform","text":"SpectralTransform(\n grids::AbstractGridArray{NF, N, ArrayType};\n trunc,\n dealiasing,\n one_more_degree,\n kwargs...\n) -> SpectralTransform{NF, _A, _B, _C, _D, _E, LowerTriangularArray{NF1, 1, _A1}, LowerTriangularArray{NF2, 2, _A2}} where {NF, _A, _B, _C, _D, _E, NF1, _A1, NF2, _A2}\n\n\nGenerator function for a SpectralTransform struct based on the size and grid type of grids. Use keyword arugments trunc, dealiasing (ignored if trunc is used) or one_more_degree to define the spectral truncation.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.SpectralTransform-Union{Tuple{ArrayType2}, Tuple{ArrayType1}, Tuple{N}, Tuple{NF2}, Tuple{NF1}, Tuple{AbstractGridArray{NF1, N, ArrayType1}, LowerTriangularArray{NF2, N, ArrayType2}}} where {NF1, NF2, N, ArrayType1, ArrayType2}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.SpectralTransform","text":"SpectralTransform(\n grids::AbstractGridArray{NF1, N, ArrayType1},\n specs::LowerTriangularArray{NF2, N, ArrayType2};\n kwargs...\n) -> SpectralTransform{NF, _A, _B, _C, _D, _E, LowerTriangularArray{NF1, 1, _A1}, LowerTriangularArray{NF2, 2, _A2}} where {NF, _A, _B, _C, _D, _E, NF1, _A1, NF2, _A2}\n\n\nGenerator function for a SpectralTransform struct to transform between grids and specs.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.SpectralTransform-Union{Tuple{LowerTriangularArray{NF, N, ArrayType}}, Tuple{ArrayType}, Tuple{N}, Tuple{NF}} where {NF, N, ArrayType}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.SpectralTransform","text":"SpectralTransform(\n specs::LowerTriangularArray{NF, N, ArrayType};\n nlat_half,\n dealiasing,\n kwargs...\n) -> SpectralTransform{NF, _A, _B, _C, _D, _E, LowerTriangularArray{NF1, 1, _A1}, LowerTriangularArray{NF2, 2, _A2}} where {NF, _A, _B, _C, _D, _E, NF1, _A1, NF2, _A2}\n\n\nGenerator function for a SpectralTransform struct based on the size of the spectral coefficients specs. Use keyword arguments nlat_half, Grid or deliasing (if nlat_half not provided) to define the grid.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.SpectralTransform-Union{Tuple{NF}, Tuple{Type{NF}, Integer, Integer, Integer}} where NF","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.SpectralTransform","text":"SpectralTransform(\n ::Type{NF},\n lmax::Integer,\n mmax::Integer,\n nlat_half::Integer;\n Grid,\n ArrayType,\n nlayers,\n LegendreShortcut\n) -> SpectralTransform{T, Array, Vector{T1}, Array{Complex{T2}, 1}, Array{Complex{T3}, 2}, Array{Complex{T4}, 3}, LowerTriangularArray{T5, 1, Vector{T6}}, LowerTriangularArray{T7, 2, Matrix{T8}}} where {T, T1, T2, T3, T4, T5, T6, T7, T8}\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 and quadrature weights.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.RingGrids.get_nlat_half","page":"Spectral transforms","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!-Tuple{LowerTriangularArray, LowerTriangularArray, LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.UV_from_vor!","text":"UV_from_vor!(\n U::LowerTriangularArray,\n V::LowerTriangularArray,\n vor::LowerTriangularArray,\n S::SpectralTransform;\n radius\n) -> Tuple{LowerTriangularArray, LowerTriangularArray}\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. Acts on the unit sphere, i.e. it omits any radius scaling as all inplace gradient operators, unless the radius keyword argument is provided.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.UV_from_vordiv!-Tuple{LowerTriangularArray, LowerTriangularArray, LowerTriangularArray, LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.UV_from_vordiv!","text":"UV_from_vordiv!(\n U::LowerTriangularArray,\n V::LowerTriangularArray,\n vor::LowerTriangularArray,\n div::LowerTriangularArray,\n S::SpectralTransform;\n radius\n) -> Tuple{LowerTriangularArray, LowerTriangularArray}\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. Acts on the unit sphere, i.e. it omits any radius scaling as all inplace gradient operators.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms._divergence!-Tuple{Any, LowerTriangularArray, LowerTriangularArray, LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms._divergence!","text":"_divergence!(\n kernel,\n div::LowerTriangularArray,\n u::LowerTriangularArray,\n v::LowerTriangularArray,\n S::SpectralTransform;\n radius\n) -> LowerTriangularArray\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. Acts on the unit sphere, i.e. it omits 1/radius scaling as all gradient operators, unless the radius keyword argument is provided.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms._fourier_batched!-Tuple{AbstractArray{<:Complex, 3}, AbstractArray{<:Complex, 3}, AbstractGridArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms._fourier_batched!","text":"_fourier_batched!(\n f_north::AbstractArray{<:Complex, 3},\n f_south::AbstractArray{<:Complex, 3},\n grids::AbstractGridArray,\n S::SpectralTransform\n)\n\n\n(Forward) Fast Fourier transform (grid to spectral) in zonal direction of grids, stored in scratch memories f_north, f_south to be passed on to the Legendre transform. Batched version that requires the number of vertical layers to be the same as precomputed in S. Not to be called directly, use transform! instead.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms._fourier_batched!-Tuple{AbstractGridArray, AbstractArray{<:Complex, 3}, AbstractArray{<:Complex, 3}, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms._fourier_batched!","text":"_fourier_batched!(\n grids::AbstractGridArray,\n g_north::AbstractArray{<:Complex, 3},\n g_south::AbstractArray{<:Complex, 3},\n S::SpectralTransform\n)\n\n\nInverse fast Fourier transform (spectral to grid) of Legendre-transformed inputs g_north and g_south to be stored in grids. Not to be called directly, use transform! instead.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms._fourier_serial!-Tuple{AbstractArray{<:Complex, 3}, AbstractArray{<:Complex, 3}, AbstractGridArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms._fourier_serial!","text":"_fourier_serial!(\n f_north::AbstractArray{<:Complex, 3},\n f_south::AbstractArray{<:Complex, 3},\n grids::AbstractGridArray,\n S::SpectralTransform\n)\n\n\n(Forward) Fast Fourier transform (grid to spectral) in zonal direction of grids, stored in scratch memories f_north, f_south to be passed on to the Legendre transform. Serial version that does not require the number of vertical layers to be the same as precomputed in S. Not to be called directly, use transform! instead.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms._fourier_serial!-Tuple{AbstractGridArray, AbstractArray{<:Complex, 3}, AbstractArray{<:Complex, 3}, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms._fourier_serial!","text":"_fourier_serial!(\n grids::AbstractGridArray,\n g_north::AbstractArray{<:Complex, 3},\n g_south::AbstractArray{<:Complex, 3},\n S::SpectralTransform\n)\n\n\n(Inverse) Fast Fourier transform (spectral to grid) of Legendre-transformed inputs g_north and g_south to be stored in grids. Serial version that does not require the number of vertical layers to be the same as precomputed in S. Not to be called directly, use transform! instead.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms._legendre!-Tuple{AbstractArray{<:Complex, 3}, AbstractArray{<:Complex, 3}, LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms._legendre!","text":"_legendre!(\n g_north::AbstractArray{<:Complex, 3},\n g_south::AbstractArray{<:Complex, 3},\n specs::LowerTriangularArray,\n S::SpectralTransform;\n unscale_coslat\n)\n\n\nInverse Legendre transform, batched in the vertical. Not to be used directly, but called from transform!.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms._legendre!-Tuple{LowerTriangularArray, AbstractArray{<:Complex, 3}, AbstractArray{<:Complex, 3}, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms._legendre!","text":"_legendre!(\n specs::LowerTriangularArray,\n f_north::AbstractArray{<:Complex, 3},\n f_south::AbstractArray{<:Complex, 3},\n S::SpectralTransform\n)\n\n\n(Forward) Legendre transform, batched in the vertical. Not to be used directly, but called from transform!.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.curl!-Tuple{LowerTriangularArray, LowerTriangularArray, LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.curl!","text":"curl!(\n curl::LowerTriangularArray,\n u::LowerTriangularArray,\n v::LowerTriangularArray,\n S::SpectralTransform;\n flipsign,\n add,\n kwargs...\n) -> LowerTriangularArray\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, otherwise curl is scaled. Acts on the unit sphere, i.e. it omits 1/radius scaling as all gradient operators unless the radius keyword argument is provided. 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{LowerTriangularArray, LowerTriangularArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.curl","text":"curl(\n u::LowerTriangularArray,\n v::LowerTriangularArray;\n kwargs...\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). Acts on the unit sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided. An example usage is therefore\n\nRingGrids.scale_coslat⁻¹!(u_grid)\nRingGrids.scale_coslat⁻¹!(v_grid)\nu = transform(u_grid)\nv = transform(v_grid)\nvor = curl(u, v, radius=6.371e6)\nvor_grid = transform(div)\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.curl-Union{Tuple{Grid}, Tuple{Grid, Grid}} where Grid<:AbstractGridArray","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.curl","text":"curl(\n u::AbstractGridArray,\n v::AbstractGridArray;\n kwargs...\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. Acts on the unit sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.divergence!-Tuple{LowerTriangularArray, LowerTriangularArray, LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.divergence!","text":"divergence!(\n div::LowerTriangularArray,\n u::LowerTriangularArray,\n v::LowerTriangularArray,\n S::SpectralTransform;\n flipsign,\n add,\n kwargs...\n) -> LowerTriangularArray\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, otherwise div is scaled. Acts on the unit sphere, i.e. it omits 1/radius scaling as all gradient operators, unless the radius keyword argument is provided. 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{LowerTriangularArray, LowerTriangularArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.divergence","text":"divergence(\n u::LowerTriangularArray,\n v::LowerTriangularArray;\n kwargs...\n) -> LowerTriangularArray\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). Acts on the unit sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided. An example usage is therefore\n\nRingGrids.scale_coslat⁻¹!(u_grid)\nRingGrids.scale_coslat⁻¹!(v_grid)\nu = transform(u_grid, one_more_degree=true)\nv = transform(v_grid, one_more_degree=true)\ndiv = divergence(u, v, radius = 6.371e6)\ndiv_grid = transform(div)\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.divergence-Union{Tuple{Grid}, Tuple{Grid, Grid}} where Grid<:AbstractGridArray","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.divergence","text":"divergence(\n u::AbstractGridArray,\n v::AbstractGridArray;\n kwargs...\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. Acts on the unit sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided.\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","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.get_recursion_factors","text":"get_recursion_factors(\n _::Type{NF},\n lmax::Int64,\n mmax::Int64\n) -> LowerTriangularArray\n\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":"Spectral transforms","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.is_power_2-Tuple{Integer}","page":"Spectral transforms","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.ismatching-Tuple{SpectralTransform, AbstractGridArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.ismatching","text":"ismatching(\n S::SpectralTransform,\n grid::AbstractGridArray\n) -> Any\n\n\nSpectral transform S and grid match if the resolution nlat_half and the type of the grid match and the number of vertical layers is equal or larger in the transform (constraints due to allocated scratch memory size).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.ismatching-Tuple{SpectralTransform, LowerTriangularArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.ismatching","text":"ismatching(\n S::SpectralTransform,\n L::LowerTriangularArray\n) -> Any\n\n\nSpectral transform S and lower triangular matrix L match if the spectral dimensions (lmax, mmax) match and the number of vertical layers is equal or larger in the transform (constraints due to allocated scratch memory size).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.plan_FFTs!-Union{Tuple{N}, Tuple{NF}, Tuple{Vector{AbstractFFTs.Plan}, Vector{AbstractFFTs.Plan}, Vector{AbstractFFTs.Plan}, Vector{AbstractFFTs.Plan}, AbstractGridArray{NF, N, <:AbstractArray{NF}}, AbstractArray{Complex{NF}}, AbstractArray, Vector{<:Int64}}} where {NF<:AbstractFloat, N}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.plan_FFTs!","text":"plan_FFTs!(\n rfft_plans::Vector{AbstractFFTs.Plan},\n brfft_plans::Vector{AbstractFFTs.Plan},\n rfft_plans_1D::Vector{AbstractFFTs.Plan},\n brfft_plans_1D::Vector{AbstractFFTs.Plan},\n fake_grid_data::AbstractGridArray{NF<:AbstractFloat, N, <:AbstractArray{NF<:AbstractFloat}},\n scratch_memory_north::AbstractArray{Complex{NF<:AbstractFloat}},\n rings::AbstractArray,\n nlons::Vector{<:Int64}\n) -> NTuple{4, Vector{AbstractFFTs.Plan}}\n\n\nUtil function to generate FFT plans based on the array type of the fake Grid data provided. Uses views, which is less allocate-y than indexing but breaks when using CuArrays (see CUDA extension for alternative implementation for CuArrays).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.power_spectrum-Tuple{LowerTriangularArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.power_spectrum","text":"power_spectrum(spec::LowerTriangularArray; normalize) -> Any\n\n\nCompute the power spectrum of the spherical harmonic coefficients spec (lower triangular matrix/array) of type Complex{NF}. For any additional dimensions in spec, the power spectrum is computed along the first/spherical harmonic dimension.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.recursion_factor-Tuple{Int64, Int64}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.recursion_factor","text":"recursion_factor(l::Int64, m::Int64) -> Float64\n\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)).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.roundup_fft-Union{Tuple{Integer}, Tuple{T}} where T<:Integer","page":"Spectral transforms","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_interpolation-Union{Tuple{ArrayType}, Tuple{N}, Tuple{T}, Tuple{NF}, Tuple{Type{NF}, LowerTriangularArray{T, N, ArrayType}, Integer, Integer}} where {NF, T, N, ArrayType}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.spectral_interpolation","text":"spectral_interpolation(\n _::Type{NF},\n alms::LowerTriangularArray{T, N, ArrayType},\n ltrunc::Integer,\n mtrunc::Integer\n) -> Any\n\n\nReturns a LowerTriangularArray that is interpolated from alms to the size (ltrunc+1) x (mtrunc+1), both inputs are 0-based, by padding zeros for higher wavenumbers. If ltrunc or mtrunc are smaller than the corresponding size ofalms than spectral_truncation is automatically called instead, returning a smaller LowerTriangularArray.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_smoothing!-Tuple{LowerTriangularArray, Real}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.spectral_smoothing!","text":"spectral_smoothing!(\n L::LowerTriangularArray,\n c::Real;\n power,\n truncation\n)\n\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{LowerTriangularArray, Real}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.spectral_smoothing","text":"spectral_smoothing(\n A::LowerTriangularArray,\n c::Real;\n power\n) -> Any\n\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}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.spectral_truncation!","text":"spectral_truncation!(\n A::AbstractMatrix\n) -> LowerTriangularArray{T, 2, ArrayType} where {T, ArrayType<:AbstractMatrix{T}}\n\n\nSets the upper triangle of A to zero.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_truncation!-Tuple{LowerTriangularArray, Integer, Integer}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.spectral_truncation!","text":"spectral_truncation!(\n alms::LowerTriangularArray,\n ltrunc::Integer,\n mtrunc::Integer\n) -> LowerTriangularArray\n\n\nTriangular truncation to degree ltrunc and order mtrunc (both 0-based). 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.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_truncation!-Tuple{LowerTriangularArray, Integer}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.spectral_truncation!","text":"spectral_truncation!(\n alms::LowerTriangularArray,\n trunc::Integer\n) -> LowerTriangularArray\n\n\nTriangular truncation of alms to degree and order trunc in-place.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_truncation!-Tuple{LowerTriangularArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.spectral_truncation!","text":"spectral_truncation!(\n alms::LowerTriangularArray\n) -> LowerTriangularArray\n\n\nTriangular truncation of alms to the size of it, sets additional rows to zero.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_truncation-Union{Tuple{ArrayType}, Tuple{N}, Tuple{T}, Tuple{NF}, Tuple{Type{NF}, LowerTriangularArray{T, N, ArrayType}, Integer, Integer}} where {NF, T, N, ArrayType}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.spectral_truncation","text":"spectral_truncation(\n _::Type{NF},\n alms::LowerTriangularArray{T, N, ArrayType},\n ltrunc::Integer,\n mtrunc::Integer\n) -> Any\n\n\nReturns a LowerTriangularArray that is truncated from alms to the size (ltrunc+1) x (mtrunc+1), both inputs are 0-based. If ltrunc or mtrunc is larger than the corresponding size ofalms than spectral_interpolation is automatically called instead, returning a LowerTriangularArray padded zero coefficients for higher wavenumbers.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.transform!-Tuple{AbstractGridArray, LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.transform!","text":"transform!(\n grids::AbstractGridArray,\n specs::LowerTriangularArray,\n S::SpectralTransform;\n unscale_coslat\n) -> AbstractGridArray\n\n\nSpectral transform (spectral to grid space) from n-dimensional array specs of spherical harmonic coefficients to an n-dimensional array grids of ring grids. Uses FFT in the zonal direction, and a Legendre Transform in the meridional direction exploiting symmetries. The spectral transform is number format-flexible but grids and the spectral transform S have to have the same number format. Uses the precalculated arrays, FFT plans and other constants in the SpectralTransform struct S. The spectral transform is grid-flexible as long as the typeof(grids)<:AbstractGridArray and S.Grid matches.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.transform!-Tuple{LowerTriangularArray, AbstractGridArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.transform!","text":"transform!(\n specs::LowerTriangularArray,\n grids::AbstractGridArray,\n S::SpectralTransform\n) -> LowerTriangularArray\n\n\nSpectral transform (grid to spectral space) from n-dimensional array of grids to an n-dimensional array specs of spherical harmonic coefficients. Uses FFT in the zonal direction, and a Legendre Transform in the meridional direction exploiting symmetries. The spectral transform is number format-flexible but grids and the spectral transform S have to have the same number format. Uses the precalculated arrays, FFT plans and other constants in the SpectralTransform struct S. The spectral transform is grid-flexible as long as the typeof(grids)<:AbstractGridArray and S.Grid matches.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.transform-Tuple{AbstractGridArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.transform","text":"transform(grids::AbstractGridArray; kwargs...) -> Any\n\n\nSpectral transform (grid to spectral space) from grids to a newly allocated LowerTriangularArray. Based on the size of grids and the keyword dealiasing the spectral resolution trunc is retrieved. SpectralTransform struct S is allocated to execute transform(grids, S).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.transform-Tuple{LowerTriangularArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.transform","text":"transform(\n specs::LowerTriangularArray;\n unscale_coslat,\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 transform(alms, S).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.transform-Union{Tuple{NF}, Tuple{AbstractGridArray, SpectralTransform{NF}}} where NF","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.transform","text":"transform(\n grids::AbstractGridArray,\n S::SpectralTransform{NF}\n) -> Any\n\n\nSpherical harmonic transform from grids to a newly allocated specs::LowerTriangularArray using the precomputed spectral transform S.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.transform-Union{Tuple{NF}, Tuple{LowerTriangularArray, SpectralTransform{NF}}} where NF","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.transform","text":"transform(\n specs::LowerTriangularArray,\n S::SpectralTransform{NF};\n kwargs...\n) -> Any\n\n\nSpherical harmonic transform from specs to a newly allocated grids::AbstractGridArray using the precomputed spectral transform S.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.zero_imaginary_zonal_modes!-Tuple{LowerTriangularArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.zero_imaginary_zonal_modes!","text":"zero_imaginary_zonal_modes!(\n alms::LowerTriangularArray\n) -> LowerTriangularArray\n\n\nSet imaginary component of m=0 modes (the zonal modes in the first column) to 0.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇!-Tuple{LowerTriangularArray, LowerTriangularArray, LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.∇!","text":"∇!(\n dpdx::LowerTriangularArray,\n dpdy::LowerTriangularArray,\n p::LowerTriangularArray,\n S::SpectralTransform;\n radius\n) -> Tuple{LowerTriangularArray, LowerTriangularArray}\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 1/radius scaling unless radius keyword argument is provided.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇-Tuple{AbstractGridArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.∇","text":"∇(\n grid::AbstractGridArray,\n S::SpectralTransform;\n kwargs...\n) -> Tuple{Any, Any}\n\n\nThe zonal and meridional gradient of grid. Transform to spectral space, takes the gradient and unscales the 1/coslat scaling in the gradient. Acts on the unit-sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided. Makes use of an existing spectral transform S.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇-Tuple{AbstractGridArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.∇","text":"∇(grid::AbstractGridArray; kwargs...) -> Tuple{Any, Any}\n\n\nThe zonal and meridional gradient of grid. Transform to spectral space, takes the gradient and unscales the 1/coslat scaling in the gradient. Acts on the unit-sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇-Tuple{LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.∇","text":"∇(\n p::LowerTriangularArray,\n S::SpectralTransform;\n kwargs...\n) -> Tuple{Any, Any}\n\n\nThe zonal and meridional gradient of p using an existing SpectralTransform S. Acts on the unit sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇-Tuple{LowerTriangularArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.∇","text":"∇(p::LowerTriangularArray; kwargs...) -> Tuple{Any, Any}\n\n\nThe zonal and meridional gradient of p. Precomputes a SpectralTransform S. Acts on the unit-sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇²!-Tuple{LowerTriangularArray, LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.∇²!","text":"∇²!(\n ∇²alms::LowerTriangularArray,\n alms::LowerTriangularArray,\n S::SpectralTransform;\n add,\n flipsign,\n inverse,\n radius\n) -> LowerTriangularArray\n\n\nLaplace operator ∇² applied to the spectral coefficients alms in spherical coordinates. The eigenvalues which are precomputed in S. ∇²! is the in-place version which directly stores the output in the first argument ∇²alms. Acts on the unit sphere, i.e. it omits any radius scaling as all inplace gradient operators, unless the radius keyword argument is provided.\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{LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.∇²","text":"∇²(\n alms::LowerTriangularArray,\n S::SpectralTransform;\n kwargs...\n) -> Any\n\n\nLaplace operator ∇² applied to input alms, using precomputed eigenvalues from S. Acts on the unit sphere, i.e. it omits 1/radius^2 scaling unless radius keyword argument is provided.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇²-Tuple{LowerTriangularArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.∇²","text":"∇²(alms::LowerTriangularArray; kwargs...) -> Any\n\n\nReturns the Laplace operator ∇² applied to input alms. Acts on the unit sphere, i.e. it omits 1/radius^2 scaling unless radius keyword argument is provided.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇⁻²!-Tuple{LowerTriangularArray, LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.∇⁻²!","text":"∇⁻²!(\n ∇⁻²alms::LowerTriangularArray,\n alms::LowerTriangularArray,\n S::SpectralTransform;\n add,\n flipsign,\n kwargs...\n) -> LowerTriangularArray\n\n\nCalls ∇²!(∇⁻²alms, alms, S; add, flipsign, inverse=true).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇⁻²-Tuple{LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.∇⁻²","text":"∇⁻²(\n ∇²alms::LowerTriangularArray,\n S::SpectralTransform;\n kwargs...\n) -> Any\n\n\nInverseLaplace operator ∇⁻² applied to input alms, using precomputed eigenvalues from S. Acts on the unit sphere, i.e. it omits radius^2 scaling unless radius keyword argument is provided.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇⁻²-Tuple{LowerTriangularArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.∇⁻²","text":"∇⁻²(∇²alms::LowerTriangularArray; kwargs...) -> Any\n\n\nReturns the inverse Laplace operator ∇⁻² applied to input alms. Acts on the unit sphere, i.e. it omits radius^2 scaling unless radius keyword argument is provided.\n\n\n\n\n\n","category":"method"},{"location":"analysis/#Analysing-a-simulation","page":"Analysis","title":"Analysing a simulation","text":"","category":"section"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"While you can analyze a SpeedyWeather simulation through its NetCDF output (i.e., offline analysis), as most users will be used to with other models, you can also reuse a lot of functionality from SpeedyWeather interactively for analysis. This makes SpeedyWeather beyond being a model for the atmospheric general circulation also a library with many functions for the analysis of simulations. Often this also avoids the two-language problem that you will face if you run a simulation with a model in one language but then do the data analysis in another, treating the model as a blackbox although it likely has many of the functions you will need for analysis already defined. With SpeedyWeather we try to avoid this and are working towards a more unified approach in atmospheric modelling where simulation and analysis are done interactively with the same library: SpeedyWeather.jl.","category":"page"},{"location":"analysis/#Advantages-of-online-analysis","page":"Analysis","title":"Advantages of online analysis","text":"","category":"section"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Now you could run a SpeedyWeather simulation, and analyse the NetCDF output but that comes with several issues related to accuracy","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"If you use a reduced grid for the simulation, then the output will (by default) be interpolated on a full grid. This interpolation introduces an error.\nComputing integrals over gridded data on the sphere by weighting every grid point according to its area is not the most accurate numerical integration.\nComputing gradients over gridded data comes with similar issues. While our RingGrids are always equidistant in longitude, they are not necessarily in latitude.","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"The first point you can avoid by running a simulation on one of the full grids that are implemented, see SpectralGrid. But that also impacts the simulation and for various reasons we don't run on full grids by default.","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"The second point you can address by defining a more advanced numerical integration scheme, but that likely requires you to depend on external libraries and then, well, you could also just depend on SpeedyWeather.jl directly, because we have to do these computations internally anyway. Similar for the third point, many gradients have to be computed on every time step and we do that with spectral transforms to reduce the discretization error.","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"The following contains a (hopefully growing) list of examples of how a simulation can be analysed interactively. We call this online analysis because you are directly using the functionality from the model as if it was a library. For simplicity, we use the shallow water model to demonstrate this.","category":"page"},{"location":"analysis/#Mass-conservation","page":"Analysis","title":"Mass conservation","text":"","category":"section"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"In the absence of sources and sinks for the interface displacement eta in the shallow water equations, total mass (or equivalently volume as density is constant) is conserved. The total volume is defined as the integral of the dynamic layer thickness h = eta + H - H_b (H is the layer thickness at rest, H_b is orography) over the surface A of the sphere","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"M = iint h dA = iint left(eta + H - H_bright) dA","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"to check for conservation we want to assess that","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"fracpartial Mpartial t = 0","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"And because V = iint H dA - iint H_b dA, the total volume at rest, is a constant (H is a global constant, the orography H_b does not change with time) we can just check whether iint eta dA changes over time. Instead of computing this integral in grid-point space, we use the spectral eta whereby the coefficient of the first spherical harmonic (the l = m = 0 mode, or wavenumber 0, see Spherical Harmonic Transform) encodes the global average.","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=31, nlayers=1)\nmodel = ShallowWaterModel(spectral_grid)\nsimulation = initialize!(model)","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Now we check eta_00 the l = m = 0 coefficent of the inital conditions of that simulation with","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"simulation.prognostic_variables.pres[1][1]","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"[1][1] pulls the first Leapfrog time step and of that the first element of the underlying LowerTriangularMatrix which is the coefficient of the l = m = 0 harmonic. Its imaginary part is always zero (which is true for any zonal harmonic m=0 as its imaginary part would just unnecessarily rotate something zonally constant in zonal direction), so you can real it. Also for spherical harmonic transforms there is a norm of the sphere by which you have to divide to get your mean value in the original units","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"a = model.spectral_transform.norm_sphere # = 2√π = 3.5449078\nη_mean = real(simulation.prognostic_variables.pres[1][1]) / a","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"So the initial conditions in this simulation are such that the global mean interface displacement is that value in meters. You would need to multiply by the area of the sphere 4pi R^2 (radius R) to get the actual integral from above, but because that doesn't change with time either, we just want to check that η_mean doesn't change with time. Which is equivalent to partial_t iint eta dA = 0 and so volume conservation and because density is constant also mass conservation. Let's check what happens after the simulation ran for some days","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"run!(simulation, period=Day(10))\n\n# now we check η_mean again\nη_mean_later = real(simulation.prognostic_variables.pres[1][1]) / a","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"which is exactly the same. So mass is conserved, woohoo. ","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Insight from a numerical perspective: The tendency of eta is partial_t eta = -nabla cdot (mathbfu h) which is a divergence of a flux. Calculating the divergence in spherical harmonics always sets the l=m=0 mode to zero exactly (the gradient of a periodic variable has zero mean) so the time integration here is always with an exactly zero tendency.","category":"page"},{"location":"analysis/#Energy","page":"Analysis","title":"Energy","text":"","category":"section"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"The total energy in the shallow water equations is the sum of the kinetic energy density frac12(u^2 + v^2) and the potential energy density gz integrated over the total volume (times h=eta+H-H_b for the vertical then integrated over the sphere iint dA).","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"beginalign\nE = iint left int_H_b^etafrac12left(u^2 + v^2 + gzright)dz rightdA \n = iint frac12left(u^2 + v^2 + ghright)h dA\nendalign","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"In contrast to the Mass conservation which, with respect to the spectral transform, is a linear calculation, here we need to multiply variables, which has to be done in grid-point space. Then we can transform to spectral space for the global integral as before. Let us define a total_energy function as","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"using SpeedyWeather\nfunction total_energy(u, v, η, model)\n H = model.atmosphere.layer_thickness\n Hb = model.orography.orography\n g = model.planet.gravity\n \n h = @. η + H - Hb # layer thickness between the bottom and free surface\n E = @. h/2*(u^2 + v^2) + g*h^2 # vertically-integrated mechanical energy\n\n # transform to spectral, take l=m=0 mode at [1] and normalize for mean\n return E_mean = real(transform(E)[1]) / model.spectral_transform.norm_sphere\nend","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"So at the current state of our simulation we have a total energy (per square meter as we haven't multiplied by the surface area of the planet).","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"# flat copies for convenience\nu = simulation.diagnostic_variables.grid.u_grid[:, 1]\nv = simulation.diagnostic_variables.grid.v_grid[:, 1]\nη = simulation.diagnostic_variables.grid.pres_grid\n\nTE = total_energy(u, v, η, model)","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"with units of m^3 s^-2 (multiplying by surface area of the sphere and density of the fluid would turn it into joule = kg m^2 s^-2). To know in general where to find the respective variables u v eta inside our simulation object see Prognostic variables and Diagnostic variables. Now let us continue the simulation","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"run!(simulation, period=Day(10))\n\n# we don't need to reassign u, v, η as they were flat copies\n# pointing directly to the diagnostic variables inside the simulation\n# which got updated during run!\nTE_later = total_energy(u, v, η, model)","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"So the total energy has somewhat changed, it decreased to","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"TE_later/TE","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"of its previous value over 10 days.","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"note: Energy conservation\nWhile technically energy should be conserved in an unforced system, numerically this is rarely exactly the case. We need some Horizontal diffusion for numerical stability and also the time integration is dissipative due to temporal filtering, see Time integration. Note that the energy here is inversely cascading to larger scales, and this makes the dissipation of energy through Horizontal diffusion really inefficient, because in spectral space, standard Laplacian diffusion is proportional to k^2 where k is the wavenumber. By default, we use a 4th power Laplacian, so the diffusion here is proportional to k^8.","category":"page"},{"location":"analysis/#Potential-vorticity","page":"Analysis","title":"Potential vorticity","text":"","category":"section"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Potential vorticity in the shallow water equations is defined as","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"q = fracf + zetah","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"with f the Coriolis parameter, zeta the relative vorticity, and h the layer thickness as before. We can calculate this conveniently directly on the model grid (whichever you chose) as","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"# vorticity\nζ = simulation.diagnostic_variables.grid.vor_grid[:,1]\nf = coriolis(ζ) # create f on that grid\n\n# layer thickness\nη = simulation.diagnostic_variables.grid.pres_grid\nH = model.atmosphere.layer_thickness\nHb = model.orography.orography\nh = @. η + H - Hb\n\n# potential vorticity\nq = @. (f + ζ) / h\nnothing # hide","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"and we can compare the relative vorticity field to","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"using CairoMakie\nheatmap(ζ, title=\"Relative vorticity [1/s]\")\nsave(\"analysis_vor.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"(Image: Relative vorticity)","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"the potential vorticity","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"heatmap(q, title=\"Potential vorticity [1/m/s]\")\nsave(\"analysis_pv.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"(Image: Potential vorticity)","category":"page"},{"location":"analysis/#Absolute-angular-momentum","page":"Analysis","title":"Absolute angular momentum","text":"","category":"section"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Similar to the total mass, in the absence of sources and sinks for momentum, total absolute angular momentum (AAM) defined as","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Lambda = iint left(ur + Omega r^2right)h dA","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"should be conserved (partial_tLambda = 0). Here u is the zonal velocity, Omega the angular velocity of the Earth, r = R cosphi the momentum arm at latitude phi, and R the radius of Earth.","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Following previous examples, let us define a total_angular_momentum function as","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"using SpeedyWeather\n\nfunction total_angular_momentum(u, η, model)\n H = model.atmosphere.layer_thickness\n Hb = model.orography.orography\n R = model.spectral_grid.radius\n Ω = model.planet.rotation\n\n r = R * cos.(model.geometry.lats) # momentum arm for every grid point\n \n h = @. η + H - Hb # layer thickness between the bottom and free surface\n Λ = @. (u*r + Ω*r^2) * h # vertically-integrated AAM\n\n # transform to spectral, take l=m=0 mode at [1] and normalize for mean\n return Λ_mean = real(transform(Λ)[1]) / model.spectral_transform.norm_sphere\nend","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Anytime we stop the simulation, we can calculate Lambda using this function (ignoring the multiplication by 4pi R^2 to get total Lambda).","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"# use u, η from current state of simulation\nΛ_current = total_angular_momentum(u, η, model)","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"So after some days of integration, we would get another Lambda with","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"run!(simulation, period=Day(10))\n\n# u, η got updated during run!\nΛ_later = total_angular_momentum(u, η, model)\nΛ_later / Λ_current","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"and measure its relative change. Similar to energy, in the numerical integration, Λ is not exactly conserved due to Horizontal diffusion.","category":"page"},{"location":"analysis/#Circulation","page":"Analysis","title":"Circulation","text":"","category":"section"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Total circulation is defined as the area-integrated absolute vorticity:","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"C = iint left(zeta + fright) dA","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Following previous fashion, we define a function total_circulation for this","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"function total_circulation(ζ, model)\n f = coriolis(ζ) # create f on the grid of ζ\n C = ζ .+ f # absolute vorticity\n # transform to spectral, take l=m=0 mode at [1] and normalize for mean\n return C_mean = real(transform(C)[1]) / model.spectral_transform.norm_sphere\nend\n\ntotal_circulation(ζ, model)","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"note: Global-integrated circulation\nNote that the area integral of relative vorticity zeta and planetary vorticity f over the whole surface of a sphere are analytically exactly zero. Numerically, C_mean should be a small number but may not be exactly zero due to numerical precision and errors in the spectral transform.","category":"page"},{"location":"analysis/#Potential-enstrophy","page":"Analysis","title":"Potential enstrophy","text":"","category":"section"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"The total potential enstrophy is defined as the second-moment of potential vorticity q","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Q = iint frac12q^2 dA","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"In the absence of source and sink for potential vorticiy, this quantity should also conserve during the integration.","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"We define a function total_enstrophy for this","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"function total_enstrophy(ζ, η, model)\n # constants from model\n H = model.atmosphere.layer_thickness\n Hb = model.orography.orography\n f = coriolis(ζ) # create f on the grid\n \n h = @. η + H - Hb # thickness\n q = @. (ζ + f) / h # Potential vorticity\n Q = @. q^2 / 2 # Potential enstrophy\n\n # transform to spectral, take l=m=0 mode at [1] and normalize for mean\n return Q_mean = real(transform(Q)[1]) / model.spectral_transform.norm_sphere\nend","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Then by evaluating Q_mean at different time steps, one can similarly check how Q is changing over time.","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Q = total_enstrophy(ζ, η, model)\nrun!(simulation, period=Day(10))\nQ_later = total_enstrophy(ζ, η, model)\nQ_later/Q","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"note: Less conservative enstrophy\nNote that the turbulent nature of the shallow water model (or generally 2D turbulence) cascades enstrophy to smaller scales where it is removed by Horizontal diffusion for numerical stability. As a result, it is decreasing more quickly than energy.","category":"page"},{"location":"analysis/#Online-diagnostics","page":"Analysis","title":"Online diagnostics","text":"","category":"section"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Now we want to calculate all the above global diagnostics periodically during a simulation. For that we will use Callbacks, which let us inject code into a simulation that is executed after every time step (or at any other scheduled time, see Schedules).","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"So we define a function global_diagnostics to calculate the integrals together. We could reuse the functions like total_enstrophy from above but we also want to show how to global integral iint dV can be written more efficiently","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"# define a global integral, reusing a precomputed SpectralTransform S\n# times surface area of sphere omitted\nfunction ∬dA(v, h, S::SpectralTransform)\n return real(transform(v .* h, S)[1]) / S.norm_sphere\nend\n\n# use SpectralTransform from model\n∬dA(v, h, model::AbstractModel) = ∬dA(v, h, model.spectral_transform)","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"By reusing model.spectral_transform we do not have to re-precompute the spectral tranform on every call to transform. Providing the spectral transform from model as the 2nd argument simply reuses a previously precomputed spectral transform which is much faster and uses less memory.","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Now the global_diagnostics function is defined as","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"function global_diagnostics(u, v, ζ, η, model)\n \n # constants from model\n NF = model.spectral_grid.NF # number format used\n H = model.atmosphere.layer_thickness\n Hb = model.orography.orography\n R = model.spectral_grid.radius\n Ω = model.planet.rotation\n g = model.planet.gravity\n\n r = NF.(R * cos.(model.geometry.lats)) # create r on that grid\n f = coriolis(u) # create f on that grid\n \n h = @. η + H - Hb # thickness\n q = @. (ζ + f) / h # potential vorticity\n λ = @. u * r + Ω * r^2 # angular momentum (in the right number format NF)\n k = @. (u^2 + v^2) / 2 # kinetic energy\n p = @. g * h / 2 # potential energy\n z = @. q^2 / 2 # potential enstrophy\n\n M = ∬dA(1, h, model) # mean mass\n C = ∬dA(q, h, model) # mean circulation\n Λ = ∬dA(λ, h, model) # mean angular momentum\n K = ∬dA(k, h, model) # mean kinetic energy\n P = ∬dA(p, h, model) # mean potential energy\n Q = ∬dA(z, h, model) # mean potential enstrophy\n\n return M, C, Λ, K, P, Q\nend\n\n# unpack diagnostic variables and call global_diagnostics from above\nfunction global_diagnostics(diagn::DiagnosticVariables, model::AbstractModel)\n u = diagn.grid.u_grid[:, 1]\n v = diagn.grid.v_grid[:, 1]\n ζR = diagn.grid.vor_grid[:, 1]\n η = diagn.grid.pres_grid\n \n # vorticity during simulation is scaled by radius R, unscale here\n ζ = ζR ./ diagn.scale[]\n\n return global_diagnostics(u, v, ζ, η, model)\nend","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"note: Radius scaling of vorticity and divergence\nThe prognostic variables vorticity and divergence are scaled with the radius of the sphere during a simulation, see Radius scaling. This did not apply above because we only analyzed vorticity before or after the simulation, i.e. outside of the run!(simulation) call. The radius scaling is only applied just before the time integration and is undone directly after it. However, because now we are accessing the vorticity during the simulation we need to unscale the vorticity (and divergence) manually. General recommendation is to divide by diagn.scale[] (and not radius) as diagn.scale[] always reflects whether a vorticity and divergence are currently scaled (scale = radius) or not (scale = 1).","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Then we define a new callback GlobalDiagnostics subtype of SpeedyWeather's AbstractCallback and define new methods of initialize!, callback! and finalize! for it (see Callbacks for more details)","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"# define a GlobalDiagnostics callback and the fields it needs\nBase.@kwdef mutable struct GlobalDiagnostics <: SpeedyWeather.AbstractCallback\n timestep_counter::Int = 0\n \n time::Vector{DateTime} = []\n M::Vector{Float64} = [] # mean mass per time step\n C::Vector{Float64} = [] # mean circulation per time step\n Λ::Vector{Float64} = [] # mean angular momentum per time step\n K::Vector{Float64} = [] # mean kinetic energy per time step\n P::Vector{Float64} = [] # mean potential energy per time step\n Q::Vector{Float64} = [] # mean enstrophy per time step\nend\n\n# define how to initialize a GlobalDiagnostics callback\nfunction SpeedyWeather.initialize!(\n callback::GlobalDiagnostics,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel,\n)\n # replace with vector of correct length\n n = progn.clock.n_timesteps + 1 # +1 for initial conditions\n callback.time = zeros(DateTime, n)\n callback.M = zeros(n)\n callback.C = zeros(n)\n callback.Λ = zeros(n)\n callback.K = zeros(n)\n callback.P = zeros(n)\n callback.Q = zeros(n)\n \n M, C, Λ, K, P, Q = global_diagnostics(diagn, model)\n \n callback.time[1] = progn.clock.time\n callback.M[1] = M # set initial conditions\n callback.C[1] = C # set initial conditions\n callback.Λ[1] = Λ # set initial conditions\n callback.K[1] = K # set initial conditions\n callback.P[1] = P # set initial conditions\n callback.Q[1] = Q # set initial conditions\n \n callback.timestep_counter = 1 # (re)set counter to 1\n \n return nothing\nend\n\n# define what a GlobalDiagnostics callback does on every time step\nfunction SpeedyWeather.callback!(\n callback::GlobalDiagnostics,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel,\n)\n callback.timestep_counter += 1 \n i = callback.timestep_counter\n \n M, C, Λ, K, P, Q = global_diagnostics(diagn, model)\n \n # store current time and diagnostics for timestep i\n callback.time[i] = progn.clock.time\n callback.M[i] = M \n callback.C[i] = C \n callback.Λ[i] = Λ \n callback.K[i] = K \n callback.P[i] = P \n callback.Q[i] = Q \nend\n\nusing NCDatasets\n\n# define how to finalize a GlobalDiagnostics callback after simulation finished\nfunction SpeedyWeather.finalize!(\n callback::GlobalDiagnostics,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel,\n)\n n_timesteps = callback.timestep_counter\n\n # create a netCDF file in current path\n ds = NCDataset(joinpath(pwd(), \"global_diagnostics.nc\"), \"c\")\n \n # save diagnostics variables within\n defDim(ds, \"time\", n_timesteps)\n defVar(ds, \"time\", callback.time, (\"time\",))\n defVar(ds, \"mass\", callback.M, (\"time\",))\n defVar(ds, \"circulation\", callback.C, (\"time\",))\n defVar(ds, \"angular momentum\", callback.Λ, (\"time\",))\n defVar(ds, \"kinetic energy\", callback.K, (\"time\",))\n defVar(ds, \"potential energy\", callback.P, (\"time\",))\n defVar(ds, \"potential enstrophy\", callback.Q, (\"time\",))\n \n close(ds)\n\n return nothing\nend","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Note that callback! will execute every time step. If execution is only desired periodically, you can use Schedules. At finalize! we decide to write the timeseries of our global diagnostics as netCDF file via NCDatasets.jl to the current path pwd(). We need to add using NCDatasets here, as SpeedyWeather does not re-export the functionality therein.","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Now we create a GlobalDiagnostics callback, add it to the model with key :global_diagnostics (you get a random key if not provided) and reinitialize the simulation to start from the initial conditions.","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"# don't name it global_diagnostics because that's a function already!\ndiagnostics_recorder = GlobalDiagnostics()\nadd!(model.callbacks, :diagnostics_recorder => diagnostics_recorder)\nsimulation = initialize!(model)\n\nrun!(simulation, period=Day(20))\nnothing # hide","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Then one could check the output file global_diagnostics.nc, or directly use the callback through its key :diagnostics_recorder as we do here","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"using CairoMakie\n\n# unpack callback\n(; M, C, Λ, K, P, Q) = model.callbacks[:diagnostics_recorder]\nt = model.callbacks[:diagnostics_recorder].time\ndays = [Second(ti - t[1]).value/3600/24 for ti in t]\n\nfig = Figure();\naxs = [Axis(fig[row, col]) for row in 1:3, col in 1:2]\n\nlines!(axs[1,1], days, M)\naxs[1,1].title = \"Mass\"\nhidexdecorations!(axs[1,1])\n\nlines!(axs[2,1], days, Λ)\naxs[2,1].title = \"Angular momentum\"\nhidexdecorations!(axs[2,1])\n\nlines!(axs[3,1], days, P)\naxs[3,1].title = \"Potential energy\"\naxs[3,1].xlabel = \"time [day]\"\n\nlines!(axs[1,2], days, C)\naxs[1,2].title = \"Circulation\"\nhidexdecorations!(axs[1,2])\n\nlines!(axs[2,2], days, K)\naxs[2,2].title = \"Kinetic energy\"\nhidexdecorations!(axs[2,2])\n\nlines!(axs[3,2], days, Q)\naxs[3,2].title = \"Potential enstrophy\"\naxs[3,2].xlabel = \"time [day]\"\nfig\nsave(\"global_diagnostics.png\", fig) # hide\nnothing # hide","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"(Image: Global diagnostics)","category":"page"},{"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/#Interactively-exploring-the-grids","page":"Grids","title":"Interactively exploring the grids","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"Based on GeoMakie.jl SpeedyWeather.jl has an extension (meaning only loaded when also using GeoMakie) that defines the globe function which visualises the implemented grids at the desired resolution. With the CairoMakie backend these visualisations are static, but using GLMakie they are interactive. For example","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"using SpeedyWeather\nusing GLMakie, GeoMakie\n\n# grid type, resolution parameter nlat_half\nglobe(FullGaussianGrid, 24)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"This will open a window with interactive zoom and rotation, visualising a full Gaussian grid at a resolution of nlat_half = 24 (i.e. 96x48 grid points). You can visualise all grids at a wide range of resolutions for non-interactive plotting use interactive=false, which is also what one should do when using CairoMakie. Additional keyword arguments are coastlines, background among others, check ?globe. You also can visualise data on a grid directly this way which will draw polygons for the cell faces, e.g.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"grid = rand(FullGaussianGrid, 24)\nglobe(grid)","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":"using CairoMakie, GeoMakie # when using GLMakie, use interactive=true (default) for zoom and rotation\nglobe(FullGaussianGrid, 24, interactive=false)\nsave(\"full_gaussian_grid.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"(Image: FullGaussianGrid)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"The full Gaussian grid is a grid that uses regularly spaced longitudes with 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 = 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","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 0 1 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":"using CairoMakie, GeoMakie # when using GLMakie, use interactive=true (default) for zoom and rotation\nglobe(OctahedralGaussianGrid, 24, interactive=false)\nsave(\"octahedral_gaussian_grid.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"(Image: 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 = 1 16+4j on the j-th latitude ring (starting with 1 around the north pole), j=1 tfracN_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/#OctaminimalGaussianGrid","page":"Grids","title":"Octaminimal Gaussian grid","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"(called OctaminimalGaussianGrid)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"using CairoMakie, GeoMakie # when using GLMakie, use interactive=true (default) for zoom and rotation\nglobe(OctaminimalGaussianGrid, 24, interactive=false)\nsave(\"octaminimal_gaussian_grid.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"(Image: OctaminimalGaussianGrid)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"The OctaminimalGaussianGrid is similar to the OctahedralGaussianGrid but starts with 4 points around the poles. It therefore minimizes the number of grid points for the Gaussian grids at the cost of a (somewhat) inexact spectral transform. But for nlat_half = 24 (i.e. 48 latitude rings) this grid reduces the number of horizontal grid points from 3168 to 2400, i.e. -24% which speeds up the computation on the grid (dynamics and physics) as well as the spectral transform. The octaminimal Gaussian grid is intended to be used for low resolutions as for higher resolutions the relative increase in the number of grid points with the octahedral Gaussian grid becomes negligible. ","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"Furthermore the longitudes are rotated: Instead of no offset where the first point on every ring starts at 0˚E, an offset of 3602n degrees is chosen similar to how the longitudinal points in the HEALPix grids (HEALPixGrid and OctaHEALPixGrid) are chosen. This allows for a more even distribution of grid points near the poles.","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":"using CairoMakie, GeoMakie # when using GLMakie, use interactive=true (default) for zoom and rotation\nglobe(FullClenshawGrid, 24, interactive=false)\nsave(\"full_clenshaw_grid.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"(Image: 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 = 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.","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":"using CairoMakie, GeoMakie # when using GLMakie, use interactive=true (default) for zoom and rotation\nglobe(OctahedralClenshawGrid, 24, interactive=false)\nsave(\"octahedral_clenshaw_grid.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"(Image: 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":"using CairoMakie, GeoMakie # when using GLMakie, use interactive=true (default) for zoom and rotation\nglobe(HEALPixGrid, 24, interactive=false)\nsave(\"healpix_grid.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"(Image: 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=1 N_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=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.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"In the north equatorial belt j=N_side 2N_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 = 1 4N_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(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)","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":"using CairoMakie, GeoMakie # when using GLMakie, use interactive=true (default) for zoom and rotation\nglobe(OctaHEALPixGrid, 24, interactive=false)\nsave(\"octahealpix_grid.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"(Image: 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=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","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 = (u v), 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 u v T q 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 q T 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_0 0frac1sqrt4pi 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(x y z t) 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(x y eta t) = Psi(x y z(x y eta t) 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 x y t 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 = 0 1 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 u v 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 tfrac12 1 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_l m 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_l m, but the surface k=N only on the temperature mode at the surface. mathbfU takes the surface value of the l m mode of the logarithm of surface pressure (ln p_s)_l m 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 l m 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 mathbfR W L 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 mathbfR U L W 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 U V\nUnscale the cos(theta) factor to obtain u v","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 u v D vertically to obtain baru barv barD 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 u v T q and add it to the respective tendency.\nFor 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 mathcalD_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 zeta mathcalD T q\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 u v zeta mathcalD T q ln 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":"orography/#Orography","page":"Orography","title":"Orography","text":"","category":"section"},{"location":"orography/","page":"Orography","title":"Orography","text":"Orography (in height above the surface) forms the surface boundary of the lowermost layer in SpeedyWeather. ","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"In the shallow-water equations the orography H_b enters the equations when computing the layer thickness h = eta + H_0 - H_b for the volume fluxes mathbfuh in the continuity equation. Here, the orography is used in meters above the surface which shortens h over mountains. The orography here is needed in grid-point space.","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"In the primitive equations the orography enters the equations when computing the Geopotential. So actually required here is the surface geopotential Phi_s = gz_s where z_s is the orography height in meters as used in the shallow-water equations too z_s = H_b. However, the primitive equations require the orography in spectral space as the geopotential calculation is a linear operation in the horizontal and can therefore be applied in either grid-point or spectral space. The latter is more convenient as SpeedyWeather solves the equations to avoid additional transforms.","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"In the current formulation of the barotropic vorticity equations there is no orography. In fact, the field model.orography is not defined for model::BarotropicModel.","category":"page"},{"location":"orography/#Orographies-implemented","page":"Orography","title":"Orographies implemented","text":"","category":"section"},{"location":"orography/","page":"Orography","title":"Orography","text":"Currently implemented are","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"using InteractiveUtils # hide\nusing SpeedyWeather\nsubtypes(SpeedyWeather.AbstractOrography)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"which are ","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"Phi_s = z_s = H_b = 0 for NoOrography\nFor ZonalRidge the zonal ridge from the Jablonowski and Williamson initial conditions, see Jablonowski-Williamson baroclinic wave\nFor EarthOrography a high-resolution orography is loaded and interpolated to the resolution as defined by spectral_grid.","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"all orographies need to be created with spectral_grid::SpectralGrid as the first argument, so that the respective fields for geopot_surf, i.e. Phi_s and orography, i.e. H_b can be allocated in the right size and number format.","category":"page"},{"location":"orography/#Earth's-orography","page":"Orography","title":"Earth's orography","text":"","category":"section"},{"location":"orography/","page":"Orography","title":"Orography","text":"Earth's orography can be created with (here we use a resolution of T85, about 165km globally)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=85)\norography = EarthOrography(spectral_grid)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"but note that only allocates the orography, it does not actually load and interpolate the orography which happens at the initialize! step. Visualised with","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"model = PrimitiveDryModel(spectral_grid; orography)\ninitialize!(orography, model) # happens also in simulation = initialize!(model)\n\nusing CairoMakie\nheatmap(orography.orography, title=\"Earth's orography at T85 resolution, no smoothing\")\nsave(\"earth_orography.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"(Image: EarthOrography)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"typing ?EarthOrography shows the various options that are provided. An orogaphy at T85 resolution that is as smooth as it would be at T42 (controlled by the smoothing_fraction, the fraction of highest wavenumbers which are the top half here, about T43 to T85) for example can be created with","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"orography = EarthOrography(spectral_grid, smoothing=true, smoothing_fraction=0.5)\ninitialize!(orography, model)\n\nheatmap(orography.orography, title=\"Earth's orography at T85 resolution, smoothed to T42\")\nsave(\"earth_orography_smooth.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"(Image: EarthOrography_smooth)","category":"page"},{"location":"orography/#Load-orography-from-file","page":"Orography","title":"Load orography from file","text":"","category":"section"},{"location":"orography/","page":"Orography","title":"Orography","text":"The easiest to load another orography from a netCDF file is to reuse the EarthOrography, e.g.","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"mars_orography = EarthOrography(spectal_grid, \n path=\"path/to/my/orography\",\n file=\"mars_orography.nc\",\n file_Grid=FullClenshawGrid)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"the orography itself need to come on one of the full grids SpeedyWeather defines, i.e. FullGaussianGrid or FullClenshawGrid (a regular lat-lon grid, see FullClenshawGrid), which you can specify. Best to inspect the correct orientation with plot(mars_orography.orography) (or heatmap after using CairoMakie; the scope mars_orography. is whatever name you chose here). You can use smoothing as above.","category":"page"},{"location":"orography/#Changing-orography-manually","page":"Orography","title":"Changing orography manually","text":"","category":"section"},{"location":"orography/","page":"Orography","title":"Orography","text":"You can also change orography manually, that means by mutating the elements in either orography.orography (to set it for the shallow-water model) or orography.geopot_surf (for the primitive equations, but this is in spectral space, advanced!). This should be done after the orography has been initialised which will overwrite these arrays (again). You can just initialize orography with initialize!(orography, model) but that also automatically happens in simulation = initialize!(model). Orography is just stored as an array, so you can do things like sort!(orography.orography) (sorting all mountains towards the south pole). But for most practical purposes, the set! function is more convenient, for example you can do","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"set!(model, orography=(λ,φ) -> 2000*cosd(φ) + 300*sind(λ) + 100*randn())\n\nusing CairoMakie\nheatmap(model.orography.orography, title=\"Zonal 2000m ridge [m] with noise\")\nsave(\"orography_set.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"(Image: Orography with set!)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"passing on a function with arguments longitude (0 to 360˚E in that unit, so use cosd, sind etc.) and latitude (-90 to 90˚N). But note that while model.orography is still of type EarthOrography we have now muted the arrays within - so do not be confused that it is not the Earth's orography anymore.","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"The set! function automatically propagates the grid array in orography.orography to spectral space in orography.geopot_surf to synchronize those two arrays that are supposed to hold essentially the same information just one in grid the other in spectral space. set! also allows for the add keyword, making it possible to add (or remove) mountains, e.g. imagine Hawaii would suddenly increase massively in size, covering a 5˚x5˚ area with a 4000m \"peak\" (given that horizontal extent it is probably more a mountain range...)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"model.orography = EarthOrography(spectral_grid) # reset orography\ninitialize!(model.orography, model) # initially that reset orography\n\n# blow up Hawaii by adding a 4000m peak on a 10˚x10˚ large island\nH, λ₀, φ₀, σ = 4000, 200, 20, 5 # height, lon, lat position, and width\nset!(model, orography=(λ,φ) -> H*exp((-(λ-λ₀)^2 - (φ-φ₀)^2)/2σ^2), add=true)\nheatmap(model.orography.orography, title=\"Super Hawaii orography [m]\")\nsave(\"orography_hawaii.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"(Image: Orography with super Hawaii)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"If you don't use set!, you want to reflect any changes to orography.orography in the surface geopotential orography.geopot_surf (which is used in the primitive equations) manually by","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"transform!(orography.geopot_surf, orography.orography, model.spectral_transform)\norography.geopot_surf .*= model.planet.gravity\nspectral_truncation!(orography.geopot_surf)\nnothing # hide","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"In the first line, the surface geopotential is still missing the gravity, which is multiplied in the second line. The spectral_truncation! removes the l_max+1 degree of the spherical harmonics as illustrated in the spectral representation or the surface geopotential here. This is because scalar fields do not use that last degree, see One more degree for spectral fields.","category":"page"},{"location":"orography/#Spherical-distance","page":"Orography","title":"Spherical distance","text":"","category":"section"},{"location":"orography/","page":"Orography","title":"Orography","text":"In the example above we have defined the \"Super Hawaii orography\" as","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"orography=(λ,φ) -> H*exp((-(λ-λ₀)^2 - (φ-φ₀)^2)/2σ^2)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"note however, that this approximates the distance on the sphere with Cartesian coordinates which is here not too bad as we are not too far north (where longitudinal distances would become considerably shorter) and also as we are far away from the prime meridian. If lambda_0 = 0 in the example above then calculating lambda - lambda_0 for lambda = 359E yields a really far distance even though lambda is actually relatively close to the prime meridian. To avoid this problem, SpeedyWeather (actually RingGrids) defines a function called spherical_distance (inputs in degrees, output in meters) to actually calculate the great-circle distance or spherical distance. Compare","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"λ₀ = 0 # move \"Super Hawaii\" mountain onto the prime meridian\nset!(model, orography=(λ,φ) -> H*exp((-(λ-λ₀)^2 - (φ-φ₀)^2)/2σ^2))\nheatmap(model.orography.orography, title=\"Mountain [m] on prime meridian, cartesian coordinates\")\nsave(\"mountain_cartesian.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"(Image: Mountain in cartesian coordinates)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"which clearly shows that the mountain is only on the Eastern hemisphere – probably not what you wanted. Note also that because we did not provide add=true the orography we set through set! overwrites the previous orography (add=false is the default). Rewrite this as","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"λ₀ = 0 # move \"Super Hawaii\" mountain onto the prime meridian\nset!(model, orography=(λ,φ) -> H*exp(-spherical_distance((λ,φ), (λ₀,φ₀), radius=360/2π)^2/2σ^2))\nheatmap(model.orography.orography, title=\"Mountain [m] on prime meridian, spherical distance\")\nsave(\"mountain_spherical.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"(Image: Mountain in spherical coordinates)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"And the mountain also shows up on the western hemisphere! Note that we could have defined the width sigma of the mountain in meters, or we keep using degrees as before but then use radius = 360/2π to convert radians into degrees. If you set radius=1 then radians are returned and so we could have defined sigma in terms of radians too.","category":"page"},{"location":"orography/#Defining-a-new-orography-type","page":"Orography","title":"Defining a new orography type","text":"","category":"section"},{"location":"orography/","page":"Orography","title":"Orography","text":"You can also define a new orography like we defined ZonalRidge or EarthOrography. The following explains what's necessary for this. The new MyOrography has to be defined as (mutable or not, but always with @kwdef)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"@kwdef struct MyOrography{NF, Grid<:RingGrids.AbstractGrid{NF}} <: SpeedyWeather.AbstractOrography\n # optional, any parameters as fields here, e.g.\n constant_height::Float64 = 100\n # add some other parameters with default values\n\n # mandatory, every <:AbstractOrography needs those (same name, same type)\n orography::Grid # in grid-point space [m]\n geopot_surf::LowerTriangularMatrix{Complex{NF}} # in spectral space *gravity [m^2/s^2]\nend","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"for convenience with a generator function is automatically defined for all AbstractOrography","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"my_orography = MyOrography(spectral_grid, constant_height=200)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"Now we have to extend the initialize! function. The first argument has to be ::MyOrography i.e. the new type we just defined, the second argument has to be ::AbstractModel although you could constrain it to ::ShallowWater for example but then it cannot be used for primitive equations.","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"function SpeedyWeather.initialize!(\n orog::MyOrography, # first argument as to be ::MyOrography, i.e. your new type\n model::AbstractModel, # second argument, use anything from model read-only\n)\n (; orography, geopot_surf) = orog # unpack\n\n # maybe use lat, lon coordinates (in degree or radians)\n (; latds, londs, lats, lons) = model.geometry\n\n # change here the orography grid [m], e.g.\n orography .= orography.constant_height\n\n # then also calculate the surface geopotential for primitive equations\n # given orography we just set\n transform!(geopot_surf, orography, model.spectral_transform)\n geopot_surf .*= model.planet.gravity\n spectral_truncation!(geopot_surf)\n return nothing\nend","category":"page"},{"location":"lowertriangularmatrices/#lowertriangularmatrices","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"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":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"This module defines LowerTriangularArray, a lower triangular matrix format, which in contrast to LinearAlgebra.LowerTriangular does not store the entries above the diagonal. SpeedyWeather.jl uses LowerTriangularArray which is defined as a subtype of AbstractArray to store the spherical harmonic coefficients (see Spectral packing). For 2D LowerTriangularArray the alias LowerTriangularMatrix exists. Higher dimensional LowerTriangularArray are 'batches' of 2D LowerTriangularMatrix. So, for example a (10times 10times 10) LowerTriangularArray holds 10 LowerTriangularMatrix of size (10times 10) in one array. ","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"warn: LowerTriangularMatrix is actually a vector\nLowerTriangularMatrix and LowerTriangularArray can in many ways be used very much like a Matrix or Array, however, because they unravel the lower triangle into a vector their dimensionality is one less than their Array counterparts. A LowerTriangularMatrix should therefore be treated as a vector rather than a matrix with some (limited) added functionality to allow for matrix-indexing (vector or flat-indexing is the default though). More details below.","category":"page"},{"location":"lowertriangularmatrices/#Creation-of-LowerTriangularArray","page":"LowerTriangularMatrices","title":"Creation of LowerTriangularArray","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"A LowerTriangularMatrix and LowerTriangularArray can be created using zeros, ones, rand, or randn","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"using SpeedyWeather.LowerTriangularMatrices\n\nL = rand(LowerTriangularMatrix{Float32}, 5, 5)\nL2 = rand(LowerTriangularArray{Float32}, 5, 5, 5)","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"or the undef initializer LowerTriangularMatrix{Float32}(undef, 3, 3). The element type is arbitrary though, you can use any type T too.","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"Note how for a matrix both the upper triangle and the lower triangle are shown in the terminal. The zeros are evident. However, for higher dimensional LowerTriangularArray we fall back to show the unravelled first two dimensions. Hence, here, the first column is the first matrix with 15 elements forming a 5x5 matrix, but the zeros are not shown.","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"Alternatively, it can be created through conversion from Array, which drops the upper triangle entries and sets them to zero (which are not stored however)","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"M = rand(Float16, 3, 3)\nL = LowerTriangularMatrix(M)\n\nM2 = rand(Float16, 3, 3, 2)\nL2 = LowerTriangularArray(M2)","category":"page"},{"location":"lowertriangularmatrices/#Size-of-LowerTriangularArray","page":"LowerTriangularMatrices","title":"Size of LowerTriangularArray","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"There are three different ways to describe the size of a LowerTriangularArray. For example with L","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"L = rand(LowerTriangularMatrix, 5, 5)","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"we have (additional dimensions follow naturally thereafter)","category":"page"},{"location":"lowertriangularmatrices/#1-based-vector-indexing-(default)","page":"LowerTriangularMatrices","title":"1-based vector indexing (default)","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"size(L) # equivalently size(L, OneBased, as=Vector)","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"The lower triangle is unravelled hence the number of elements in the lower triangle is returned.","category":"page"},{"location":"lowertriangularmatrices/#1-based-matrix-indexing","page":"LowerTriangularMatrices","title":"1-based matrix indexing","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"size(L, as=Matrix) # equivalently size(L, OneBased, as=Matrix)","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"If you think of a LowerTriangularMatrix as a matrix this is the most intuitive size of L, which, however, does not agree with the size of the underlying data array (hence it is not the default).","category":"page"},{"location":"lowertriangularmatrices/#0-based-matrix-indexing","page":"LowerTriangularMatrices","title":"0-based matrix indexing","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"Because LowerTriangularArrays are used to represent the coefficients of spherical harmonics which are commonly indexed based on zero (i.e. starting with the zero mode representing the mean), we also add ZeroBased to get the corresponding size.","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"size(L, ZeroBased, as=Matrix)","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"which is convenient if you want to know the maximum degree and order of the spherical harmonics in L. 0-based vector indexing is not implemented.","category":"page"},{"location":"lowertriangularmatrices/#Indexing-LowerTriangularArray","page":"LowerTriangularMatrices","title":"Indexing LowerTriangularArray","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"We illustrate the two types of indexing LowerTriangularArray supports.","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"Matrix indexing, by denoting two indices, column and row [l, m, ..]\nVector/flat indexing, by denoting a single index [lm, ..].","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"The matrix index works as expected","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"L\n\nL[2, 2]","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"But the single index skips the zero entries in the upper triangle, i.e. a 2, 2 index points to the same element as the index 6","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"L[6]","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"which, important, is different from single indices of an AbstractMatrix","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"Matrix(L)[6]","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"which would point to the first element in the upper triangle (hence zero).","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"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 matrix index is somewhat slower as it first has to be converted to the corresponding single index. ","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"Consequently, many loops in SpeedyWeather.jl are build with the following structure","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"n, m = size(L, as=Matrix)\n\nij = 0\nfor j in 1:m, i in j:n\n ij += 1\n L[ij] = i+j\nend","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"which loops over all lower triangle entries of L::LowerTriangularArray 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).","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"warn: `end` doesn't work for matrix indexing\nIndexing LowerTriangularMatrix and LowerTriangularArray in matrix style ([i, j]) with end doesn't work. It either returns an error or wrong results as the end is lowered by Julia to the size of the underlying flat array dimension.","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"The setindex! functionality of matrixes will throw a BoundsError when trying to write into the upper triangle of a LowerTriangularArray, for example","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"L[2, 1] = 0 # valid index\n\nL[1, 2] = 0 # invalid index in the upper triangle","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"But reading from it will just return a zero","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"L[2, 3] # in the upper triangle","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"Higher dimensional LowerTriangularArray can be indexed with multidimensional array indices like most other arrays types. Both the vector index and the matrix index for the lower triangle work as shown here","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"L = rand(LowerTriangularArray{Float32}, 3, 3, 5)\n\nL[2, 1] # second lower triangle element of the first lower triangle matrix \n\nL[2, 1, 1] # (2,1) element of the first lower triangle matrix ","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"The setindex! functionality follows accordingly. ","category":"page"},{"location":"lowertriangularmatrices/#Iterators","page":"LowerTriangularMatrices","title":"Iterators","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"An iterator over all entries in the array can be created with eachindex","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"L = rand(LowerTriangularArray, 5, 5, 5)\nfor ij in eachindex(L)\n # do something\nend\n\neachindex(L)","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"In order to only loop over the harmonics (essentially the horizontal, ignoring other dimensions) use eachharmonic","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"eachharmonic(L)","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"If you only want to loop over the other dimensions use eachmatrix","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"eachmatrix(L)","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"together they can be used as","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"for k in eachmatrix(L)\n for lm in eachharmonic(L)\n L[lm, k]\n end\nend","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"Note that k is a CartesianIndex that will loop over all other dimensions, whether there's only 1 (representing a 3D variable) or 5 (representing a 6D variable with the first two dimensions being a lower triangular matrix).","category":"page"},{"location":"lowertriangularmatrices/#Linear-algebra-with-LowerTriangularArray","page":"LowerTriangularMatrices","title":"Linear algebra with LowerTriangularArray","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"The LowerTriangularMatrices module's main purpose is not linear algebra, and typical matrix operations will not work with LowerTriangularMatrix because it's treated as a vector not as a matrix, meaning that the following will not work as expected","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"L = rand(LowerTriangularMatrix{Float32}, 3, 3)\nL * L\ninv(L)","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"And many other operations that require L to be a AbstractMatrix which it isn't. In contrast, typical vector operations like a scalar product between two \"LowerTriangularMatrix\" vectors does work","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"L' * L","category":"page"},{"location":"lowertriangularmatrices/#Broadcasting-with-LowerTriangularArray","page":"LowerTriangularMatrices","title":"Broadcasting with LowerTriangularArray","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"In contrast to linear algebra, many element-wise operations work as expected thanks to broadcasting, so operations that can be written in . notation whether implicit +, 2*, ... or explicitly written .+, .^, ... or via the @. macro","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"L + L\n2L\n\n@. L + 2L - 1.1*L / L^2","category":"page"},{"location":"lowertriangularmatrices/#GPU","page":"LowerTriangularMatrices","title":"GPU","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"LowerTriangularArray{T, N, ArrayType} wraps around an array of type ArrayType. If this array is a GPU array (e.g. CuArray), all operations are performed on GPU as well (work in progress). The implementation was written so that scalar indexing is avoided in almost all cases, so that GPU operation should be performant. To use LowerTriangularArray on GPU you can e.g. just adapt an existing LowerTriangularArray.","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"using Adapt\nL = rand(LowerTriangularArray{Float32}, 5, 5, 5)\nL_gpu = adapt(CuArray, L)","category":"page"},{"location":"lowertriangularmatrices/#Function-and-type-index","page":"LowerTriangularMatrices","title":"Function and type index","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"Modules = [SpeedyWeather.LowerTriangularMatrices]","category":"page"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.LowerTriangularArray","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.LowerTriangularArray","text":"A lower triangular array implementation that only stores the non-zero entries explicitly. L<:AbstractArray{T,N-1} although we do allow both \"flat\" N-1-dimensional indexing and additional N-dimensional or \"matrix-style\" indexing.\n\nSupports n-dimensional lower triangular arrays, so that for all trailing dimensions L[:, :, ..] is a matrix in lower triangular form, e.g. a (5x5x3)-LowerTriangularArray would hold 3 lower triangular matrices.\n\n\n\n\n\n","category":"type"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.LowerTriangularArray-Union{Tuple{ArrayType}, Tuple{N}, Tuple{T}} where {T, N, ArrayType<:AbstractArray{T, N}}","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.LowerTriangularArray","text":"LowerTriangularArray(\n M::AbstractArray{T, N}\n) -> LowerTriangularArray\n\n\nCreate a LowerTriangularArray L from Array M by copying over the non-zero elements in M.\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.LowerTriangularMatrix","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.LowerTriangularMatrix","text":"2-dimensional LowerTriangularArray of type Twith its non-zero entries unravelled into aVector{T}`\n\n\n\n\n\n","category":"type"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.LowerTriangularMatrix-Union{Tuple{Matrix{T}}, Tuple{T}} where T","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.LowerTriangularMatrix","text":"LowerTriangularMatrix(M::Array{T, 2}) -> Any\n\n\nCreate a LowerTriangularArray L from Matrix M by copying over the non-zero elements in M.\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.OneBased","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.OneBased","text":"Abstract type to dispatch for 1-based indexing of the spherical harmonic degree l and order m, i.e. l=m=1 is the mean, the zonal modes are m=1 etc. This indexing matches Julia's 1-based indexing for arrays.\n\n\n\n\n\n","category":"type"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.ZeroBased","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.ZeroBased","text":"Abstract type to dispatch for 0-based indexing of the spherical harmonic degree l and order m, i.e. l=m=0 is the mean, the zonal modes are m=0 etc. This indexing is more common in mathematics.\n\n\n\n\n\n","category":"type"},{"location":"lowertriangularmatrices/#Base.fill!-Tuple{LowerTriangularArray, Any}","page":"LowerTriangularMatrices","title":"Base.fill!","text":"fill!(L::LowerTriangularArray, x) -> LowerTriangularArray\n\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/#Base.length-Tuple{LowerTriangularArray}","page":"LowerTriangularMatrices","title":"Base.length","text":"length(L::LowerTriangularArray) -> Any\n\n\nLength of a LowerTriangularArray defined as number of non-zero elements.\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#Base.size","page":"LowerTriangularMatrices","title":"Base.size","text":"size(L::LowerTriangularArray; ...) -> Any\nsize(\n L::LowerTriangularArray,\n base::Type{<:SpeedyWeather.LowerTriangularMatrices.IndexBasis};\n as\n) -> Any\n\n\nSize of a LowerTriangularArray defined as size of the flattened array if as <: AbstractVector and as if it were a full matrix when as <: AbstractMatrix` .\n\n\n\n\n\n","category":"function"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.eachharmonic-Tuple{LowerTriangularArray, Vararg{LowerTriangularArray}}","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.eachharmonic","text":"eachharmonic(\n L1::LowerTriangularArray,\n Ls::LowerTriangularArray...\n) -> Any\n\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.eachharmonic-Tuple{LowerTriangularArray}","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.eachharmonic","text":"eachharmonic(L::LowerTriangularArray) -> Any\n\n\ncreates unit_range::UnitRange to loop over all non-zeros/spherical harmonics numbers in a LowerTriangularArray L. Like eachindex but skips the upper triangle with zeros in L.\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.eachmatrix-Tuple{LowerTriangularArray, Vararg{LowerTriangularArray}}","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.eachmatrix","text":"eachmatrix(\n L1::LowerTriangularArray,\n Ls::LowerTriangularArray...\n) -> Any\n\n\nIterator for the non-horizontal dimensions in LowerTriangularArrays. Checks that the LowerTriangularArrays match according to lowertriangular_match.\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.eachmatrix-Tuple{LowerTriangularArray}","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.eachmatrix","text":"eachmatrix(L::LowerTriangularArray) -> Any\n\n\nIterator for the non-horizontal dimensions in LowerTriangularArrays. To be used like\n\nfor k in eachmatrix(L)\n L[1, k]\n\nto loop over every non-horizontal dimension of L.\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.find_L-Tuple{Base.Broadcast.Broadcasted}","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.find_L","text":"L = find_L(Ls) returns the first LowerTriangularArray among the arguments. Adapted from Julia documentation of Broadcast interface\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.ij2k-Tuple{Integer, Integer, Integer}","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.ij2k","text":"ij2k(i::Integer, j::Integer, m::Integer) -> Any\n\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":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.k2ij-Tuple{Integer, Integer}","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.k2ij","text":"k2ij(k::Integer, m::Integer) -> Tuple{Any, Any}\n\n\nConverts the linear index k in the lower triangle into a pair (i, j) of indices of the matrix in column-major form. (Formula taken from Angeletti et al, 2019, https://hal.science/hal-02047514/document)\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.lowertriangular_match-Tuple{LowerTriangularArray, LowerTriangularArray}","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.lowertriangular_match","text":"lowertriangular_match(\n L1::LowerTriangularArray,\n L2::LowerTriangularArray;\n horizontal_only\n) -> Any\n\n\nTrue if both L1 and L2 are of the same size (as matrix), but ignores singleton dimensions, e.g. 5x5 and 5x5x1 would match. With horizontal_only=true (default false) ignore the non-horizontal dimensions, e.g. 5x5, 5x5x1, 5x5x2 would all match.\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.lowertriangular_match-Tuple{LowerTriangularArray, Vararg{LowerTriangularArray}}","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.lowertriangular_match","text":"lowertriangular_match(\n L1::LowerTriangularArray,\n Ls::LowerTriangularArray...;\n kwargs...\n) -> Any\n\n\nTrue if all lower triangular matrices provided as arguments match according to lowertriangular_match wrt to L1 (and therefore all).\n\n\n\n\n\n","category":"method"},{"location":"radiation/#Radiation","page":"Radiation","title":"Radiation","text":"","category":"section"},{"location":"radiation/#Longwave-radiation-implementations","page":"Radiation","title":"Longwave radiation implementations","text":"","category":"section"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"Currently implemented is","category":"page"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"using InteractiveUtils # hide\nusing SpeedyWeather\nsubtypes(SpeedyWeather.AbstractLongwave)","category":"page"},{"location":"radiation/#Uniform-cooling","page":"Radiation","title":"Uniform cooling","text":"","category":"section"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"Following Paulius and Garner[PG06], the uniform cooling of the atmosphere is defined as ","category":"page"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"fracpartial Tpartial t = begincases - tau^-1quadtextforquad T T_min \n fracT_strat - Ttau_strat quad textelse endcases","category":"page"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"with tau = 16h resulting in a cooling of -1.5K/day for most of the atmosphere, except below temperatures of T_min = 2075K in the stratosphere where a relaxation towards T_strat = 200K with a time scale of tau_strat = 5days is present.","category":"page"},{"location":"radiation/#Jeevanjee-radiation","page":"Radiation","title":"Jeevanjee radiation","text":"","category":"section"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"Jeevanjee and Zhou [JZ22] (eq. 2) define a longwave radiative flux F for atmospheric cooling as (following Seeley and Wordsworth [SW23], eq. 1)","category":"page"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"fracdFdT = α*(T_t - T)","category":"page"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"The flux F (in Wm^2K) is a vertical upward flux between two layers (vertically adjacent) of temperature difference dT. The change of this flux across layers depends on the temperature T and is a relaxation term towards a prescribed stratospheric temperature T_t = 200K with a radiative forcing constant alpha = 0025 Wm^2K^2. Two layers of identical temperatures T_1 = T_2 would have no net flux between them, but a layer below at higher temperature would flux into colder layers above as long as its temperature T T_t. This flux is applied above the lowermost layer and above, leaving the surface fluxes unchanged. The uppermost layer is tied to T_t through a relaxation at time scale tau = 6h","category":"page"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"fracpartial Tpartial t = fracT_t - Ttau","category":"page"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"The flux F is converted to temperature tendencies at layer k via","category":"page"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"fracpartial T_kpartial t = (F_k+12 - F_k-12)fracgDelta p c_p","category":"page"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"The term in parentheses is the absorbed flux in layer k of the upward flux from below at interface k+12 (k increases downwards, see Vertical coordinates and resolution and Sigma coordinates). Delta p = p_k+12 - p_k-12 is the pressure thickness of layer k, gravity g and heat capacity c_p.","category":"page"},{"location":"radiation/#Shortwave-radiation","page":"Radiation","title":"Shortwave radiation","text":"","category":"section"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"Currently implemented is","category":"page"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"subtypes(SpeedyWeather.AbstractShortwave)","category":"page"},{"location":"radiation/#References","page":"Radiation","title":"References","text":"","category":"section"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"[PG06]: Paulius and Garner, 2006. JAS. DOI:10.1175/JAS3705.1","category":"page"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"[SW23]: Seeley, J. T. & Wordsworth, R. D. Moist Convection Is Most Vigorous at Intermediate Atmospheric Humidity. Planet. Sci. J. 4, 34 (2023). DOI:10.3847/PSJ/acb0cb","category":"page"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"[JZ22]: Jeevanjee, N. & Zhou, L. On the Resolution‐Dependence of Anvil Cloud Fraction and Precipitation Efficiency in Radiative‐Convective Equilibrium. J Adv Model Earth Syst 14, e2021MS002759 (2022). DOI:10.1029/2021MS002759","category":"page"},{"location":"convection/#Convection","page":"Convection","title":"Convection","text":"","category":"section"},{"location":"convection/","page":"Convection","title":"Convection","text":"Convection is the atmospheric process of rising motion because of positively buoyant air parcels compared to its surroundings. In hydrostatic models like the primitive equation model in SpeedyWeather.jl convection has to be parameterized as the vertical velocity is not a prognostic variable that depends on vertical stability but rather diagnosed to satisfy horizontal divergence. Convection can be shallow and non-precipitating denoting that buoyant air masses can rise but do not reach saturation until they reach a level of zero buoyancy. But convection can also be deep denoting that saturation has been reached during ascent whereby the latent heat release from condensation provides additional energy for further ascent. Deep convection is therefore usually also precipitating as the condensed humidity forms cloud droplets that eventually fall down as convective precipitation. See also Large-scale condensation in comparison.","category":"page"},{"location":"convection/#Convection-implementations","page":"Convection","title":"Convection implementations","text":"","category":"section"},{"location":"convection/","page":"Convection","title":"Convection","text":"Currently implemented are","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"using InteractiveUtils # hide\nusing SpeedyWeather\nsubtypes(SpeedyWeather.AbstractConvection)","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"which are described in the following.","category":"page"},{"location":"convection/#BettsMiller","page":"Convection","title":"Simplified Betts-Miller convection","text":"","category":"section"},{"location":"convection/","page":"Convection","title":"Convection","text":"We follow the simplification of the Betts-Miller convection scheme [Betts1986][BettsMiller1986] as studied by Frierson, 2007 [Frierson2007]. The central idea of this scheme is to represent the effect of convection as an adjustment towards a (pseudo-) moist adiabat reference profile and its associated humidity profile. Meaning that conceptually for every vertical column in the atmosphere we","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"Diagnose the vertical temperature and humidity profile of the environment relative to the adiabat up to the level of zero buoyancy.\nDecide whether convection should take place and whether it is deep (precipitating) or shallow (non-precipitating).\nRelax temperature and humidity towards (corrected) profiles from 1.","category":"page"},{"location":"convection/#Reference-profiles","page":"Convection","title":"Reference profiles","text":"","category":"section"},{"location":"convection/","page":"Convection","title":"Convection","text":"The dry adiabat is","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"T = T_0 (fracpp_0)^fracRc_p","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"The temperature T of an air parcel at pressure p is determined by the temperature T_0 it had at pressure p_0 (this can be surface but it does not have to be) and the gas constant for dry air R = 28704 JKkg and the heat capacity c_p = 100464 JKkg. The pseudo adiabat follows the dry adiabat until saturation is reached (the lifting condensation level, often abbreviated to LCL), q q^star. Then it follows the pseudoadiabatic lapse rate","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"Gamma = -fracdTdz = fracgc_pleft(\n frac 1 + fracq^star L_v (1-q^star)^2 R_d T_v\n 1 + fracq^star L_v^2(1-q^star)^2 c_p R_v T^2right)","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"with gravity g, heat capacity c_p, the saturation specific humidity of the parcel q^star (which is its specific humidity given that it has already reached saturation), latent heat of vaporization L_v, dry gas constant R_d, water vapour gas constant R_v, and Virtual temperature T_v. Starting with a temperature T and humidity q = q^star at the lifting condensation level temperature aloft changes with dT = -fracdPhic_p() between two layers separated dPhi in geopotential Phi apart. On that new layer, q^star is recalculated as well as the virtual temperature T_v = T(1 + mu q^star). mu is derived from the ratio of dry to vapour gas constants see Virtual temperature. Note that the pseudoadiabatic ascent is independent of the environmental temperature and humidity and function of temperature and humidity of the parcel only (although that one starts with surface temperature and humidity from the environment). Solely the level of zero buoyancy is determined by comparing the parcel's virtual temperature T_v to the virtual temperature of the environment T_ve at that level. Level of zero buoyancy is reached when T_v = T_ve but continues for T_v T_ve which means that the parcel is still buoyant. Note that the virtual temperature includes the effect that humidity has on its density.","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"The (absolute) temperature a lifted parcel has during ascent (following its pseudoadiabat, dry and/ or moist, until reaching the level of zero buoyancy) is then taken as the reference temperature profile T_ref that the Betts-Miller convective parameterization relaxes towards as a first guess (with a following adjustment as discussed below). The humidity profile is taken as q_ref = RH_SBMT_ref with a parameter RH_SBM (default RH_SBM = 07) of the scheme (Simplified Betts-Miller, SBM) that determines a constant relative humidity of the reference profile.","category":"page"},{"location":"convection/#First-guess-relaxation","page":"Convection","title":"First-guess relaxation","text":"","category":"section"},{"location":"convection/","page":"Convection","title":"Convection","text":"With the Reference profiles T_ref q_ref obtained, we relax the actual environmental temperature T and specific humidity q in the column","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"beginaligned\ndelta q = - fracq - q_reftau_SBM \ndelta T = - fracT - T_reftau_SBM\nendaligned","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"with the second parameter of the parameterization, the time scale tau_SBM. Note that because this is a first-guess relaxation, these tendencies are not actually the resulting tendencies from this scheme. Those will be calculated in Corrected relaxation.","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"Note that above the level of zero buoyancy no relaxation takes place delta T = delta q = 0, or, equivalently T = T_ref, q = q_ref there. Vertically integration from surface p_0 to level of zero buoyancy in pressure coordinates p_LZB yields","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"beginaligned\nP_q = - int_p_0^p_LZB delta q fracdpg \nP_T = int_p_0^p_LZB fracc_pL_v delta T fracdpg\nendaligned","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"P_q is the precipitation in units of kg m^2 s due to drying (as a consequence of the humidity tendency) and P_T is the precipitation in the same units due to warming (as resulting from temperature tendencies). Note that they are the vertically difference between current profiles and the references profiles, so if P_q 0 this would mean that a convective adjustment to q_ref would release humidity from the column through condensation, but P_q can also be negative. Consequently similar for P_T.","category":"page"},{"location":"convection/#Convective-criteria","page":"Convection","title":"Convective criteria","text":"","category":"section"},{"location":"convection/","page":"Convection","title":"Convection","text":"We now distinguish three cases","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"Deep convection when P_T 0 and P_q 0\nShallow convection when P_T 0 and P_q = 0\nNo convection for P_T = 0.","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"Note that to evaluate these cases it is not necessary to divide by tau_SBM in the first-guess relaxation, neither are the 1g and tfracc_pg L_v necessary to multiply during the vertical integration as all are positive constants. While this changes the units of P_T P_q they can be reused in the following.","category":"page"},{"location":"convection/#Deep-convection","page":"Convection","title":"Deep convection","text":"","category":"section"},{"location":"convection/","page":"Convection","title":"Convection","text":"Following Frierson, 2007 [Frierson2007] in order to conserve enthalpy we correct the reference profile for temperature T_ref to T_ref 2 so that P_T = P_q.","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"T_ref 2 = T_ref + frac1Delta p c_p int_p_0^p_LZB c_p (T - T_ref) + L_v (q - q_ref) dp","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"Delta p is the pressure difference p_LZB - p_0. The terms inside the integral are rearranged compared to Frierson, 2007 to show that the vertical integral in First-guess relaxation really only has to be computed once.","category":"page"},{"location":"convection/#Shallow-convection","page":"Convection","title":"Shallow convection","text":"","category":"section"},{"location":"convection/","page":"Convection","title":"Convection","text":"In the following we describe the \"qref\" scheme from Frierson, 2007 which corrects reference profiles for both temperature and humidity to guarantee that P_q = 0, i.e. no precipitation during convection. In that sense, shallow convection is non-precipitating. Although shallow convection is supposed to be shallow we do not change the height of the convection and keep using the p_LZB determined during the calculation of the Reference profiles.","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"beginaligned\nDelta q = int_p_0^p_LZB q - q_ref dp \nQ_ref = int_p_0^p_LZB -q_ref dp \nf_q = 1 - fracDelta qQ_ref \nq_ref 2 = f_q q_ref \nDelta T = frac1Delta p int_p_0^p_LZB -(T - T_ref) dp \nT_ref2 = T_ref - Delta T\nendaligned","category":"page"},{"location":"convection/#Corrected-relaxation","page":"Convection","title":"Corrected relaxation","text":"","category":"section"},{"location":"convection/","page":"Convection","title":"Convection","text":"After the reference profiles have been corrected in Deep convection and Shallow convection we actually calculate tendencies from","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"beginaligned\ndelta q = - fracq - q_ref 2tau_SBM \ndelta T = - fracT - T_ref 2tau_SBM\nendaligned","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"with tau_SBM = 2h as default.","category":"page"},{"location":"convection/#Convective-precipitation","page":"Convection","title":"Convective precipitation","text":"","category":"section"},{"location":"convection/","page":"Convection","title":"Convection","text":"The convective precipitation P results then from the vertical integration of the delta q tendencies, similar to Large-scale precipitation.","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"P = -int fracDelta tg rho delta q dp","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"In the shallow convection case P=0 due to the correction even though in the first guess relaxation P0 was possible, but for deep convection P0 by definition.","category":"page"},{"location":"convection/#Dry-convection","page":"Convection","title":"Dry convection","text":"","category":"section"},{"location":"convection/","page":"Convection","title":"Convection","text":"In the primitive equation model with humidity the Betts-Miller convection scheme as described above is defined. Without humidity, a dry version reduces to the Shallow convection case. The two different shallow convection schemes in Frierson 2007[Frierson2007], the \"shallower\" shallow convection scheme and the \"qref\" (as implemented here in Shallow convection) in that case also reduce to the same formulation. The dry Betts-Miller convection scheme is the default in the primitive equation model without humidity.","category":"page"},{"location":"convection/#References","page":"Convection","title":"References","text":"","category":"section"},{"location":"convection/","page":"Convection","title":"Convection","text":"[Betts1986]: Betts, A. K., 1986: A new convective adjustment scheme. Part I: Observational and theoretical basis. Quart. J. Roy. Meteor. Soc.,112, 677-691. DOI: 10.1002/qj.49711247307","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"[BettsMiller1986]: Betts, A. K. and M. J. Miller, 1986: A new convective adjustment scheme. Part II: Single column tests using GATE wave, BOMEX, ATEX and Arctic air-mass data sets. Quart. J. Roy. Meteor. Soc.,112, 693-709. DOI: 10.1002/qj.49711247308","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"[Frierson2007]: Frierson, D. M. W., 2007: The Dynamics of Idealized Convection Schemes and Their Effect on the Zonally Averaged Tropical Circulation. J. Atmos. Sci., 64, 1959-1976. DOI:10.1175/JAS3935.1","category":"page"},{"location":"custom_netcdf_output/#Customizing-netCDF-output","page":"NetCDF output variables","title":"Customizing netCDF output","text":"","category":"section"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"SpeedyWeather's NetCDF output is modularised for the output variables, meaning you can add relatively easy new variables to be outputted alongside the default variables in the netCDF file. We explain here how to define a new output variable largely following the logic of Extending SpeedyWeather.","category":"page"},{"location":"custom_netcdf_output/#New-output-variable","page":"NetCDF output variables","title":"New output variable","text":"","category":"section"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"Say we want to output the Vertical velocity. In Sigma coordinates on every time step, one has to integrate the divergence vertically to know where the flow is not divergence-free, meaning that the horizontally converging or diverging motion is balanced by a vertical velocity. This leads to the variable partial sigma partial t, which is the equivalent of Vertical velocity in the Sigma coordinates. This variable is calculated and stored at every time step in ","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"simulation.diagnostic_variables.dynamics.σ_tend","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"So how do we access it and add it the netCDF output?","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"First we define VerticalVelocityOutput as a new struct subtype of SpeedyWeather.AbstractOutputVariable we add the required fields name::String, unit::String, long_name::String and dims_xyzt::NTuple{4, Bool} (we skip the optional fields for missing_value or compression).","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"using SpeedyWeather\n\n@kwdef struct VerticalVelocityOutput <: SpeedyWeather.AbstractOutputVariable\n name::String = \"w\"\n unit::String = \"s^-1\"\n long_name::String = \"vertical velocity dσ/dt\"\n dims_xyzt::NTuple{4, Bool} = (true, true, true, true)\nend","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"By default (using the @kwdef macro) we set the dimensions in dims_xyzt to 4D because the vertical velocity is a 3D variable that we want to output on every time step. So while dims_xyzt is a required field for every output variable you should not actually change it as it is an inherent property of the output variable.","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"You can now add this variable to the NetCDFOutput as already described in Output variables","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"spectral_grid = SpectralGrid()\noutput = NetCDFOutput(spectral_grid)\nadd!(output, VerticalVelocityOutput())","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"Note that here we skip the SpeedyWeather. prefix which would point to the SpeedyWeather scope but we have defined VerticalVelocityOutput in the global scope.","category":"page"},{"location":"custom_netcdf_output/#Extend-the-output!-function","page":"NetCDF output variables","title":"Extend the output! function","text":"","category":"section"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"While we have defined a new output variable we have not actually defined how to output it. Because in the end we will need to write that variable into the netcdf file in NetCDFOutput, which we describe now. We have to extend extend SpeedyWeather's output! function with the following function signature","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"function SpeedyWeather.output!(\n output::NetCDFOutput,\n variable::VerticalVelocityOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel,\n)\n # INTERPOLATION\n w = output.grid3D # scratch grid to interpolate into\n (; σ_tend) = diagn.dynamics # point to data in diagnostic variables\n RingGrids.interpolate!(w, σ_tend , output.interpolator)\n\n # WRITE TO NETCDF\n i = output.output_counter # output time step to write\n output.netcdf_file[variable.name][:, :, :, i] = w\n return nothing\nend","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"The first argument has to be ::NetCDFOutput as this is the argument we write into (i.e. mutate). The second argument has to be ::VerticalVelocityOutput so that Julia's multiple dispatch calls this output! method for our new variable. Then the prognostic, diagnostic variables and the model follows which allows us generally to read any data and use it to write into the netCDF file.","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"In most cases you will need to interpolate any gridded variables inside the model (which can be on a reduced grd) onto the output grid (which has to be a full grid, see Output grid). For that the NetCDFOutput has two scratch arrays grid3D and grid2D which are of type and size as defined by the output_Grid and nlat_half arguments when creating the NetCDFOutput. So the three lines for interpolation are essentially those in which your definition of a new output variable is linked with where to find that variable in diagnostic_variables. You can, in principle, also do any kind of computation here, for example adding two variables, normalising data and so on. In the end it has to be on the output_Grid hence you probably do not want to skip the interpolation step but you are generally allowed to do much more here before or after the interpolation.","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"The last two lines are then just about actually writing to netcdf. For any variable that is written on every output time step you can use the output counter i to point to the correct index i in the netcdf file as shown here. For 2D variables (horizontal+time) the indexing would be [:, :, i]. 2D variables without time you only want to write once (because they do not change) the indexing would change to [:, :] and you then probably want to add a line at the top like output.output_counter > 1 || return nothing to escape immediately after the first output time step. But you could also check for a specific condition (e.g. a new temperature record in a given location) and only then write to netcdf. Just some ideas how to customize this even further.","category":"page"},{"location":"custom_netcdf_output/#Reading-the-new-variable","page":"NetCDF output variables","title":"Reading the new variable","text":"","category":"section"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"Now let's try this in a primitive dry model","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"model = PrimitiveDryModel(spectral_grid; output)\nmodel.output.variables[:w]","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"By passing on output to the model constructor the output variables now contain w and we see it here as we have defined it earlier.","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"simulation = initialize!(model)\nrun!(simulation, period=Day(5), output=true)\n\n# read netcdf data\nusing NCDatasets\npath = joinpath(model.output.run_path, model.output.filename)\nds = NCDataset(path)\nds[\"w\"]","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"Fantastic, it's all there. We wrap this back into a FullGaussianGrid but ignore the mask (there are no masked values) in the netCDF file which causes a Union{Missing, Float32} element type by reading out the raw data with .var. And visualise the vertical velocity in sigma coordinates (remember this is actually partial sigma partial t) of the last time step (index end) stored on layer k=4 (counted from the top)","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"w = FullGaussianGrid(ds[\"w\"].var[:, :, :, :], input_as=Matrix)\n\nusing CairoMakie\nheatmap(w[:, 4, end], title=\"vertical velocity dσ/dt at k=4\")\nsave(\"sigma_tend.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"(Image: Sigma tendency)","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"This is now the vertical velocity between layer k=4 and k=5. You can check that the vertical velocity on layer k=8 is actually zero (because that is the boundary condition at the surface) and so would be the velocity between k=0 and k=1 at the top of the atmosphere, which however is not explicitly stored. The vertical velocity is strongest on the wind and leeward side of mountains which is reassuring and all the analysis we want to do here for now.","category":"page"},{"location":"gradients/#Gradient-operators","page":"Gradient operators","title":"Gradient operators","text":"","category":"section"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","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. We call them divergence, curl, ∇, ∇², ∇⁻² (as well as their in-place versions with !) within the limits of unicode characters and Julia syntax. These functions are defined for inputs being spectral coefficients (i.e. LowerTriangularMatrix) or gridded fields (i.e. <:AbstractGrid) and also allow as an additional argument a spectral transform object (see SpectralTransform) which avoids recalculating it under the hood.","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"info: SpeedyTransforms assumes a unit sphere\nThe gradient 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 divergence, curl, ∇, ∇², ∇⁻² omit the radius scaling unless you provide the optional keyword radius (or you can do ./= radius manually). Also note that meridional derivates in spectral space expect a cos^-1(theta) scaling. Details are always outlined in the respective docstrings, ?∇ for example.","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"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":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"hatnabla A = left(fracpartial Apartial lambda cos(theta)fracpartial Apartial theta right) =\nRcos(theta)nabla A","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","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":"gradients/","page":"Gradient operators","title":"Gradient operators","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":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"And the Laplace operators omit a R^2 (radius R) scaling, i.e.","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"hatnabla^-2A = frac1R^2nabla^-2A quad hatnabla^2A = R^2nabla^2A","category":"page"},{"location":"gradients/#Gradient","page":"Gradient operators","title":"Gradient ∇","text":"","category":"section"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"We illustrate the usage of the gradient function ∇. Let us create some fake data G on the grid first ","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"using SpeedyWeather, CairoMakie\n\n# create some data with wave numbers 0,1,2,3,4\ntrunc = 64 # 1-based maximum degree of spherical harmonics\nL = randn(LowerTriangularMatrix{ComplexF32}, trunc, trunc)\nspectral_truncation!(L, 5) # remove higher wave numbers\nG = transform(L)\nheatmap(G, title=\"Some fake data G\") # requires `using CairoMakie`\nsave(\"gradient_data.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"(Image: Gradient data)","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"Now we can take the gradient as follows","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"dGdx, dGdy = ∇(G)\nnothing # hide","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"this transforms internally back to spectral space takes the gradients in zonal and meridional direction, transforms to grid-point space again und unscales the coslat-scaling on the fly but assumes a radius of 1 as the keyword argument radius was not provided. Use ∇(G, radius=6.371e6) for a gradient on Earth in units of \"data unit\" divided by meters.","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"heatmap(dGdx, title=\"dG/dx on the unit sphere\")\nsave(\"dGdx.png\", ans) # hide\nheatmap(dGdy, title=\"dG/dy on the unit sphere\")\nsave(\"dGdy.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"(Image: dGdx) (Image: dGdy)","category":"page"},{"location":"gradients/#Geostrophy","page":"Gradient operators","title":"Geostrophy","text":"","category":"section"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"Now, we want to use the following example to illustrate a more complex use of the gradient operators: We have u v and want to calculate eta in the shallow water system from it following geostrophy. Analytically we have","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"-fv = -gpartial_lambda eta quad fu = -gpartial_theta eta","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"which becomes, if you take the divergence of these two equations","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"zeta = fracgfnabla^2 eta","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"Meaning that if we start with u v 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":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"Let us start by generating some data","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"spectral_grid = SpectralGrid(trunc=31, nlayers=1)\nforcing = SpeedyWeather.JetStreamForcing(spectral_grid)\ndrag = QuadraticDrag(spectral_grid)\nmodel = ShallowWaterModel(spectral_grid; forcing, drag)\nsimulation = initialize!(model);\nrun!(simulation, period=Day(30))\nnothing # hide","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","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":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"u = simulation.diagnostic_variables.grid.u_grid[:, 1] # [:, 1] for 1st layer\nv = simulation.diagnostic_variables.grid.v_grid[:, 1]\nvor = curl(u, v, radius = spectral_grid.radius)\nnothing # hide","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","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":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"RingGrids.scale_coslat⁻¹!(u)\nRingGrids.scale_coslat⁻¹!(v)\n\nS = SpectralTransform(u, one_more_degree=true)\nus = transform(u, S)\nvs = transform(v, S)\n\nvor = curl(us, vs, radius = spectral_grid.radius)","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"(Copies of) the velocity fields are unscaled by the cosine of latitude (see above), then transformed into spectral space, and the curl has the keyword argument radius to divide internally by the radius (if not provided it assumes a unit sphere). We always unscale vector fields by the cosine of latitude if they are provided to curl or divergence in spectral as you can only do this scaling effectively in grid-point space. The methods accepting arguments as grids generally do this for you. If in doubt, check the docstrings, ?∇ for example.","category":"page"},{"location":"gradients/#One-more-degree-for-spectral-fields","page":"Gradient operators","title":"One more degree for spectral fields","text":"","category":"section"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","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":"gradients/","page":"Gradient operators","title":"Gradient operators","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":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"You may also generally assume that a SpectralTransform struct precomputed for some truncation, say l_max = m_max = T could also be used for smaller lower triangular matrices. While this is mathematically true, this does not work here in practice because LowerTriangularMatrices are implemented as a vector. So always use a SpectralTransform struct that fits matches your resolution exactly (otherwise an error will be thrown).","category":"page"},{"location":"gradients/#Example:-Geostrophy-(continued)","page":"Gradient operators","title":"Example: Geostrophy (continued)","text":"","category":"section"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","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":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"vor_grid = transform(vor, Grid=spectral_grid.Grid)\nf = coriolis(vor_grid) # create Coriolis parameter f on same grid with default rotation\ng = model.planet.gravity\nfζ_g = @. vor_grid * f / g # in-place and element-wise\nnothing # hide","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"Now we need to apply the inverse Laplace operator to fzetag which we do as follows","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"fζ_g_spectral = transform(fζ_g, one_more_degree=true)\n\nR = spectral_grid.radius\nη = SpeedyTransforms.∇⁻²(fζ_g_spectral) * R^2\nη_grid = transform(η, Grid=spectral_grid.Grid)\nnothing # hide","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"Note the manual scaling with the radius R^2 here. We now compare the results","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"using CairoMakie\nheatmap(η_grid, title=\"Geostrophic interface displacement η [m]\")\nsave(\"eta_geostrophic.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"(Image: Geostrophic eta)","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"Which is the interface displacement assuming geostrophy. The actual interface displacement contains also ageostrophy","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"η_grid2 = simulation.diagnostic_variables.grid.pres_grid\nheatmap(η_grid2, title=\"Interface displacement η [m] with ageostrophy\")\nsave(\"eta_ageostrophic.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"(Image: Ageostrophic eta)","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"Strikingly similar! The remaining differences are the ageostrophic motions but also note that the mean can be 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 can be higher or lower depending on the mass/volume in the shallow water system, see Mass conservation.","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 = (u v) 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_u F_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 u v or zeta mathcalD 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 x y: 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 U V\nUnscale the cos(theta) factor to obtain u v\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 u v 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 (A B)_lm in spectral space and add to forcing of zeta_lm\nCompute the divergence of (A B)_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 uh vh 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 zeta mathcalD 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 u v zeta mathcalD eta 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 0 1 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_mathcalD N_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":"ocean/#Ocean","page":"Ocean","title":"Ocean","text":"","category":"section"},{"location":"ocean/","page":"Ocean","title":"Ocean","text":"The ocean in SpeedyWeather.jl is defined with two horizontal fields in the prognostic variables which has a field ocean, i.e. simulation.prognostic_variables.ocean.","category":"page"},{"location":"ocean/","page":"Ocean","title":"Ocean","text":"ocean.sea_surface_temperature with units of Kelvin [K].\nocean.sea_ice_concentration with units of area fraction [1].","category":"page"},{"location":"ocean/","page":"Ocean","title":"Ocean","text":"Both are two-dimensional grids using the same grid type and resolution as the dynamical core. So both sea surface temperature and sea ice concentration are globally defined but their mask is defined with The land-sea mask. However, one should still set grid cells where the sea surface temperature is not defined to NaN in which case any fluxes are zero. This is important when a fractional land-sea mask does not align with the sea surface temperatures to not produce unphysical fluxes. The sea ice concentration is simply set to zero everywhere where there is no sea ice.","category":"page"},{"location":"ocean/","page":"Ocean","title":"Ocean","text":"Note that neither sea surface temperature, land-sea mask or orography have to agree. It is possible to have an ocean on top of a mountain. For an ocean grid-cell that is (partially) masked by the land-sea mask, its value will be (fractionally) ignored in the calculation of surface fluxes (potentially leading to a zero flux depending on land surface temperatures). For an ocean grid cell that is NaN but not masked by the land-sea mask, its value is always ignored.","category":"page"},{"location":"ocean/#Custom-ocean-model","page":"Ocean","title":"Custom ocean model","text":"","category":"section"},{"location":"ocean/","page":"Ocean","title":"Ocean","text":"Now the ocean model is expected to change ocean.sea_surface_temperature and/or ocean.sea_ice_concentration on a given time step. A new ocean model has to be defined as","category":"page"},{"location":"ocean/","page":"Ocean","title":"Ocean","text":"struct CustomOceanModel <: AbstractOcean\n # fields, coefficients, whatever is constant, \nend","category":"page"},{"location":"ocean/","page":"Ocean","title":"Ocean","text":"and can have parameters like CustomOceanModel{T} and any fields. CustomOceanModel then needs to extend the following functions","category":"page"},{"location":"ocean/","page":"Ocean","title":"Ocean","text":"function initialize!(\n ocean_model::CustomOceanModel,\n model::PrimitiveEquation)\n # your code here to initialize the ocean model itself\n # you can use other fields from model, e.g. model.geometry\nend\n\nfunction initialize!( \n ocean::PrognosticVariablesOcean,\n time::DateTime,\n ocean_model::CustomOceanModel,\n model::PrimitiveEquation)\n \n # your code here to initialize the prognostic variables for the ocean\n # namely, ocean.sea_surface_temperature, ocean.sea_ice_concentration, e.g.\n # ocean.sea_surface_temperature .= 300 # 300K everywhere\n\n # ocean also has its own time, set initial time\n ocean.time = time\nend","category":"page"},{"location":"ocean/","page":"Ocean","title":"Ocean","text":"Note that the first is only to initialize the CustomOceanModel not the prognostic variables. For example SeasonalOceanClimatology <: AbstractOcean loads in climatological sea surface temperatures for every time month in the first initialize! but only writes them (given time) into the prognostic variables in the second initialize!. They are internally therefore also called in that order. Note that the function signatures should not be changed except to define a new method for CustomOceanModel or whichever name you chose.","category":"page"},{"location":"ocean/","page":"Ocean","title":"Ocean","text":"Then you have to extend the ocean_timestep! function which has a signature like","category":"page"},{"location":"ocean/","page":"Ocean","title":"Ocean","text":"function ocean_timestep!(\n ocean::PrognosticVariablesOcean,\n time::DateTime,\n ocean_model::CustomOceanModel,\n)\n # your code here to change the ocean.sea_surface_temperature and/or\n # ocean.sea_ice_concentration on any timestep\n # you should also synchronize the clocks when executed like\n # ocean.time = time\nend","category":"page"},{"location":"ocean/","page":"Ocean","title":"Ocean","text":"which is called on every time step before the land and before the parameterization and therefore also before the dynamics. You can schedule the execution with Schedules or you can use the ocean.time time to determine when last the ocean time step was executed and whether it should be executed now, e.g. (time - ocean.time) < ocean_model.Δt && return nothing would not execute unless the period of the ocean_model.Δt time step has passed. Note that the ocean.sea_surface_temperature or .sea_ice_concentration are unchanged if the ocean time step is not executed, meaning that the sea surface temperatures for example can lag behind the dynamical core for some days essentially assuming constant temperatures throughout that period. Any ocean model with constant temperatures and sea ice should just return nothing.","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 = (0 2pi) and colatitudes theta = (-pi2 pi2), 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 = (0 pi) (0 at the north pole) but the dynamical core uses latitudes theta = (-pi2 pi2) (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(phi theta) (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(phi theta) = sum_l=0^infty sum_m=-l^l a_lm Y_lm(phi theta)","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"We obtain an approximation with a finite set of a_l m 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_l m 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_l m^*. 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_l m=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(phi theta) = sum_l=0^l_max sum_m=0^l (2-delta_m0) a_lm Y_lm(phi theta)","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_l m(phi pi-theta) = (-1)^l+mY_lm(phi phi)","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(lambda theta) into the spectral space of the spherical harmonics by","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"a_l m = int_0^2pi int_0^pi f(phi theta) Y_l m(phi theta) 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 (-fracpi2 fracpi2). A discretization with N grid points at location (phi_i theta_i), indexed by i can be written as [Willmert2020]","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"hata_l m = sum_i f(phi_i theta_i) Y_l m(phi_i theta_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 -1 1 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_l m = sum_j left sum_i f(phi_i theta_j) e^-imphi_i right lambda_l m(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 l m 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 = (u v) 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 u v are then obtained from (u v) = 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(phi theta) (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_l mdtheta = -lepsilon_l+1 mP_l+1 m + (l+1)epsilon_l mP_l-1 m","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_l m = 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(lambda theta) = sum_l mPsi_l mP_l m(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_l mPsi_l me^imlambdacosthetapartial_theta P_l m","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_l m then yields","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"(cos(theta)u)_l m = -frac1R(-(l-1)epsilon_l mPsi_l-1 m + (l+2)epsilon_l+1 mPsi_l+1 m)","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"To obtain the coefficient of each spherical harmonic l m of the meridional gradient of a spectral field, two coefficients at l-1 m and l+1 m 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+1 m Psi_l-1 m. 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 u v 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 u v 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 (u v) = 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_l m = frac12piint_-tfracpi2^tfracpi2int_0^2pi zeta(lambda theta)\nP_l m(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_l m 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_l m (u costheta) dtheta = -int costheta partial_theta P_l m\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+1 mP_l+1 m + (l+1)epsilon_l m P_l-1 m 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 l m harmonic will project ontoP_l m. 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_l m = imv^*_l m + (l+1)epsilon_l mu^*_l-1 m - lepsilon_l+1 mu^*_l+1 m \nRD_l m = imu^*_l m - (l+1)epsilon_l mv^*_l-1 m + lepsilon_l+1 mv^*_l+1 m \nendaligned","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"And the divergence D is similar, but (u v) to (-v u). We have moved the scaling with the radius R directly into zeta D 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_l m = frac-l(l+1)R^2Psi_l m","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_0 0 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_l m = fracR^2-l(l+1)zeta_l m quad foralll m 0 \nPsi_0 0 = 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 U V directly from zeta D 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_l m = -fraciml(l+1)(RD)_l m + fracepsilon_l+1 ml+1(Rzeta)_l+1 m -\nfracepsilon_l ml(Rzeta)_l-1 m \nV_l m = -fraciml(l+1)(Rzeta)_l m - fracepsilon_l+1 ml+1(RD)_l+1 m +\nfracepsilon_l ml(RD)_l-1 m \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 zeta D 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 i j k 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":"stochastic_physics/#Stochastic-physics","page":"Stochastic physics","title":"Stochastic physics","text":"","category":"section"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"Stochastic physics introduces stochasticity into the parameterizations of physical process. There is conceptually several classes of how this can be done","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"Stochastic perturbations of the tendencies\nStochastic parameter perturbations\nStochastic perturbations to the inputs of parameterizations","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"all of these use random numbers created from some random processes (white noise or with some autocorrelation in space or time or sampled from some other distribution) and are applied additive or multiplicative.","category":"page"},{"location":"stochastic_physics/#Stochastic-physics-implementations","page":"Stochastic physics","title":"Stochastic physics implementations","text":"","category":"section"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"Currently implemented is","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"using InteractiveUtils # hide\nusing SpeedyWeather\nsubtypes(SpeedyWeather.AbstractStochasticPhysics)","category":"page"},{"location":"stochastic_physics/#Stochastically-perturbed-parameterization-tendencies-(SPPT)","page":"Stochastic physics","title":"Stochastically perturbed parameterization tendencies (SPPT)","text":"","category":"section"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"SPPT is based on the idea that the dynamics D have a higher certainty but that the Parameterizations P are more uncertain and hence any stochasticity should scale with the size of the tendencies coming from the parameterizations. Conceptually an atmospheric state mathbfx is integrated in time t as","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"fracpartial mathbfxpartial t = D(mathbfx) + P(mathbfx t p)","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"with the parameterizations being a function of the atmospheric state (column only for the single column parameterizations in SpeedyWeather), time t and parameters p. Now SPPT changes this to","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"fracpartial mathbfxpartial t = D(mathbfx) + (1+r)P(mathbfx t p)","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"with r in -1 1 being a random value changing with time and space but not in the vertical (but you could define some tapering). The idea to constrain r in -1 1 is such that","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"the sign up the tendencies P is never reversed\nthe tendencies P are at most 2x larger after perturbation","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"So the stochasticity is multiplicative, in regions/times when the parameterizations are small, so is the perturbation, but with larger tendencies form parameterizations the perturbation is also stronger; satisfying the conceptual idea of uncertainty from the parameterizations scaling with the size of those.","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"You can use SPPT as follows. Start by defining a random_process, this controls how the random values r are created","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"using SpeedyWeather\n\nspectral_grid = SpectralGrid()\nrandom_process = SpectralAR1Process(spectral_grid)","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"and you could change the length scale via wavenumber the time_scale or set a seed for reproducibility. seed=0 (default) means that a random seed is taken from Julia's global random number generator. Now we define SPPT as","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"stochastic_physics = StochasticallyPerturbedParameterizationTendencies(spectral_grid)","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"tapering (by default an anonymous function hence it looks like var\"#269#273\" = #269 as the compiler assigns a \"name\") can be used to change vertically the amplitude of r, e.g tapering = σ -> σ < 0.8 ? 1 : 1 - (σ - 0.8)/0.2 (in Sigma coordinates) could be passed on a (keyword) arugment to reduce the SPPT perturbation towards the surface. A tapering tau(sigma) is applied like (1 + tau r), where r = r(lambda varphi t) is a function of horizontal coordinates longitude lambda, latitude varphi and time t only.","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"Now we pass these on to the model constructor and run a simulation","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"model = PrimitiveWetModel(spectral_grid; random_process, stochastic_physics)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(10))\n\n# surface humidity, kg/kg -> g/kg\nk = spectral_grid.nlayers # surface layer\nhumid = simulation.diagnostic_variables.grid.humid_grid[:, k]*1000\n\nusing CairoMakie\nheatmap(humid, title=\"Surface humidity [g/kg], with SPPT\", colormap=:oslo)\nsave(\"humid_sppt.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"(Image: Surface humidity with SPPT)","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"if we now run the same simulation again (but with a different seed for the random process, that's randomly drawn at initialize!)","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"simulation = initialize!(model)\nrun!(simulation, period=Day(10))\n\n# surface humidity, kg/kg -> g/kg\nhumid = simulation.diagnostic_variables.grid.humid_grid[:, k]*1000\n\nheatmap(humid, title=\"Surface humidity [g/kg], with SPPT, other seed\", colormap=:oslo)\nsave(\"humid_sppt2.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"(Image: Surface humidity with SPPT)","category":"page"},{"location":"callbacks/#Callbacks","page":"Callbacks","title":"Callbacks","text":"","category":"section"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"SpeedyWeather.jl implements a callback system to let users include a flexible piece of code into the time stepping. You can think about the main time loop calling back to check whether anything else should be done before continuing with the next time step. The callback system here is called after the time step only (plus one call at initialize! and one at finalize!), we currently do not implement other callsites.","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Callbacks are mainly introduced for diagnostic purposes, meaning that they do not influence the simulation, and access the prognostic variables and the model components in a read-only fashion. However, a callback is not strictly prevented from changing prognostic or diagnostic variables or the model. For example, you may define a callback that changes the orography during the simulation. In general, one has to keep the general order of executions during a time step in mind (valid for all models)","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"set tendencies to zero\ncompute parameterizations, forcing, or drag terms. Accumulate tendencies.\ncompute dynamics, accumulate tendencies.\ntime stepping\noutput\ncallbacks","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"This means that, at the current callsite, a callback can read the tendencies but writing into it would be overwritten by the zeroing of the tendencies in 1. anyway. At the moment, if a callback wants to implement an additional tendency then it currently should be implemented as a parameterization, forcing or drag term. ","category":"page"},{"location":"callbacks/#Defining-a-callback","page":"Callbacks","title":"Defining a callback","text":"","category":"section"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"You can (and are encouraged!) to write your own callbacks to diagnose SpeedyWeather simulations. Let us implement a StormChaser callback, recording the highest surface wind speed on every time step, that we want to use to illustrate how a callback needs to be defined.","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Every custom callback needs to be defined as a (mutable) struct, subtype of AbstractCallback, i.e. struct or mutable struct CustomCallback <: SpeedyWeather.AbstractCallback. In our case, this is","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"using SpeedyWeather\n\nBase.@kwdef mutable struct StormChaser{NF} <: SpeedyWeather.AbstractCallback\n timestep_counter::Int = 0\n maximum_surface_wind_speed::Vector{NF} = [0]\nend\n\n# Generator function\nStormChaser(SG::SpectralGrid) = StormChaser{SG.NF}()","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"We decide to have a field timestep_counter in the callback that allows us to track the number of times the callback was called to create a time series of our highest surface wind speeds. The actual maximum_surface_wind_speed is then a vector of a given type NF (= number format), which is where we'll write into. Both are initialised with zeros. We also add a generator function, similar as to many other components in SpeedyWeather that just pulls the number format from the SpectralGrid object.","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Now every callback needs to extend three methods","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"initialize!, called once before the main time loop starts\ncallback!, called after every time step\nfinalize!, called once after the last time step","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"And we'll go through them one by one.","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"function SpeedyWeather.initialize!(\n callback::StormChaser,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel,\n)\n # allocate recorder: number of time steps (incl initial conditions) in simulation \n callback.maximum_surface_wind_speed = zeros(progn.clock.n_timesteps + 1)\n \n # where surface (=lowermost model layer) u, v on the grid are stored\n u_grid = diagn.grid.u_grid[:, diagn.nlayers]\n v_grid = diagn.grid.u_grid[:, diagn.nlayers]\n\n # maximum wind speed of initial conditions\n callback.maximum_surface_wind_speed[1] = max_2norm(u_grid, v_grid)\n \n # (re)set counter to 1\n callback.timestep_counter = 1\nend","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"The initialize! function has to be extended for the new callback ::StormChaser as first argument, then followed by prognostic and diagnostic variables and model. For correct multiple dispatch it is important to restrict the first argument to the new StormChaser type (to not call another callback instead), but the other type declarations are for clarity only. initialize!(::AbstractCallback, args...) is called once just before the main time loop, meaning after the initial conditions are set and after all other components are initialized. We replace the vector inside our storm chaser with a vector of the correct length so that we have a \"recorder\" allocated, a vector that can store the maximum surface wind speed on every time step. We then also compute that maximum for the initial conditions and set the time step counter to 1. We define the max_2norm function as follows","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"\"\"\"Maximum of the 2-norm of elements across two arrays.\"\"\"\nfunction max_2norm(u::AbstractArray{T}, v::AbstractArray{T}) where T\n max_norm = zero(T) # = u² + v²\n for ij in eachindex(u, v)\n # find largest wind speed squared\n max_norm = max(max_norm, u[ij]^2 + v[ij]^2)\n end\n return sqrt(max_norm) # take sqrt only once\nend","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Note that this function is defined in the scope Main and not inside SpeedyWeather, this is absolutely possible due to Julia's scope of variables which will use max_2norm from Main scope if it doesn't exist in the global scope inside the SpeedyWeather module scope. Then we need to extend the callback! function as follows","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"function SpeedyWeather.callback!(\n callback::StormChaser,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel,\n)\n\n # increase counter\n callback.timestep_counter += 1 \n i = callback.timestep_counter\n\n # where surface (=lowermost model layer) u, v on the grid are stored\n u_grid = diagn.grid.u_grid[:, diagn.nlayers]\n v_grid = diagn.grid.u_grid[:, diagn.nlayers]\n\n # maximum wind speed at current time step\n callback.maximum_surface_wind_speed[i] = max_2norm(u_grid, v_grid)\nend","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"The function signature for callback! is the same as for initialize!. You may access anything from progn, diagn or model, although for a purely diagnostic callback this should be read-only. While you could change other model components like the land sea mask in model.land_sea_mask or orography etc. then you interfere with the simulation which is more advanced and will be discussed in Intrusive callbacks below.","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Lastly, we extend the finalize! function which is called once after the last time step. This could be used, for example, to save the maximum_surface_wind_speed vector to file or in case you want to find the highest wind speed across all time steps. But in many cases you may not need to do anything, in which case you just just let it return nothing.","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"SpeedyWeather.finalize!(::StormChaser, args...) = nothing","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"note: Always extend `initialize!`, `callback!` and `finalize!`\nFor a custom callback you need to extend all three, initialize!, callback! and finalize!, even if your callback doesn't need it. Just return nothing in that case. Otherwise a MethodError will occur. While we could have defined all callbacks by default to do nothing on each of these, this may give you the false impression that your callback is already defined correctly, although it's not.","category":"page"},{"location":"callbacks/#Adding-a-callback","page":"Callbacks","title":"Adding a callback","text":"","category":"section"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Every model has a field callbacks::Dict{Symbol, AbstractCallback} such that the callbacks keyword can be used to create a model with a dictionary of callbacks. Callbacks are identified with a Symbol key inside such a dictionary. We have a convenient CallbackDict generator function which can be used like Dict but the key-value pairs have to be of type Symbol-AbstractCallback. Let us illustrate this with the dummy callback NoCallback (which is a callback that returns nothing on initialize!, callback! and finalize!)","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"callbacks = CallbackDict() # empty dictionary\ncallbacks = CallbackDict(:my_callback => NoCallback()) # key => callback","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"If you don't provide a key a random key will be assigned","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"callbacks = CallbackDict(NoCallback())","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"and you can add (or delete) additional callbacks","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"add!(callbacks, NoCallback()) # this will also pick a random key\nadd!(callbacks, :my_callback => NoCallback()) # use key :my_callback\ndelete!(callbacks, :my_callback) # remove by key\ncallbacks","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"And you can chain them too","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"add!(callbacks, NoCallback(), NoCallback()) # random keys\nadd!(callbacks, :key1 => NoCallback(), :key2 => NoCallback()) # keys provided","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Meaning that callbacks can be added before and after model construction","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"spectral_grid = SpectralGrid()\ncallbacks = CallbackDict(:callback_added_before => NoCallback())\nmodel = PrimitiveWetModel(spectral_grid; callbacks)\nadd!(model.callbacks, :callback_added_afterwards => NoCallback())\nadd!(model, :callback_added_afterwards2 => NoCallback())","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Note how the first argument can be model.callbacks as outlined in the sections above because this is the callbacks dictionary, but also simply model, which will add the callback to model.callbacks. It's equivalent. Let us add two more meaningful callbacks","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"storm_chaser = StormChaser(spectral_grid)\nrecord_surface_temperature = GlobalSurfaceTemperatureCallback(spectral_grid)\nadd!(model.callbacks, :storm_chaser => storm_chaser)\nadd!(model.callbacks, :temperature => record_surface_temperature)","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"which means that now in the calls to callback! first the two dummy NoCallbacks are called and then our storm chaser callback and then the GlobalSurfaceTemperatureCallback which records the global mean surface temperature on every time step. From normal NetCDF output the information these callbacks analyse would not be available, only at the frequency of the model output, which for every time step would create way more data and considerably slow down the simulation. Let's run the simulation and check the callbacks","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"simulation = initialize!(model)\nrun!(simulation, period=Day(3))\nv = model.callbacks[:storm_chaser].maximum_surface_wind_speed\nmaximum(v) # highest surface wind speeds in simulation [m/s]","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Cool, our StormChaser callback with the key :storm_chaser has been recording maximum surface wind speeds in [m/s]. And the :temperature callback a time series of global mean surface temperatures in Kelvin on every time step while the model ran for 3 days.","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"model.callbacks[:temperature].temp","category":"page"},{"location":"callbacks/#intrusive_callbacks","page":"Callbacks","title":"Intrusive callbacks","text":"","category":"section"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"In the sections above, callbacks were introduced as a tool to define custom diagnostics or simulation output. This is the simpler and recommended way of using them but nothing stops you from defining a callback that is intrusive, meaning that it can alter the prognostic or diagnostic variables or the model.","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Changing any components of the model, e.g. boundary conditions like orography or the land-sea mask through a callback is possible although one should notice that this only comes into effect on the next time step given the execution order mentioned above. One could for example run a simulation for a certain period and then start moving continents around. Note that for physical consistency this should be reflected in the orography, land-sea mask, as well as in the available sea and land-surface temperatures, but one is free to do this only partially too. Another example would be to switch on/off certain model components over time. If these components are implemented as mutable struct then one could define a callback that weakens their respective strength parameter over time.","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"As an example of a callback that changes the model components see","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Millenium flood: Time-dependent land-sea mask","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Changing the diagnostic variables, however, will not have any effect. All of them are treated as work arrays, meaning that their state is completely overwritten on every time step. Changing the prognostic variables in spectral space directly is not advised though possible because this can easily lead to stability issues. It is generally easier to implement something like this as a parameterization, forcing or drag term (which can also be made time-dependent).","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Overall, callbacks give the user a wide range of possibilities to diagnose the simulation while running or to interfere with a simulation. We therefore encourage users to use callbacks as widely as possible, but if you run into any issues please open an issue in the repository and explain what you'd like to achieve and which errors you are facing. We are happy to help.","category":"page"},{"location":"callbacks/#Schedules","page":"Callbacks","title":"Schedules","text":"","category":"section"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"For convenience, SpeedyWeather.jl implements a Schedule which helps to schedule when callbacks are called. Because in many situations you don't want to call them on every time step but only periodically, say once a day, or only on specific dates and times, e.g. Jan 1 at noon. Several examples how to create schedules","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"using SpeedyWeather\n\n# execute on timestep at or after Jan 2 2000\nevent_schedule = Schedule(DateTime(2000,1,2)) \n\n# several events scheduled\nevents = (DateTime(2000,1,3), DateTime(2000,1,5,12))\nseveral_events_schedule = Schedule(events...)\n\n# provided as Vector{DateTime} with times= keyword\nalways_at_noon = [DateTime(2000,1,i,12) for i in 1:10]\nnoon_schedule = Schedule(times=always_at_noon)\n\n# or using every= for periodic execution, here once a day\nperiodic_schedule = Schedule(every=Day(1))","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"A Schedule has 5 fields, see Schedule. every is an option to create a periodic schedule to execute every time that indicated period has passed. steps and counter will let you know how many callback execution steps there are and count them up. times is a Vector{DateTime} containing scheduled events. schedule is the actual schedule inside a Schedule, implemented as BitVector indicating whether to execute on a given time step (true) or not (false).","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Let's show how to use a Schedule inside a callback","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"struct MyScheduledCallback <: SpeedyWeather.AbstractCallback\n schedule::Schedule\n # add other fields here that you need\nend\n\nfunction SpeedyWeather.initialize!(\n callback::MyScheduledCallback,\n progn::PrognosticVariables,\n args...\n)\n # when initializing a scheduled callback also initialize its schedule!\n initialize!(callback.schedule, progn.clock)\n\n # initialize other things in your callback here\nend\n\nfunction SpeedyWeather.callback!(\n callback::MyScheduledCallback,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel,\n)\n # scheduled callbacks start with this line to execute only when scheduled!\n # else escape immediately\n isscheduled(callback.schedule, progn.clock) || return nothing\n\n # Just print the North Pole surface temperature to screen\n (;time) = progn.clock\n temp_at_north_pole = diagn.grid.temp_grid[1,end]\n\n @info \"North pole has a temperature of $temp_at_north_pole on $time.\"\nend\n\n# nothing needs to be done after simulation is finished\nSpeedyWeather.finalize!(::MyScheduledCallback, args...) = nothing","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"So in summary","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"add a field schedule::Schedule to your callback\nadd the line initialize!(callback.schedule, progn.clock) when initializing your callback\nstart your callback! method with isscheduled(callback.schedule, progn.clock) || return nothing to execute only when scheduled","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"A Schedule is a field inside a callback as this allows you the set the callbacks desired schedule when creating it. In the example above we can create our callback that is supposed to print the North Pole's temperature like so","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"north_pole_temp_at_noon_jan9 = MyScheduledCallback(Schedule(DateTime(2000,1,9,12)))","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"The default for every is typemax(Int) indicating \"never\". This just means that there is no periodically reoccuring schedule, only schedule.times would include some times for events that are scheduled. Now let's create a primitive equation model with that callback","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"spectral_grid = SpectralGrid(trunc=31, nlayers=5)\nmodel = PrimitiveWetModel(spectral_grid)\nadd!(model.callbacks, north_pole_temp_at_noon_jan9)\n\n# start simulation 7 days earlier\nsimulation = initialize!(model, time = DateTime(2000,1,2,12))\nrun!(simulation, period=Day(10))\nnothing # hide","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"So the callback gives us the temperature at the North Pole exactly when scheduled. We could have also stored this temperature, or conditionally changed parameters inside the model. There are plenty of ways how to use the scheduling, either by event, or in contrast, we could also schedule for once a day. As illustrated in the following","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"north_pole_temp_daily = MyScheduledCallback(Schedule(every=Day(1)))\nadd!(model.callbacks, north_pole_temp_daily)\n\n# resume simulation, start time is now 2000-1-12 noon\nrun!(simulation, period=Day(5))\nnothing # hide","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Note that the previous callback is still part of the model, we haven't deleted it with delete!. But because it's scheduled for a specific time that's in the past now that we resume the simulation it's schedule is empty (which is thrown as a warning). However, our new callback, scheduled daily, is active and prints daily at noon, because the simulation start time was noon.","category":"page"},{"location":"callbacks/#Scheduling-logic","page":"Callbacks","title":"Scheduling logic","text":"","category":"section"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"An event Schedule (created with DateTime object(s)) for callbacks, executes on or after the specified times. For two consecutive time steps i, i+1, an event is scheduled at i+1 when it occurs in (ii+1. So a simulation with timestep i on Jan-1 at 1am, and i+1 at 2am, will execute a callback scheduled for 1am at i but scheduled for 1am and 1s (=01:00:01 on a 24H clock) at 2am. Because callbacks are always executed after a timestep this also means that a simulation starting at midnight with a callback scheduled for midnight will not execute this callback as it is outside of the (i i+1 range. You'd need to include this execution into the initialization. If several events inside the Schedule fall into the same time step (in the example above, 1am and 1s and 1am 30min) the execution will not happen twice. Think of a scheduled callback as a binary \"should the callback be executed now or not?\". Which is in fact how it's implemented, as a BitVector of the length of the number of time steps. If the bit at a given timestep is true, execute, otherwise not.","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"A periodic Schedule (created with every = Hour(2) or similar) will execute on the timestep after that period (here 2 hours) has passed. If a simulation starts at midnight with one hour time steps then execution would take place after the timestep from 1am to 2am because that's when the clock switches to 2am which is 2 hours after the start of the simulation. Note that therefore the initial timestep is not included, however, the last time step would be if the period is a multiple of the scheduling period. If the first timestep should be included (e.g. you want to do something with the initial conditions) then you'll need to include that into the initialization of the callback.","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Periodic schedules which do not match the simulation time step will be adjusted by rounding. Example, if you want a schedule which executes every hour but your simulation time step is 25min then it will be adjusted to execute every 2nd time step, meaning every 50min and not 1 hour. However, an info will be thrown if that is the case","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"odd_schedule = MyScheduledCallback(Schedule(every = Minute(70)))\nadd!(model.callbacks, odd_schedule)\n\n# resume simulation for 4 hours\nrun!(simulation, period=Hour(4))\nnothing # hide","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Now we get two empty schedules, one from callback that's supposed to execute on Jan 9 noon (this time has passed in our simulation) and one from the daily callback (we're not simulating for a day). You could just delete! those callbacks. You can see that while we wanted our odd_schedule to execute every 70min, it has to adjust it to every 60min to match the simulation time step of 30min.","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"After the model initialization you can always check the simulation time step from model.time_stepping ","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"model.time_stepping","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Or converted into minutes (the time step internally is at millisecond accuracy)","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Minute(model.time_stepping.Δt_millisec)","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"which illustrates why the adjustment of our callback frequency was necessary.","category":"page"},{"location":"ringgrids/#RingGrids","page":"RingGrids","title":"RingGrids","text":"","category":"section"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"RingGrids defines and exports the following grids:","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"full grids: FullClenshawGrid, FullGaussianGrid, FullHEALPix, and FullOctaHEALPix\nreduced grids: OctahedralGaussianGrid, OctahedralClenshawGrid, OctaHEALPixGrid and HEALPixGrid","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"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":"RingGrids","title":"Creating data on a RingGrid","text":"","category":"section"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"Data in a Matrix which follows this ring order can be put on a FullGaussianGrid like so","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"using SpeedyWeather.RingGrids\nmap = randn(Float32, 8, 4)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"grid = FullGaussianGrid(map, input_as=Matrix)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"Note that input_as=Matrix is necessary as, RingGrids have a flattened horizontal dimension into a vector. To distinguish the 2nd horizontal dimension from a possible vertical dimension the keyword argument here is required.","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"grid.data","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"map == Matrix(FullGaussianGrid(map))","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"nlat_half = 4\ngrid = randn(OctahedralGaussianGrid{Float16}, nlat_half)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"Visualising RingGrid data","text":"","category":"section"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"nlat_half = 24\ngrid = randn(OctahedralGaussianGrid, nlat_half)\nRingGrids.plot(grid)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"(Note that to skip the RingGrids. in the last line you can do import SpeedyWeather.RingGrids: plot, import SpeedyWeather: plot or simply using SpeedyWeather. It's just that LowerTriangularMatrices also defines plot which otherwise causes naming conflicts.)","category":"page"},{"location":"ringgrids/#Indexing-RingGrids","page":"RingGrids","title":"Indexing RingGrids","text":"","category":"section"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"grid = randn(OctahedralClenshawGrid, 5)\nlatd = get_latd(grid)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"Now we could calculate Coriolis and add it on the grid as follows","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"for ij in eachgridpoint(grid)\n grid[ij]\nend","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"or use eachindex instead.","category":"page"},{"location":"ringgrids/#Interpolation-on-RingGrids","page":"RingGrids","title":"Interpolation on RingGrids","text":"","category":"section"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"grid = randn(OctahedralGaussianGrid{Float32}, 4)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"interpolate(FullGaussianGrid, grid)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"interpolate(FullGaussianGrid, 6, grid)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"One can also interpolate onto a given coordinate ˚N, ˚E like so","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"interpolate(30.0, 10.0, grid)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"interpolate([30.0, 40.0, 50.0], [10.0, 10.0, 10.0], grid)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"Performance for RingGrid interpolation","text":"","category":"section"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"output vector\ninput grid\ninterpolator","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"grid_in = rand(HEALPixGrid, 4)\ngrid_out = zeros(FullClenshawGrid, 6)\ninterp = RingGrids.interpolator(grid_out, grid_in)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"interpolate!(grid_out, grid_in, interp)\ngrid_out","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"grid_out = zeros(FullClenshawGrid{Float16}, 6);\ninterpolate!(grid_out, grid_in, interp)\ngrid_out","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"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":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"npoints = 10 # number of coordinates to interpolate onto\ninterp = AnvilInterpolator(Float32, HEALPixGrid, 24, npoints)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"now we can update the locator inside our interpolator as follows","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"RingGrids.update_locator!(interp, latds, londs)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"output_vec = zeros(10)\ngrid_input = rand(HEALPixGrid, 24)\nnothing # hide","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"we can use the interpolator as follows","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"interpolate!(output_vec, grid_input, interp)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"interpolate(latds, londs, grid_input)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"Anvil interpolator","text":"","category":"section"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"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":"RingGrids","title":"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":"RingGrids","title":"Function index","text":"","category":"section"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"Modules = [SpeedyWeather.RingGrids]","category":"page"},{"location":"ringgrids/#Core.Type-Tuple{AbstractArray}","page":"RingGrids","title":"Core.Type","text":"() Initialize an instance of the grid from an Array. For keyword argument input_as=Vector (default) the leading dimension is interpreted as a flat vector of all horizontal entries in one layer. For input_as==Matrx the first two leading dimensions are interpreted as longitute and latitude. This is only possible for full grids that are a subtype of AbstractFullGridArray.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractFullGrid","page":"RingGrids","title":"SpeedyWeather.RingGrids.AbstractFullGrid","text":"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 latitudes (also called Clenshaw from Clenshaw-Curtis quadrature), or others.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractFullGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.AbstractFullGridArray","text":"Subtype of AbstractGridArray for all N-dimensional arrays of ring grids that have the same number of longitude points on every ring. As such these (horizontal) grids are representable as a matrix, with denser grid points towards the poles.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractGrid","page":"RingGrids","title":"SpeedyWeather.RingGrids.AbstractGrid","text":"Abstract supertype for all ring grids, representing 2-dimensional data on the sphere unravelled into a Julia Vector. Subtype of AbstractGridArray with N=1 and ArrayType=Vector{T} of eltype T.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.AbstractGridArray","text":"Abstract supertype for all arrays of ring grids, representing N-dimensional data on the sphere in two dimensions (but unravelled into a vector in the first dimension, the actual \"ring grid\") plus additional N-1 dimensions for the vertical and/or time etc. Parameter T is the eltype of the underlying data, held as in the array type ArrayType (Julia's Array for CPU or others for GPU).\n\nRing grids have several consecuitive grid points share the same latitude (= a ring), grid points on a given ring are equidistant. Grid points are ordered 0 to 360˚E, starting around the north pole, ring by ring to the south pole. \n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractInterpolator","page":"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":"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.AbstractReducedGrid","page":"RingGrids","title":"SpeedyWeather.RingGrids.AbstractReducedGrid","text":"Horizontal abstract type for all AbstractReducedGridArray with N=1 (i.e. horizontal only) and ArrayType of Vector{T} with element type T.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractReducedGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.AbstractReducedGridArray","text":"Subtype of AbstractGridArray for arrays of rings grids that have a reduced number of longitude points towards the poles, i.e. they are not \"full\", see AbstractFullGridArray. Data on these grids cannot be represented as matrix and has to be unravelled into a vector, ordered 0 to 360˚E then north to south, ring by ring. Examples for reduced grids are the octahedral Gaussian or Clenshaw grids, or the HEALPix grid.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractSphericalDistance","page":"RingGrids","title":"SpeedyWeather.RingGrids.AbstractSphericalDistance","text":"abstract type AbstractSphericalDistance end\n\nSuper type of formulas to calculate the spherical distance or great-circle distance. To define a NewFormula, define struct NewFormula <: AbstractSphericalDistance end and the actual calculation as a functor\n\nfunction NewFormula(lonlat1::Tuple, lonlat2::Tuple; radius=DEFAULT_RADIUS, kwargs...)\n\nassuming inputs in degrees and returning the distance in meters (or radians for radius=1). Then use the general interface spherical_distance(NewFormula, args...; kwargs...)\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AnvilLocator","page":"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":"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.FullClenshawArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.FullClenshawArray","text":"A FullClenshawArray is an array of full grid, subtyping AbstractFullGridArray, that use equidistant latitudes for each ring (a regular lon-lat grid). These require the Clenshaw-Curtis quadrature in the spectral transform, hence the name. One ring is on the equator, total number of rings is odd, no rings on the north or south pole. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings. Fields are\n\ndata::AbstractArray\nnlat_half::Int64\nrings::Vector{UnitRange{Int64}}\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.FullClenshawGrid","page":"RingGrids","title":"SpeedyWeather.RingGrids.FullClenshawGrid","text":"A FullClenshawArray but constrained to N=1 dimensions (horizontal only) and data is a Vector{T}.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.FullGaussianArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.FullGaussianArray","text":"A FullGaussianArray is an array of full grids, subtyping AbstractFullGridArray, that use Gaussian latitudes for each ring. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings. Fields are\n\ndata::AbstractArray: Data array, west to east, ring by ring, north to south.\nnlat_half::Int64: Number of latitudes on one hemisphere\nrings::Vector{UnitRange{Int64}}: Precomputed ring indices, ranging from first to last grid point on every ring.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.FullGaussianGrid","page":"RingGrids","title":"SpeedyWeather.RingGrids.FullGaussianGrid","text":"A FullGaussianArray but constrained to N=1 dimensions (horizontal only) and data is a Vector{T}.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.FullHEALPixArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.FullHEALPixArray","text":"A FullHEALPixArray is an array of full grids, subtyping AbstractFullGridArray, that use HEALPix latitudes for each ring. This type primarily equists to interpolate data from the (reduced) HEALPixGrid onto a full grid for output.\n\nFirst dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings. Fields are\n\ndata::AbstractArray\nnlat_half::Int64\nrings::Vector{UnitRange{Int64}}\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.FullHEALPixGrid","page":"RingGrids","title":"SpeedyWeather.RingGrids.FullHEALPixGrid","text":"A FullHEALPixArray but constrained to N=1 dimensions (horizontal only) and data is a Vector{T}.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.FullOctaHEALPixArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.FullOctaHEALPixArray","text":"A FullOctaHEALPixArray is an array of full grids, subtyping AbstractFullGridArray that use OctaHEALPix latitudes for each ring. This type primarily equists to interpolate data from the (reduced) OctaHEALPixGrid onto a full grid for output.\n\nFirst dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings. Fields are\n\ndata::AbstractArray\nnlat_half::Int64\nrings::Vector{UnitRange{Int64}}\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.FullOctaHEALPixGrid","page":"RingGrids","title":"SpeedyWeather.RingGrids.FullOctaHEALPixGrid","text":"A FullOctaHEALPixArray but constrained to N=1 dimensions (horizontal only) and data is a Vector{T}.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.GridGeometry","page":"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{<:AbstractGrid}, Integer}","page":"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.HEALPixArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.HEALPixArray","text":"A HEALPixArray is an array of HEALPix grids, subtyping AbstractReducedGridArray. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) which has to be even (non-fatal error thrown otherwise) which is less strict than the original HEALPix formulation (only powers of two for nside = nlat_half/2). Ring indices are precomputed in rings.\n\nA HEALPix grid has 12 faces, each nsidexnside grid points, each covering the same area of the sphere. They start with 4 longitude points on the northern-most ring, increase by 4 points per ring in the \"polar cap\" (the top half of the 4 northern-most faces) but have a constant number of longitude points in the equatorial belt. The southern hemisphere is symmetric to the northern, mirrored around the Equator. HEALPix grids have a ring on the Equator. For more details see Górski et al. 2005, DOI:10.1086/427976. \n\nrings are the precomputed ring indices, for nlat_half = 4 it is rings = [1:4, 5:12, 13:20, 21:28, 29:36, 37:44, 45:48]. So the first ring has indices 1:4 in the unravelled first dimension, etc. For efficient looping see eachring and eachgrid. Fields are\n\ndata::AbstractArray\nnlat_half::Int64\nrings::Vector{UnitRange{Int64}}\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.HEALPixGrid","page":"RingGrids","title":"SpeedyWeather.RingGrids.HEALPixGrid","text":"An HEALPixArray but constrained to N=1 dimensions (horizontal only) and data is a Vector{T}.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.Haversine-Tuple{Tuple, Tuple}","page":"RingGrids","title":"SpeedyWeather.RingGrids.Haversine","text":"Haversine(lonlat1::Tuple, lonlat2::Tuple; radius) -> Any\n\n\nHaversine formula calculating the great-circle or spherical distance (in meters) on the sphere between two tuples of longitude-latitude points in degrees ˚E, ˚N. Use keyword argument radius to change the radius of the sphere (default 6371e3 meters, Earth's radius), use radius=1 to return the central angle in radians or radius=360/2π to return degrees.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.OctaHEALPixArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.OctaHEALPixArray","text":"An OctaHEALPixArray is an array of OctaHEALPix grids, subtyping AbstractReducedGridArray. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings.\n\nAn OctaHEALPix grid has 4 faces, each nlat_half x nlat_half in size, covering 90˚ in longitude, pole to pole. As part of the HEALPix family of grids, the grid points are equal area. They start with 4 longitude points on the northern-most ring, increase by 4 points per ring towards the Equator with one ring on the Equator before reducing the number of points again towards the south pole by 4 per ring. There is no equatorial belt for OctaHEALPix grids. The southern hemisphere is symmetric to the northern, mirrored around the Equator. OctaHEALPix grids have a ring on the Equator. For more details see Górski et al. 2005, DOI:10.1086/427976, the OctaHEALPix grid belongs to the family of HEALPix grids with Nθ = 1, Nφ = 4 but is not explicitly mentioned therein.\n\nrings are the precomputed ring indices, for nlat_half = 3 (in contrast to HEALPix this can be odd) it is rings = [1:4, 5:12, 13:24, 25:32, 33:36]. For efficient looping see eachring and eachgrid. Fields are\n\ndata::AbstractArray\nnlat_half::Int64\nrings::Vector{UnitRange{Int64}}\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.OctaHEALPixGrid","page":"RingGrids","title":"SpeedyWeather.RingGrids.OctaHEALPixGrid","text":"An OctaHEALPixArray but constrained to N=1 dimensions (horizontal only) and data is a Vector{T}.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.OctahedralClenshawArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.OctahedralClenshawArray","text":"An OctahedralClenshawArray is an array of octahedral grids, subtyping AbstractReducedGridArray, that use equidistant latitudes for each ring, the same as for FullClenshawArray. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings.\n\nThese grids are called octahedral (same as for the OctahedralGaussianArray which only uses different latitudes) because after starting with 20 points on the first ring around the north pole (default) they increase the number of longitude points for each ring by 4, such that they can be conceptually thought of as lying on the 4 faces of an octahedron on each hemisphere. Hence, these grids have 20, 24, 28, ... longitude points for ring 1, 2, 3, ... Clenshaw grids have a ring on the Equator which has 16 + 4nlat_half longitude points before reducing the number of longitude points per ring by 4 towards the southern-most ring j = nlat. rings are the precomputed ring indices, the the example above rings = [1:20, 21:44, 45:72, ...]. For efficient looping see eachring and eachgrid. Fields are\n\ndata::AbstractArray\nnlat_half::Int64\nrings::Vector{UnitRange{Int64}}\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.OctahedralClenshawGrid","page":"RingGrids","title":"SpeedyWeather.RingGrids.OctahedralClenshawGrid","text":"An OctahedralClenshawArray but constrained to N=1 dimensions (horizontal only) and data is a Vector{T}.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.OctahedralGaussianArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.OctahedralGaussianArray","text":"An OctahedralGaussianArray is an array of octahedral grids, subtyping AbstractReducedGridArray, that use Gaussian latitudes for each ring. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings.\n\nThese grids are called octahedral because after starting with 20 points on the first ring around the north pole (default) they increase the number of longitude points for each ring by 4, such that they can be conceptually thought of as lying on the 4 faces of an octahedron on each hemisphere. Hence, these grids have 20, 24, 28, ... longitude points for ring 1, 2, 3, ... There is no ring on the Equator and the two rings around it have 16 + 4nlat_half longitude points before reducing the number of longitude points per ring by 4 towards the southern-most ring j = nlat. rings are the precomputed ring indices, the the example above rings = [1:20, 21:44, 45:72, ...]. For efficient looping see eachring and eachgrid. Fields are\n\ndata::AbstractArray\nnlat_half::Int64\nrings::Vector{UnitRange{Int64}}\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.OctahedralGaussianGrid","page":"RingGrids","title":"SpeedyWeather.RingGrids.OctahedralGaussianGrid","text":"An OctahedralGaussianArray but constrained to N=1 dimensions (horizontal only) and data is a Vector{T}.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.OctaminimalGaussianArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.OctaminimalGaussianArray","text":"An OctaminimalGaussianArray is an array of octahedral grids, subtyping AbstractReducedGridArray, that use Gaussian latitudes for each ring. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings.\n\nThese grids are called octahedral because after starting with 4 points on the first ring around the north pole (default) they increase the number of longitude points for each ring by 4, such that they can be conceptually thought of as lying on the 4 faces of an octahedron on each hemisphere. Hence, these grids have 4, 8, 12, ... longitude points for ring 1, 2, 3, ... which is in contrast to the OctahedralGaussianArray which starts with 20 points around the poles, hence \"minimal\". There is no ring on the Equator and the two rings around it have 4nlat_half longitude points before reducing the number of longitude points per ring by 4 towards the southern-most ring j = nlat. rings are the precomputed ring indices, in the example above rings = [1:4, 5:12, 13:24, ...]. For efficient looping see eachring and eachgrid. Fields are\n\ndata::AbstractArray\nnlat_half::Int64\nrings::Vector{UnitRange{Int64}}\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.OctaminimalGaussianGrid","page":"RingGrids","title":"SpeedyWeather.RingGrids.OctaminimalGaussianGrid","text":"An OctaminimalGaussianArray but constrained to N=1 dimensions (horizontal only) and data is a Vector{T}.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#Base.sizeof-Tuple{AbstractGridArray}","page":"RingGrids","title":"Base.sizeof","text":"sizeof(G::AbstractGridArray) -> Any\n\n\nSize of underlying data array plus precomputed ring indices.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.Matrix!-Tuple{AbstractMatrix, OctaHEALPixGrid}","page":"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":"RingGrids","title":"SpeedyWeather.RingGrids.Matrix!","text":"Matrix!(\n M::AbstractMatrix,\n G::OctahedralClenshawGrid;\n kwargs...\n) -> AbstractMatrix\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":"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":"RingGrids","title":"SpeedyWeather.RingGrids.Matrix!","text":"Matrix!(\n MGs::Tuple{AbstractArray{T, 2}, OctahedralClenshawGrid}...;\n quadrant_rotation,\n matrix_quadrant\n) -> Union{Tuple, AbstractMatrix}\n\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{T}, Tuple{AbstractGridArray{T, N, ArrayType} where {N, ArrayType<:AbstractArray{T, N}}, AbstractVector}} where T","page":"RingGrids","title":"SpeedyWeather.RingGrids._scale_lat!","text":"_scale_lat!(\n grid::AbstractGridArray{T, N, ArrayType} where {N, ArrayType<:AbstractArray{T, N}},\n v::AbstractVector\n) -> AbstractGridArray\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":"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{AbstractGrid{NF}, Vector{<:UnitRange{<:Integer}}}} where NF<:Integer","page":"RingGrids","title":"SpeedyWeather.RingGrids.average_on_poles","text":"average_on_poles(\n A::AbstractGridArray{NF<:Integer, 1, Array{NF<:Integer, 1}},\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.average_on_poles-Union{Tuple{NF}, Tuple{AbstractVector{NF}, Vector{<:UnitRange{<:Integer}}}} where NF<:AbstractFloat","page":"RingGrids","title":"SpeedyWeather.RingGrids.average_on_poles","text":"average_on_poles(\n A::AbstractArray{NF<:AbstractFloat, 1},\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.check_inputs-NTuple{4, Any}","page":"RingGrids","title":"SpeedyWeather.RingGrids.check_inputs","text":"check_inputs(data, nlat_half, rings, Grid) -> Any\n\n\nTrue for data, nlat_half and rings that all match in size to construct a grid of type Grid.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.clenshaw_curtis_weights-Tuple{Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.clenshaw_curtis_weights","text":"clenshaw_curtis_weights(nlat_half::Integer) -> Any\n\n\nThe Clenshaw-Curtis weights for a Clenshaw grid (full or octahedral) of size nlathalf. Clenshaw-Curtis weights are of length nlat, i.e. a vector for every latitude ring, pole to pole. `sum(clenshawcurtisweights(nlathalf))is always2` as int0^π sin(x) dx = 2 (colatitudes), or equivalently int-pi/2^pi/2 cos(x) dx (latitudes).\n\nIntegration (and therefore the spectral transform) is exact (only rounding errors) when using Clenshaw grids provided that nlat >= 2(T + 1), meaning that a grid resolution of at least 128x64 (nlon x nlat) is sufficient for an exact transform with a T=31 spectral truncation.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.each_index_in_ring!-Tuple{Vector{<:UnitRange{<:Integer}}, Type{<:OctahedralGaussianArray}, Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.each_index_in_ring!","text":"each_index_in_ring!(\n rings::Vector{<:UnitRange{<:Integer}},\n Grid::Type{<:OctahedralGaussianArray},\n nlat_half::Integer\n)\n\n\nprecompute a Vector{UnitRange{Int}} to index grid points on every ringj(elements of the vector) ofGridat resolutionnlat_half. Seeeachringandeachgrid` for efficient looping over grid points.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.each_index_in_ring!-Tuple{Vector{<:UnitRange{<:Integer}}, Type{<:OctaminimalGaussianArray}, Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.each_index_in_ring!","text":"each_index_in_ring!(\n rings::Vector{<:UnitRange{<:Integer}},\n Grid::Type{<:OctaminimalGaussianArray},\n nlat_half::Integer\n)\n\n\nprecompute a Vector{UnitRange{Int}} to index grid points on every ringj(elements of the vector) ofGridat resolutionnlat_half. Seeeachringandeachgrid` for efficient looping over grid points.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.each_index_in_ring-Tuple{Type{<:SpeedyWeather.RingGrids.AbstractFullGridArray}, Integer, Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.each_index_in_ring","text":"each_index_in_ring(\n Grid::Type{<:SpeedyWeather.RingGrids.AbstractFullGridArray},\n j::Integer,\n nlat_half::Integer\n) -> Any\n\n\nUnitRange for every grid point of grid Grid of resolution nlat_half on ring j (j=1 is closest ring around north pole, j=nlat around south pole).\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.each_index_in_ring-Union{Tuple{Grid}, Tuple{Grid, Integer}} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.each_index_in_ring","text":"each_index_in_ring(\n grid::AbstractGridArray,\n j::Integer\n) -> Any\n\n\nUnitRange to access data on grid grid on ring j.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.eachgrid-Tuple{AbstractGridArray}","page":"RingGrids","title":"SpeedyWeather.RingGrids.eachgrid","text":"eachgrid(grid::AbstractGridArray) -> Any\n\n\nCartesianIndices for the 2nd to last dimension of an AbstractGridArray, to be used like\n\nfor k in eachgrid(grid) for ring in eachring(grid) for ij in ring grid[ij, k]\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.eachgridpoint-Tuple{AbstractGridArray}","page":"RingGrids","title":"SpeedyWeather.RingGrids.eachgridpoint","text":"eachgridpoint(grid::AbstractGridArray) -> Base.OneTo\n\n\nUnitRange to access each horizontal grid point on grid grid. For a NxM (N horizontal grid points, M vertical layers) OneTo(N) is returned.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.eachgridpoint-Union{Tuple{Grid}, Tuple{Grid, Vararg{Grid}}} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.eachgridpoint","text":"eachgridpoint(\n grid1::AbstractGridArray,\n grids::Grid<:AbstractGridArray...\n) -> Base.OneTo\n\n\nLike eachgridpoint(::AbstractGridArray) but checks for equal size between input arguments first.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.eachring-Tuple{AbstractGridArray, Vararg{AbstractGridArray}}","page":"RingGrids","title":"SpeedyWeather.RingGrids.eachring","text":"eachring(\n grid1::AbstractGridArray,\n grids::AbstractGridArray...\n) -> Any\n\n\nSame as eachring(grid) but performs a bounds check to assess that all grids according to grids_match (non-parametric grid type, nlat_half and length).\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.eachring-Tuple{AbstractGridArray}","page":"RingGrids","title":"SpeedyWeather.RingGrids.eachring","text":"eachring(grid::AbstractGridArray) -> 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\nAccesses precomputed grid.rings.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.eachring-Tuple{Type{<:AbstractGridArray}, Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.eachring","text":"eachring(\n Grid::Type{<:AbstractGridArray},\n nlat_half::Integer\n) -> Any\n\n\nComputes the ring indices i0:i1 for start and end of every longitudinal point on a given ring j of Grid at resolution nlat_half. Used to loop over rings of a grid. These indices are also precomputed in every grid.rings.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.equal_area_weights-Tuple{Type{<:AbstractGridArray}, Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.equal_area_weights","text":"equal_area_weights(\n Grid::Type{<:AbstractGridArray},\n nlat_half::Integer\n) -> Any\n\n\nThe equal-area weights used for the HEALPix grids (original or OctaHEALPix) of size nlathalf. The weights are of length nlat, i.e. a vector for every latitude ring, pole to pole. `sum(equalareaweights(nlathalf))is always2` as int0^π sin(x) dx = 2 (colatitudes), or equivalently int-pi/2^pi/2 cos(x) dx (latitudes). Integration (and therefore the spectral transform) is not exact with these grids but errors reduce for higher resolution.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.extrema_in-Tuple{AbstractVector, Real, Real}","page":"RingGrids","title":"SpeedyWeather.RingGrids.extrema_in","text":"extrema_in(v::AbstractVector, a::Real, b::Real) -> Any\n\n\nFor every element vᵢ in v does a<=vi<=b hold?\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.full_array_type-Tuple{AbstractGridArray}","page":"RingGrids","title":"SpeedyWeather.RingGrids.full_array_type","text":"full_array_type(grid::AbstractGridArray) -> Any\n\n\nFull grid array type for grid. Always returns the N-dimensional *Array not the two-dimensional (N=1) *Grid. For reduced grids the corresponding full grid that share the same latitudes.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.full_grid_type-Tuple{AbstractGridArray}","page":"RingGrids","title":"SpeedyWeather.RingGrids.full_grid_type","text":"full_grid_type(grid::AbstractGridArray) -> Any\n\n\nFull (horizontal) grid type for grid. Always returns the two-dimensional (N=1) *Grid type. For reduced grids the corresponding full grid that share the same latitudes.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.gaussian_weights-Tuple{Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.gaussian_weights","text":"gaussian_weights(nlat_half::Integer) -> Any\n\n\nThe Gaussian weights for a Gaussian grid (full or octahedral) of size nlathalf. Gaussian weights are of length nlat, i.e. a vector for every latitude ring, pole to pole. `sum(gaussianweights(nlathalf))is always2` as int0^π sin(x) dx = 2 (colatitudes), or equivalently int_-pi/2^pi/2 cos(x) dx (latitudes).\n\nIntegration (and therefore the spectral transform) is exact (only rounding errors) when using Gaussian grids provided that nlat >= 3(T + 1)/2, meaning that a grid resolution of at least 96x48 (nlon x nlat) is sufficient for an exact transform with a T=31 spectral truncation.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_colat-Tuple{Grid} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_colat","text":"get_colat(grid::AbstractGridArray) -> Any\n\n\nColatitudes (radians) for meridional column (full grids only).\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_colatlons-Tuple{Grid} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_colatlons","text":"get_colatlons(grid::AbstractGridArray) -> Tuple{Any, Any}\n\n\nLatitudes (in radians, 0-π) and longitudes (0 - 2π) for every (horizontal) grid point in grid.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_lat-Tuple{Grid} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_lat","text":"get_lat(grid::AbstractGridArray) -> Any\n\n\nLatitude (radians) for each ring in grid, north to south.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_latd-Tuple{Grid} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_latd","text":"get_latd(grid::AbstractGridArray) -> Any\n\n\nLatitude (degrees) for each ring in grid, north to south.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_latdlonds-Tuple{Grid} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_latdlonds","text":"get_latdlonds(grid::AbstractGridArray) -> Tuple{Any, Any}\n\n\nLatitudes (in degrees, -90˚-90˚N) and longitudes (0-360˚E) for every (horizontal) grid point in grid. Ordered 0-360˚E then north to south.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_latlons-Tuple{Grid} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_latlons","text":"get_latlons(grid::AbstractGridArray) -> Tuple{Any, Any}\n\n\nLatitudes (in radians, 0-2π) and longitudes (-π/2 - π/2) for every (horizontal) grid point in grid.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_lon-Tuple{Grid} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_lon","text":"get_lon(grid::AbstractGridArray) -> Any\n\n\nLongitude (radians) for meridional column (full grids only).\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_lond-Tuple{Grid} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_lond","text":"get_lond(grid::AbstractGridArray) -> Any\n\n\nLongitude (degrees) for meridional column (full grids only).\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_nlat-Tuple{Grid} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_nlat","text":"get_nlat(grid::AbstractGridArray) -> Any\n\n\nGet number of latitude rings, pole to pole.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_nlat_half-Tuple{AbstractGridArray}","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_nlat_half","text":"get_nlat_half(grid::AbstractGridArray) -> Any\n\n\nResolution paraemeters nlat_half of a grid. Number of latitude rings on one hemisphere, Equator included.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_nlon_per_ring-Tuple{AbstractGridArray, Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_nlon_per_ring","text":"get_nlon_per_ring(\n grid::AbstractGridArray,\n j::Integer\n) -> Any\n\n\nNumber of longitude points per latitude ring j.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_nlon_per_ring-Tuple{Type{<:HEALPixArray}, Integer, Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_nlon_per_ring","text":"get_nlon_per_ring(\n Grid::Type{<:HEALPixArray},\n nlat_half::Integer,\n j::Integer\n) -> Any\n\n\nNumber of longitude points for ring j on Grid of resolution nlat_half.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_nlons-Tuple{Type{<:AbstractGridArray}, Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_nlons","text":"get_nlons(\n Grid::Type{<:AbstractGridArray},\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 keyword argument both_hemispheres=false only the northern hemisphere (incl Equator) is returned.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_npoints-Tuple{Grid} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_npoints","text":"get_npoints(grid::AbstractGridArray) -> Any\n\n\nTotal number of grid points in all dimensions of grid. Equivalent to length of the underlying data array.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_npoints2D-Tuple{Grid} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_npoints2D","text":"get_npoints2D(grid::AbstractGridArray) -> Any\n\n\nNumber of grid points in the horizontal dimension only, even if grid is N-dimensional.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_vertices-Tuple{Type{<:AbstractGridArray}, Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_vertices","text":"get_vertices(\n Grid::Type{<:AbstractGridArray},\n nlat_half::Integer\n) -> NTuple{4, Any}\n\n\nVertices are defined for every grid point on a ring grid through 4 points: east, south, west, north.\n\n- east: longitude mid-point with the next grid point east\n- south: longitude mid-point between the two closest grid points on one ring to the south\n- west: longitude mid-point with the next grid point west\n- north: longitude mid-point between the two closest grid points on one ring to the north\n\nExample\n\n o ----- n ------ o\n\no --- w --- c --- e --- o\n\n o ----- s ------ o\n\nwith cell center c (the grid point), e, s, w, n the vertices and o the surrounding grid points. Returns 2xnpoints arrays for east, south, west, north each containing the longitude and latitude of the vertices.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_vertices-Tuple{Type{<:SpeedyWeather.RingGrids.AbstractFullGridArray}, Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_vertices","text":"get_vertices(\n Grid::Type{<:SpeedyWeather.RingGrids.AbstractFullGridArray},\n nlat_half::Integer\n) -> NTuple{4, Any}\n\n\nVertices for full grids, other definition than for reduced grids to prevent a diamond shape of the cells. Use default rectangular instead.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.grid_cell_average!-Tuple{AbstractGrid, SpeedyWeather.RingGrids.AbstractFullGrid}","page":"RingGrids","title":"SpeedyWeather.RingGrids.grid_cell_average!","text":"grid_cell_average!(\n output::AbstractGrid,\n input::SpeedyWeather.RingGrids.AbstractFullGrid\n) -> 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{<:AbstractGrid}, Integer, SpeedyWeather.RingGrids.AbstractFullGrid}","page":"RingGrids","title":"SpeedyWeather.RingGrids.grid_cell_average","text":"grid_cell_average(\n Grid::Type{<: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.grids_match-Tuple{AbstractGridArray, AbstractGridArray}","page":"RingGrids","title":"SpeedyWeather.RingGrids.grids_match","text":"grids_match(\n A::AbstractGridArray,\n B::AbstractGridArray;\n horizontal_only,\n vertical_only\n) -> Any\n\n\nTrue if both A and B are of the same nonparametric grid type (e.g. OctahedralGaussianArray, regardless type parameter T or underyling array type ArrayType) and of same resolution (nlat_half) and total grid points (length). Sizes of (4,) and (4,1) would match for example, but (8,1) and (4,2) would not (nlat_half not identical).\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.grids_match-Tuple{AbstractGridArray, Vararg{AbstractGridArray}}","page":"RingGrids","title":"SpeedyWeather.RingGrids.grids_match","text":"grids_match(\n A::AbstractGridArray,\n B::AbstractGridArray...;\n kwargs...\n) -> Any\n\n\nTrue if all grids A, B, C, ... provided as arguments match according to grids_match wrt to A (and therefore all).\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.horizontal_grid_type-Tuple{AbstractGridArray}","page":"RingGrids","title":"SpeedyWeather.RingGrids.horizontal_grid_type","text":"horizontal_grid_type(grid::AbstractGridArray) -> Any\n\n\nThe two-dimensional (N=1) *Grid for grid, which can be an N-dimensional *GridArray.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.isdecreasing-Tuple{AbstractVector}","page":"RingGrids","title":"SpeedyWeather.RingGrids.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":"ringgrids/#SpeedyWeather.RingGrids.isincreasing-Tuple{AbstractVector}","page":"RingGrids","title":"SpeedyWeather.RingGrids.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":"ringgrids/#SpeedyWeather.RingGrids.matrix_size-Tuple{Grid} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.matrix_size","text":"matrix_size(grid::AbstractGridArray) -> Tuple{Int64, Int64}\n\n\nSize of the matrix of the horizontal grid if representable as such (not all grids).\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.nlat_odd-Tuple{AbstractGridArray}","page":"RingGrids","title":"SpeedyWeather.RingGrids.nlat_odd","text":"nlat_odd(grid::AbstractGridArray) -> Any\n\n\nTrue for a grid that has an odd number of latitude rings nlat (both hemispheres).\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.nonparametric_type-Tuple{AbstractGridArray}","page":"RingGrids","title":"SpeedyWeather.RingGrids.nonparametric_type","text":"nonparametric_type(grid::AbstractGridArray) -> Any\n\n\nFor any instance of AbstractGridArray type its n-dimensional type (*Grid{T, N, ...} returns *Array) but without any parameters {T, N, ArrayType}\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.npoints_added_per_ring-Tuple{Type{<:OctahedralGaussianArray}}","page":"RingGrids","title":"SpeedyWeather.RingGrids.npoints_added_per_ring","text":"npoints_added_per_ring(\n _::Type{<:OctahedralGaussianArray}\n) -> Int64\n\n\n[EVEN MORE EXPERIMENTAL] number of longitude points added (removed) for every ring towards the Equator (on the southern hemisphere towards the south pole).\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.npoints_added_per_ring-Tuple{Type{<:OctaminimalGaussianArray}}","page":"RingGrids","title":"SpeedyWeather.RingGrids.npoints_added_per_ring","text":"npoints_added_per_ring(\n _::Type{<:OctaminimalGaussianArray}\n) -> Int64\n\n\n[EVEN MORE EXPERIMENTAL] number of longitude points added (removed) for every ring towards the Equator (on the southern hemisphere towards the south pole).\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.npoints_pole-Tuple{Type{<:OctahedralGaussianArray}}","page":"RingGrids","title":"SpeedyWeather.RingGrids.npoints_pole","text":"npoints_pole(_::Type{<:OctahedralGaussianArray}) -> Int64\n\n\n[EXPERIMENTAL] additional number of longitude points on the first and last ring. Change to 0 to start with 4 points on the first ring.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.npoints_pole-Tuple{Type{<:OctaminimalGaussianArray}}","page":"RingGrids","title":"SpeedyWeather.RingGrids.npoints_pole","text":"npoints_pole(_::Type{<:OctaminimalGaussianArray}) -> Int64\n\n\n[EXPERIMENTAL] additional number of longitude points on the first and last ring. Change to 0 to start with 4 points on the first ring.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.nside_healpix-Tuple{Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.nside_healpix","text":"nside_healpix(nlat_half::Integer) -> Any\n\n\nThe original Nside resolution parameter of the HEALPix grids. The number of grid points on one side of each (square) face. While we use nlat_half across all ring grids, this function translates this to Nside. Even nlat_half only.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.rotate_matrix_indices_180-Tuple{Integer, Integer, Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.rotate_matrix_indices_180","text":"rotate_matrix_indices_180(\n i::Integer,\n j::Integer,\n s::Integer\n) -> Tuple{Any, Any}\n\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":"RingGrids","title":"SpeedyWeather.RingGrids.rotate_matrix_indices_270","text":"rotate_matrix_indices_270(\n i::Integer,\n j::Integer,\n s::Integer\n) -> Tuple{Integer, Any}\n\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":"RingGrids","title":"SpeedyWeather.RingGrids.rotate_matrix_indices_90","text":"rotate_matrix_indices_90(\n i::Integer,\n j::Integer,\n s::Integer\n) -> Tuple{Any, Integer}\n\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.spherical_distance-Tuple{Type{<:SpeedyWeather.RingGrids.AbstractSphericalDistance}, Vararg{Any}}","page":"RingGrids","title":"SpeedyWeather.RingGrids.spherical_distance","text":"spherical_distance(\n Formula::Type{<:SpeedyWeather.RingGrids.AbstractSphericalDistance},\n args...;\n kwargs...\n) -> Any\n\n\nSpherical distance, or great-circle distance, between two points lonlat1 and lonlat2 using the Formula (default Haversine).\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.whichring-Tuple{Integer, Vector{UnitRange{Int64}}}","page":"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 rings describing rind indices as obtained from eachring(::Grid)\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.zonal_mean-Tuple{AbstractGridArray}","page":"RingGrids","title":"SpeedyWeather.RingGrids.zonal_mean","text":"zonal_mean(grid::AbstractGridArray) -> Any\n\n\nZonal mean of grid, i.e. along its latitude rings.\n\n\n\n\n\n","category":"method"},{"location":"structure/#Tree-structure","page":"Tree structure","title":"Tree structure","text":"","category":"section"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"At the top of SpeedyWeather's type tree sits the Simulation, containing variables and model, which in itself contains model components with their own fields and so on. (Note that we are talking about the structure of structs within structs not the type hierarchy as defined by subtyping abstract types.) This can quickly get complicated with a lot of nested structs. The following is to give users a better overview of how simulation, variables and model are structured within SpeedyWeather. Many types in SpeedyWeather have extended Julia's show function to give you an overview of its contents, e.g. a clock::Clock is printed as","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"using SpeedyWeather\nclock = Clock()","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"illustrating the fields within a clock, their types and (unless they are an array) also their values. For structs within structs however, this information, would not be printed by default. You could use Julia's autocomplete like clock. by hitting tab after the . to inspect the fields of an instance but that would require you to manually go down every branch of that tree. To better visualise this, we have defined a tree(S) function for any instance S defined in SpeedyWeather which will print every field, and also its containing fields if they are also defined within SpeedyWeather. The \"if defined in SpeedyWeather\" is important because otherwise the tree would also show you the contents of a complex number or other types defined in Julia Base itself that we aren't interested in here. But let's start at the top.","category":"page"},{"location":"structure/#Simulation","page":"Tree structure","title":"Simulation","text":"","category":"section"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"When creating a Simulation, its fields are","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"spectral_grid = SpectralGrid(nlayers = 1)\nmodel = BarotropicModel(spectral_grid)\nsimulation = initialize!(model)","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"the prognostic_variables, the diagnostic_variables and the model (that we just initialized). We could now do tree(simulation) but that gets very lengthy and so will split things into tree(simulation.prognostic_variables), tree(simulation.diagnostic_variables) and tree(simulation.model) for more digestible chunks. You can also provide the with_types=true keyword to get also the types of these fields printed, but we'll skip that here.","category":"page"},{"location":"structure/#Prognostic-variables","page":"Tree structure","title":"Prognostic variables","text":"","category":"section"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"The prognostic variables struct is parametric on the model type, model_type(model) (which strips away its parameters), but this is only to dispatch over it. The fields are for all models the same, just the barotropic model would not use temperature for example (but you could use nevertheless). ","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"tree(simulation.prognostic_variables)","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"The prognostic variable struct can be mutated (e.g. to set new initial conditions) with the SpeedyWeather.set! function. ","category":"page"},{"location":"structure/#Diagnostic-variables","page":"Tree structure","title":"Diagnostic variables","text":"","category":"section"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"Similar for the diagnostic variables, regardless the model type, they contain the same fields but for the 2D models many will not be used for example.","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"tree(simulation.diagnostic_variables)","category":"page"},{"location":"structure/#BarotropicModel","page":"Tree structure","title":"BarotropicModel","text":"","category":"section"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"The BarotropicModel is the simplest model we have, which will not have many of the model components that are needed to define the primitive equations for example. Note that forcing or drag aren't further branched which is because the default BarotropicModel has NoForcing and NoDrag which don't have any fields. If you create a model with non-default conponents they will show up here. tree dynamicallt inspects the current contents of a (mutable) struct and that tree may look different depending on what model you have constructed!","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"model = BarotropicModel(spectral_grid)\ntree(model)","category":"page"},{"location":"structure/#ShallowWaterModel","page":"Tree structure","title":"ShallowWaterModel","text":"","category":"section"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"The ShallowWaterModel is similar to the BarotropicModel, but it contains for example orography, that the BarotropicModel doesn't have.","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"model = ShallowWaterModel(spectral_grid)\ntree(model)","category":"page"},{"location":"structure/#PrimitiveDryModel","page":"Tree structure","title":"PrimitiveDryModel","text":"","category":"section"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"The PrimitiveDryModel is a big jump in complexity compared to the 2D models, but because it doesn't contain humidity, several model components like evaporation aren't needed.","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"spectral_grid = SpectralGrid()\nmodel = PrimitiveDryModel(spectral_grid)\ntree(model)","category":"page"},{"location":"structure/#PrimitiveWetModel","page":"Tree structure","title":"PrimitiveWetModel","text":"","category":"section"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"The PrimitiveWetModel is the most complex model we currently have, hence its field tree is the longest, defining many components for the physics parameterizations.","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"model = PrimitiveWetModel(spectral_grid)\ntree(model)","category":"page"},{"location":"structure/#Size-of-a-Simulation","page":"Tree structure","title":"Size of a Simulation","text":"","category":"section"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"The tree function also allows for the with_size::Bool keyword (default false), which will also print the size of the respective branches to give you an idea of how much memory a SpeedyWeather simulation uses.","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"tree(simulation, max_level=1, with_size=true)","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"And with max_level you can truncate the tree to go down at most that many levels. 1MB is a typical size for a one-level T31 resolution simulation. In comparison, a higher resolution PrimitiveWetModel would use","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"spectral_grid = SpectralGrid(trunc=127, nlayers=8)\nmodel = PrimitiveWetModel(spectral_grid)\nsimulation = initialize!(model)\ntree(simulation, max_level=1, with_size=true)","category":"page"},{"location":"examples_3D/#Examples-3D","page":"Examples 3D","title":"Examples 3D","text":"","category":"section"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"The following showcases several examples of SpeedyWeather.jl simulating the Primitive equations with and without humidity and with and without physical parameterizations.","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"See also Examples 2D for examples with the Barotropic vorticity equation and the shallow water model.","category":"page"},{"location":"examples_3D/#Jablonowski-Williamson-baroclinic-wave","page":"Examples 3D","title":"Jablonowski-Williamson baroclinic wave","text":"","category":"section"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=31, nlayers=8, Grid=FullGaussianGrid, dealiasing=3)\n\norography = ZonalRidge(spectral_grid)\ninitial_conditions = InitialConditions(\n vordiv = ZonalWind(),\n temp = JablonowskiTemperature(),\n pres = ZeroInitially())\n\nmodel = PrimitiveDryModel(spectral_grid; orography, initial_conditions, physics=false)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(9))\nnothing # hide","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","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 ZonalWind for vorticity and divergence (curl and divergence of u v), JablonowskiTemperature for temperature and ZeroInitially for pressure. 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":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"using CairoMakie\n\nvor = simulation.diagnostic_variables.grid.vor_grid[:, end]\nheatmap(vor, title=\"Surface relative vorticity\")\nsave(\"jablonowski.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"(Image: Jablonowski pyplot)","category":"page"},{"location":"examples_3D/#Held-Suarez-forcing","page":"Examples 3D","title":"Held-Suarez forcing","text":"","category":"section"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=31, nlayers=8)\n\n# construct model with only Held-Suarez forcing, no other physics\nmodel = PrimitiveDryModel(\n spectral_grid,\n\n # Held-Suarez forcing and drag\n temperature_relaxation = HeldSuarez(spectral_grid),\n boundary_layer_drag = LinearDrag(spectral_grid),\n\n # switch off other physics\n convection = NoConvection(),\n shortwave_radiation = NoShortwave(),\n longwave_radiation = NoLongwave(),\n vertical_diffusion = NoVerticalDiffusion(),\n\n # switch off surface fluxes (makes ocean/land/land-sea mask redundant)\n surface_wind = NoSurfaceWind(),\n surface_heat_flux = NoSurfaceHeatFlux(),\n\n # use Earth's orography\n orography = EarthOrography(spectral_grid)\n)\n\nsimulation = initialize!(model)\nrun!(simulation, period=Day(20))\nnothing # hide","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"The code above defines the Held-Suarez forcing [HS94] in terms of temperature relaxation and a linear drag term that is applied near the planetary boundary but switches off all other physics in the primitive equation model without humidity. Switching off the surface wind would also automatically turn off the surface evaporation (not relevant in the primitive dry model) and sensible heat flux as that one is proportional to the surface wind (which is zero with NoSurfaceWind). But to also avoid the calculation being run at all we use NoSurfaceHeatFlux() for the model constructor. Many of the NoSomething model components do not require the spectral grid to be passed on, but as a convention we allow every model component to have it for construction even if not required.","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"Visualising surface temperature with","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"using CairoMakie\n\ntemp = simulation.diagnostic_variables.grid.temp_grid[:, end]\nheatmap(temp, title=\"Surface temperature [K]\", colormap=:thermal)\n\nsave(\"heldsuarez.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"(Image: Held-Suarez)","category":"page"},{"location":"examples_3D/#Aquaplanet","page":"Examples 3D","title":"Aquaplanet","text":"","category":"section"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"using SpeedyWeather\n\n# components\nspectral_grid = SpectralGrid(trunc=31, nlayers=8)\nocean = AquaPlanet(spectral_grid, temp_equator=302, temp_poles=273)\nland_sea_mask = AquaPlanetMask(spectral_grid)\norography = NoOrography(spectral_grid)\n\n# create model, initialize, run\nmodel = PrimitiveWetModel(spectral_grid; ocean, land_sea_mask, orography)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(20))\nnothing # hide","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"Here we have defined an aquaplanet simulation by","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"creating an ocean::AquaPlanet. This will use constant sea surface temperatures that only vary with latitude.\ncreating a land_sea_mask::AquaPlanetMask this will use a land-sea mask with false=ocean everywhere.\ncreating an orography::NoOrography which will have no orography and zero surface geopotential.","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"All passed on to the model constructor for a PrimitiveWetModel, we have now a model with humidity and physics parameterization as they are defined by default (typing model will give you an overview of its components). We could have change the model.land and model.vegetation components too, but given the land-sea masks masks those contributions to the surface fluxes anyway, this is not necessary. Note that neither sea surface temperature, land-sea mask or orography have to agree. It is possible to have an ocean on top of a mountain. For an ocean grid-cell that is (partially) masked by the land-sea mask, its value will be (fractionally) ignored in the calculation of surface fluxes (potentially leading to a zero flux depending on land surface temperatures).","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"Now with the following we visualize the surface humidity after the 50 days of simulation. We use 50 days as without mountains it takes longer for the initial conditions to become unstable. The surface humidity shows small-scale patches in the tropics, which is a result of the convection scheme, causing updrafts and downdrafts in both humidity and temperature.","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"using CairoMakie\n\nhumid = simulation.diagnostic_variables.grid.humid_grid[:, end]\nheatmap(humid, title=\"Surface specific humidity [kg/kg]\", colormap=:oslo)\n\nsave(\"aquaplanet.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"(Image: Aquaplanet)","category":"page"},{"location":"examples_3D/#Aquaplanet-without-(deep)-convection","page":"Examples 3D","title":"Aquaplanet without (deep) convection","text":"","category":"section"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"Now we want to compare the previous simulation to a simulation without deep convection, called DryBettsMiller, because it is the Betts-Miller convection but with humidity set to zero in which case the convection is always non-precipitating shallow (because the missing latent heat release from condensation makes it shallower) convection. In fact, this convection is the default when using the PrimitiveDryModel. Instead of redefining the Aquaplanet setup again, we simply reuse these components spectral_grid, ocean, land_sea_mask and orography (because spectral_grid hasn't changed this is possible).","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"# Execute the code from Aquaplanet above first!\nconvection = DryBettsMiller(spectral_grid, time_scale=Hour(4))\n\n# reuse other model components from before\nmodel = PrimitiveWetModel(spectral_grid; ocean, land_sea_mask, orography, convection)\n\nsimulation = initialize!(model)\nrun!(simulation, period=Day(20))\n\nhumid = simulation.diagnostic_variables.grid.humid_grid[:, end]\nheatmap(humid, title=\"No deep convection: Surface specific humidity [kg/kg]\", colormap=:oslo)\nsave(\"aquaplanet_nodeepconvection.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"But we also want to compare this to a setup where convection is completely disabled, i.e. convection = NoConvection() (many of the No model components don't require the spectral_grid to be passed on, but some do!)","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"# Execute the code from Aquaplanet above first!\nconvection = NoConvection(spectral_grid)\n\n# reuse other model components from before\nmodel = PrimitiveWetModel(spectral_grid; ocean, land_sea_mask, orography, convection)\n\nsimulation = initialize!(model)\nrun!(simulation, period=Day(20))\n\nhumid = simulation.diagnostic_variables.grid.humid_grid[:, end]\nheatmap(humid, title=\"No convection: Surface specific humidity [kg/kg]\", colormap=:oslo)\nsave(\"aquaplanet_noconvection.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"And the comparison looks like","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"(Image: Aquaplanet, no deep convection) (Image: Aquaplanet, no convection)","category":"page"},{"location":"examples_3D/#Large-scale-vs-convective-precipitation","page":"Examples 3D","title":"Large-scale vs convective precipitation","text":"","category":"section"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"using SpeedyWeather\n\n# components\nspectral_grid = SpectralGrid(trunc=31, nlayers=8)\nlarge_scale_condensation = ImplicitCondensation(spectral_grid)\nconvection = SimplifiedBettsMiller(spectral_grid)\n\n# create model, initialize, run\nmodel = PrimitiveWetModel(spectral_grid; large_scale_condensation, convection)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(10))\nnothing # hide","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"We run the default PrimitiveWetModel with ImplicitCondensation as large-scale condensation (see Implicit large-scale condensation) and the SimplifiedBettsMiller for convection (see Simplified Betts-Miller). These schemes have some additional parameters, we leave them as default for now, but you could do ImplicitCondensation(spectral_grid, relative_humidity_threshold = 0.8) to let it rain at 80% instead of 100% relative humidity. We now want to analyse the precipitation that comes from these parameterizations","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"using CairoMakie\n\n(; precip_large_scale, precip_convection) = simulation.diagnostic_variables.physics\nm2mm = 1000 # convert from [m] to [mm]\nheatmap(m2mm*precip_large_scale, title=\"Large-scale precipiation [mm]: Accumulated over 10 days\", colormap=:dense)\nsave(\"large-scale_precipitation_acc.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"(Image: Large-scale precipitation)","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"Precipitation (both large-scale and convective) are written into the simulation.diagnostic_variables.physics which, however, accumulate all precipitation during simulation. In the NetCDF output, precipitation rate (in mm/hr) is calculated from accumulated precipitation as a post-processing step. More interactively, you can also reset these accumulators and integrate for another 6 hours to get the precipitation only in that period.","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"# reset accumulators and simulate 6 hours\nsimulation.diagnostic_variables.physics.precip_large_scale .= 0\nsimulation.diagnostic_variables.physics.precip_convection .= 0\nrun!(simulation, period=Hour(6))\n\n# visualise, precip_* arrays are flat copies, no need to read them out again!\nm2mm_hr = (1000*Hour(1)/Hour(6)) # convert from [m] to [mm/hr]\nheatmap(m2mm_hr*precip_large_scale, title=\"Large-scale precipiation [mm/hr]\", colormap=:dense)\nsave(\"large-scale_precipitation.png\", ans) # hide\nheatmap(m2mm_hr*precip_convection, title=\"Convective precipiation [mm/hr]\", colormap=:dense)\nsave(\"convective_precipitation.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"(Image: Large-scale precipitation) (Image: Convective precipitation)","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"As the precipitation fields are accumulated meters over the integration period we divide by 6 hours to get a precipitation rate ms but then multiply with 1 hour and 1000 to get the typical precipitation unit of mmhr.","category":"page"},{"location":"examples_3D/#References","page":"Examples 3D","title":"References","text":"","category":"section"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","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. DOI:10.1256/qj.06.12","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"[HS94]: Held, I. M. & Suarez, M. J. A Proposal for the Intercomparison of the Dynamical Cores of Atmospheric General Circulation Models. Bulletin of the American Meteorological Society 75, 1825-1830 (1994). DOI:10.1175/1520-0477(1994)075<1825:APFTIO>2.0.CO;2","category":"page"},{"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: Convection, clouds, precipitation, radiation, 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":"#Vision","page":"Home","title":"Vision","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Why another model? You may ask. We believe that most currently available are stiff, difficult to use and extend, and therefore slow down research whereas a modern code in a modern language wouldn't have to. We decided to use Julia because it combines the best of Fortran and Python: Within a single language we can interactively run SpeedyWeather but also extend it, inspect its components, evaluate individual terms of the equations, and analyse and visualise output on the fly.","category":"page"},{"location":"","page":"Home","title":"Home","text":"We do not aim to make SpeedyWeather an atmospheric model similar to the production-ready models used in weather forecasting, at least not at the cost of our current level of interactivity and ease of use or extensibility. If someone wants to implement a cloud parameterization that is very complicated and expensive to run then they are more than encouraged to do so, but it will probably live in its own repository and we are happy to provide a general interface to do so. But SpeedyWeather's defaults should be balanced: Physically accurate yet general; as independently as possible from other components and parameter choices; not too complicated to implement and understand; and computationally cheap. Finding a good balance is difficult but we try our best. ","category":"page"},{"location":"#Developers-and-contributing","page":"Home","title":"Developers and contributing","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":"(Apologies if you've recently started contributing but this isn't reflected here yet, create a pull request!) Any contributions are always welcome!","category":"page"},{"location":"","page":"Home","title":"Home","text":"Open-source lives from large teams of (even occasional) contributors. If you are interested to fix something, implement something, or just use it and provide feedback you are always welcome. We are more than happy to guide you, especially when you don't know where to start. We can point you to the respective code, highlight how everything is connected and tell you about dos and don'ts. Just express your interest to contribute and we'll be happy to have you.","category":"page"},{"location":"#Citing","page":"Home","title":"Citing","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"If you use SpeedyWeather.jl in research, teaching, or other activities, we would be grateful if you could mention SpeedyWeather.jl and cite our paper in JOSS:","category":"page"},{"location":"","page":"Home","title":"Home","text":"Klöwer et al., (2024). SpeedyWeather.jl: Reinventing atmospheric general circulation models towards interactivity and extensibility. Journal of Open Source Software, 9(98), 6323, doi:10.21105/joss.06323.","category":"page"},{"location":"","page":"Home","title":"Home","text":"The bibtex entry for the paper is:","category":"page"},{"location":"","page":"Home","title":"Home","text":"@article{SpeedyWeatherJOSS,\n doi = {10.21105/joss.06323},\n url = {https://doi.org/10.21105/joss.06323},\n year = {2024},\n publisher = {The Open Journal},\n volume = {9},\n number = {98},\n pages = {6323},\n author = {Milan Klöwer and Maximilian Gelbrecht and Daisuke Hotta and Justin Willmert and Simone Silvestri and Gregory L. Wagner and Alistair White and Sam Hatfield and Tom Kimpson and Navid C. Constantinou and Chris Hill},\n title = {{SpeedyWeather.jl: Reinventing atmospheric general circulation models towards interactivity and extensibility}},\n journal = {Journal of Open Source Software}\n}","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. From 2022-2024 this project was also funded by the National Science Foundation NSF. Since 2024, the main funding is from Schmidt Sciences LLC through MK's Eric & Wendy Schmidt AI in Science Fellowship.","category":"page"},{"location":"extensions/#Extending-SpeedyWeather","page":"Extensions","title":"Extending SpeedyWeather","text":"","category":"section"},{"location":"extensions/","page":"Extensions","title":"Extensions","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":"extensions/","page":"Extensions","title":"Extensions","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":"extensions/#logic","page":"Extensions","title":"SpeedyWeather's modular logic","text":"","category":"section"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"Almost every component in SpeedyWeather is implemented in three steps:","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"Define a new type,\ndefine its initialization,\nextend a function that defines what it does.","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","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. For the latter, see Parameterizations 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":"extensions/","page":"Extensions","title":"Extensions","text":"To define a new forcing type, at the most basic level you would do","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"using SpeedyWeather\n\nstruct MyForcing{NF} <: SpeedyWeather.AbstractForcing\n # define some parameters and work arrays here\n a::NF\n v::Vector{NF}\nend","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","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 (see Abstract model components) 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 @kwdef and can also be parametric with respect to the number format NF 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 (ideally not work arrays, see discussion in Use ColumnVariables work arrays). 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 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 and drag.","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"To define the new type's initialization, at the most basic level you need to extend the initialize! function for this new type. A dummy example:","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"function initialize!(forcing::MyForcing, model::AbstractModel)\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":"extensions/","page":"Extensions","title":"Extensions","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":"extensions/","page":"Extensions","title":"Extensions","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":"extensions/","page":"Extensions","title":"Extensions","text":"function forcing!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n forcing::MyForcing,\n model::AbstractModel,\n lf::Integer,\n)\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 = forcing.a\n diagn.tendencies.v_tend_grid = forcing.a\n diagn.tendencies.vor_tend = forcing.a\nend","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"DiagnosticVariables 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 can make use of anything else in model, but often we unpack the model in a function barrier (which can help with type inference and therefore performance). 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":"extensions/#Scope-of-variables","page":"Extensions","title":"Scope of variables","text":"","category":"section"},{"location":"extensions/","page":"Extensions","title":"Extensions","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":"extensions/","page":"Extensions","title":"Extensions","text":"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":"extensions/","page":"Extensions","title":"Extensions","text":"function SpeedyWeather.initialize!(forcing::MyForcing, model::SpeedyWeather.AbstractModel)\n # how to initialize it\nend","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"And similar for SpeedyWeather.forcing!.","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","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: AbstractModel to bring a given variable into global scope which removes the necessity to write SpeedyWeather.AbstractModel.","category":"page"},{"location":"extensions/#Abstract-model-components","page":"Extensions","title":"Abstract model components","text":"","category":"section"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"You may wonder which abstract model components there are, you can always check this with","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"using InteractiveUtils # hide\nsubtypes(SpeedyWeather.AbstractModelComponent)","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"we illustrated the modular logic here using AbstractForcing and AbstractDrag is very similar. However, other model components also largely follow SpeedyWeather's modular logic as for example outlined in Defining a callback or Defining a new orography type. If you do not find much documentation about a new custom type where you would like extend SpeedyWeather's functionality it is probably because we have not experimented much with this either. But that does not mean it is not possible. Just reach out by creating an issue in this case.","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"Similarly, AbstractParameterization has several subtypes that define conceptual classes of parameterizations, namely","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"subtypes(SpeedyWeather.AbstractParameterization)","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"but these are discussed in more detail in Parameterizations. For a more concrete example of how to define a new forcing for the 2D models, see Custom forcing and drag.","category":"page"}] +[{"location":"land_sea_mask/#The-land-sea-mask","page":"Land-Sea Mask","title":"The land-sea mask","text":"","category":"section"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"The following describes how a custom land-sea mask can be defined. SpeedyWeather uses a fractional land-sea mask, i.e. for every grid-point","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"1 indicates land\n0 indicates ocean\na value in between indicates a grid-cell partially covered by ocean and land","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"Setting the land-sea mask to ocean therefore will disable any fluxes that may come from land, and vice versa. However, with an ocean-everywhere land-sea mask you must also define sea surface temperatures everywhere, otherwise the fluxes in those regions will be zero.","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"For more details, see Surface fluxes and the Land-sea mask section therein.","category":"page"},{"location":"land_sea_mask/#Manual-land-sea-mask","page":"Land-Sea Mask","title":"Manual land-sea mask","text":"","category":"section"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"You can create the default land-sea mask as follows","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=31, nlayers=8)\nland_sea_mask = LandSeaMask(spectral_grid)","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"which will automatically interpolate the land-sea mask onto grid and resolution as defined in spectral_grid at initialization. The actual mask is in land_sea_mask.mask and you can visualise it with","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"model = PrimitiveWetModel(spectral_grid; land_sea_mask)\nsimulation = initialize!(model) # triggers also initialization of model.land_sea_mask\n\nusing CairoMakie\nheatmap(land_sea_mask.mask, title=\"Land-sea mask at T31 resolution\")\nsave(\"land-sea_mask.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"(Image: Land-sea mask)","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"Now after initialization (otherwise you reinitialize the mask, overwriting your changes) you could manually change the land-sea mask with the set! function which can take scalars as global constants or functions of two arguments longitude lambda and varphi. You can use an anonymous function (λ, φ) -> ... but you do not have to, defining function f(λ, φ) and then using land_sea_mask = f works too. ","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"set!(model, land_sea_mask=0) # aqua planet\nset!(model, land_sea_mask=1) # rocky planet\nset!(model, land_sea_mask=(λ, φ) -> rand()-1) # random small islands\n\n# snowball planet with ocean in the tropics between 10˚S and 10˚N\nset!(model, land_sea_mask=(λ, φ) -> abs(φ) < 10 ? 0 : 1)\n\n# flood the northern hemisphere only, values are automatically clamped into [0, 1]\ninitialize!(model.land_sea_mask, model) # back to Earth's mask\nset!(model, land_sea_mask=(λ, φ) -> φ > 0 ? -1 : 0, add=true)\n\n# visualise\nheatmap(land_sea_mask.mask, title=\"Land-sea mask with Northern Hemisphere ocean\")\nsave(\"nh_ocean.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"(Image: NH ocean)","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"And now you can run the simulation as usual with run!(simulation).","category":"page"},{"location":"land_sea_mask/#Earth's-land-sea-mask","page":"Land-Sea Mask","title":"Earth's land-sea mask","text":"","category":"section"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"The Earth's LandSeaMask has itself the option to load another land-sea mask from file, but you also have to specify the grid that mask from files comes on. It will then attempt to read it via NCDatasets and interpolate onto the model grid.","category":"page"},{"location":"land_sea_mask/#AquaPlanetMask","page":"Land-Sea Mask","title":"AquaPlanetMask","text":"","category":"section"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"Predefined is also the AquaPlanetMask which can be created as","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"land_sea_mask = AquaPlanetMask(spectral_grid)","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"and is equivalent to using Earth's LandSeaMask but setting the entire mask to zero afterwards land_sea_mask.mask .= 0.","category":"page"},{"location":"land_sea_mask/#Custom-land-sea-mask","page":"Land-Sea Mask","title":"Custom land-sea mask","text":"","category":"section"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"Every (custom) land-sea mask has to be a subtype of AbstractLandSeaMask. A custom land-sea mask has to be defined as a new type (struct or mutable struct)","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"CustomMask{NF<:AbstractFloat, Grid<:AbstractGrid{NF}} <: AbstractLandSeaMask{NF, Grid}","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"and needs to have at least a field called mask::Grid that uses a Grid as defined by the spectral grid object, so of correct size and with the number format NF. All AbstractLandSeaMask have a convenient generator function to be used like mask = CustomMask(spectral_grid, option=argument), but you may add your own or customize by defining CustomMask(args...) which should return an instance of type CustomMask{NF, Grid} with parameters matching the spectral grid. Then the initialize function has to be extended for that new mask","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"initialize!(mask::CustomMask, model::PrimitiveEquation)","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"which generally is used to tweak the mask.mask grid as you like, using any other options you have included in CustomMask as fields or anything else (preferably read-only, because this is only to initialize the land-sea mask, nothing else) from model. You can for example read something from file, set some values manually, or use coordinates from model.geometry.","category":"page"},{"location":"land_sea_mask/#Time-dependent-land-sea-mask","page":"Land-Sea Mask","title":"Time-dependent land-sea mask","text":"","category":"section"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"It is possible to define an intrusive callback to change the land-sea mask during integration. The grid in model.land_sea_mask.mask is mutable, meaning you can change the values of grid points in-place but not replace the entire mask or change its size. If that mask is changed, this will be reflected in all relevant model components. For example, we can define a callback that floods the entire planet at the beginning of the 21st century as","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"Base.@kwdef struct MilleniumFlood <: SpeedyWeather.AbstractCallback\n schedule::Schedule = Schedule(DateTime(2000,1,1))\nend\n\n# initialize the schedule\nfunction SpeedyWeather.initialize!(\n callback::MilleniumFlood,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel,\n)\n initialize!(callback.schedule, progn.clock)\nend\n\nfunction SpeedyWeather.callback!(\n callback::MilleniumFlood,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel,\n)\n # escape immediately if not scheduled yet\n isscheduled(callback.schedule, progn.clock) || return nothing\n\n # otherwise set the entire land-sea mask to ocean\n model.land_sea_mask.mask .= 0\n @info \"Everything flooded on $(progn.clock.time)\"\nend\n\n# nothing needs to be done after simulation is finished\nSpeedyWeather.finalize!(::MilleniumFlood, args...) = nothing","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"Note that the flooding will take place only at the start of the 21st century, last indefinitely, but not if the model integration period does not cover that exact event, see Schedules. Initializing a model a few days earlier would then have this MilleniumFlood take place","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"land_sea_mask = LandSeaMask(spectral_grid) # start with Earth's land-sea mask\nmodel = PrimitiveWetModel(spectral_grid; land_sea_mask)\nadd!(model, MilleniumFlood()) # or MilleniumFlood(::DateTime) for any non-default date\n\nsimulation = initialize!(model, time=DateTime(1999,12,29))\nrun!(simulation, period=Day(5))\nheatmap(model.land_sea_mask.mask, title=\"Land-sea mask after MilleniumFlood callback\")\nsave(\"land-sea_mask2.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"(Image: Land-sea mask2)","category":"page"},{"location":"land_sea_mask/","page":"Land-Sea Mask","title":"Land-Sea Mask","text":"And the land-sea mask has successfully been set to ocean everywhere at the start of the 21st century. Note that while we added an @info line into the callback! function, this is here not printed because of how the Documenter works. If you execute this in the REPL you'll see it.","category":"page"},{"location":"surface_fluxes/#Surface-fluxes","page":"Surface fluxes","title":"Surface fluxes","text":"","category":"section"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"The surfaces fluxes in SpeedyWeather represent the exchange of momentum, heat, and moisture between ocean and land as surface into the lowermost atmospheric layer. Surface fluxes of momentum represent a drag that the boundary layer wind experiences due to friction over more or less rough ground on land or over sea. Surface fluxes of heat represent a sensible heat flux from a warmer or colder ocean or land into or out of the surface layer of the atmosphere. Surface fluxes of moisture represent evaporation of sea water into undersaturated surface air masses or, similarly, evaporation from land with a given soil moisture and vegetation's evapotranspiration.","category":"page"},{"location":"surface_fluxes/#Surface-flux-implementations","page":"Surface fluxes","title":"Surface flux implementations","text":"","category":"section"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"Currently implemented surface fluxes of momentum are","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"using InteractiveUtils # hide\nusing SpeedyWeather\nsubtypes(SpeedyWeather.AbstractSurfaceWind)","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"note: Interdependence of surface flux computations\nSurfaceWind computes the surface fluxes of momentum but also the computation of the surface wind (which by default includes wind gusts) meaning that NoSurfaceWind will also effectively disable other surface fluxes unless custom surface fluxes have been implemented that do not rely on column.surface_wind_speed.","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"with more explanation below. The surface heat fluxes currently implemented are","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"subtypes(SpeedyWeather.AbstractSurfaceHeatFlux)","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"and the surface moisture fluxes, i.e. evaporation (this does not include Convection or Large-scale condensation which currently immediately removes humidity instead of fluxing it out at the bottom) implemented are","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"subtypes(SpeedyWeather.AbstractSurfaceEvaporation)","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"The calculation of thermodynamic quantities at the surface (air density, temperature, humidity) are handled by","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"subtypes(SpeedyWeather.AbstractSurfaceThermodynamics)","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"and the computation of drag coefficients (which is used by default for the surface fluxes above) is handled through the model.boundary_layer where currently implemented are","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"subtypes(SpeedyWeather.AbstractBoundaryLayer)","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"Note that LinearDrag is the linear drag following Held and Suarez (see Held-Suarez forcing) which does not compute a drag coefficient and therefore by default effectively disables other surface fluxes (as the Held and Suarez forcing and drag is supposed to be used instead of physical parameterizations).","category":"page"},{"location":"surface_fluxes/#Fluxes-to-tendencies","page":"Surface fluxes","title":"Fluxes to tendencies","text":"","category":"section"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"In SpeedyWeather.jl, parameterizations can be defined either in terms of tendencies for a given layer or as fluxes between two layers including the surface flux and a top-of-the-atmosphere flux. The upward flux F^uparrow out of layer k+1 into layer k (vertical indexing k increases downwards) is F^uparrow_k+h (h = frac12 for half, as the flux sits on the cell face, the half-layer in between k and k+1) and similarly F^downarrow_k+h is the downward flux. For clarity we may define fluxes as either upward or downward depending on the process although an upward flux can always be regarded as a negative downward flux. The absorbed flux in layer k is","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"Delta F_k = (F^uparrow_k+h - F^uparrow_k-h) + (F^downarrow_k-h - F^downarrow_k+h)","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"A quick overview of the units","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"Quantity Variable Unit Flux unit\nMomentum Velocity u v ms Pa = kgms^2\nHeat Temperature T ms Wm^2 = kgs^3\nMoisture Specific humidity q kgkg kgm^2s","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"The time-stepping in SpeedyWeather.jl (see Time integration) eventually requires tendencies which are calculated from the absorbed fluxes of momentum u or v and moisture q as","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"fracpartial u_kpartial t = fracg Delta F_kDelta p_k","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"with gravity g and layer-thickness Delta p_k (see Sigma coordinates) so that the right-hand side divides the absorbed flux by the mass of layer k (per unit area). Tendencies for v q equivalently with their respective absorbed fluxes.","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"The temperature tendency is calculated from the absorbed heat flux as","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"fracpartial T_kpartial t = fracg Delta F_kc_p Delta p_k","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"with heat capacity of air at constant pressure c_p with a typical value of 1004JkgK. Because we define the heat flux as having units of Wm^2 the conversion includes the division by the heat capacity to convert to a rate of temperature change.","category":"page"},{"location":"surface_fluxes/#Bulk-Richardson-based-drag-coefficient","page":"Surface fluxes","title":"Bulk Richardson-based drag coefficient","text":"","category":"section"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"All surface fluxes depend on a dimensionless drag coefficient C which we calculate as a function of the bulk Richardson number Ri following Frierson, et al. 2006 [Frierson2006] with some simplification as outlined below. We use the same drag coefficient for momentum, heat and moisture fluxes. The bulk Richardson number at the lowermost model layer k = N of height z_N is","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"Ri_N = fracgz_N left( Theta_v(z_N) - Theta_v(0) right)v(z_N)^2 Theta_v(0)","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"with gz_N = Phi_N the Geopotential at z = z_N, Theta = c_pT_v + gz the virtual dry static energy and T_v the Virtual temperature. Then the drag coefficient C follows as","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"C = begincases\n kappa^2 left( log(fracz_Nz_0) right)^-2 quad textfor quad Ri_N leq 0\n kappa^2 left( log(fracz_Nz_0) right)^-2 left(1 - fracRi_NRi_cright)^2 quad textfor quad 0 Ri_N Ri_c \n 0 quad textfor quad Ri_N geq Ri_c \n endcases","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"with kappa = 04 the von Kármán constant, z_0 = 321 cdot 10^-5 the roughness length. There is a maximum drag C for negative bulk Richardson numbers Ri_N but the drag becomes 0 for bulk Richardson numbers being larger than a critical Ri_c = 1 with a smooth transition in between. The height of the N-th model layer is z_N = tfracPhi_N - Phi_0g with the geopotential","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"Phi_N = Phi_0 + T_N R_d ( log p_N+h - log p_N)","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"which depends on the temperature T_N of that layer. To simplify this calculation and avoid the logarithm we use a constant Z approx z_N from a reference temperature T_ref instead of T_N in the calculation of log(z_Nz_0). While z_N depends on the vertical resolution (Delta p_N of the lowermost layer) it is proportional to that layer's temperature which in Kelvin does not change much in space and in time, especially with the logarithm applied. For T_ref = 200K with specific gas constant R_d = 287 JkgK on sigma level sigma_N = 095 we have log(z_Nz_0) approx 161 for T_ref = 300K this increases to log(z_Nz_0) approx 165 a 2.5% increase which we are happy to approximate. Note that we do not apply this approximation within the bulk Richardson number Ri_N. So we calculate once a typical height of the lowermost layer Z = T_refR_d log(1sigma_N)g^-1 for the given parameter choices and then define a maximum drag constant","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"C_max = left(frackappalog(fracZz_0) right)^2","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"to simplify the drag coefficient calculation to","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"C = C_max left(1 - fracRi_N^*Ri_cright)^2","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"with Ri_N^* = max(0 min(Ri_N Ri_c)) the clamped Ri_N which is at least 0 and at most Ri_c.","category":"page"},{"location":"surface_fluxes/#Surface-momentum-fluxes","page":"Surface fluxes","title":"Surface momentum fluxes","text":"","category":"section"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"The surface momentum flux is calculated from the surface wind velocities","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"u_s = f_w u_N quad v_s = f_w v_N","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"meaning it is scaled down by f_w = 095 (Fortran SPEEDY default, [SPEEDY]) from the lowermost layer wind velocities u_N v_N. A wind speed scale accounting for gustiness with V_gust = 5ms (Fortran SPEEDY default, [SPEEDY]) is then defined as","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"V_0 = sqrtu_s^2 + v_s^2 + V_gust^2","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"such that for low wind speeds the fluxes are somewhat higher because of unresolved winds on smaller time and length scales. The momentum flux is then","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"beginaligned\nF_u^uparrow = - rho_s C V_0 u_s \nF_v^uparrow = - rho_s C V_0 v_s\nendaligned","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"with rho_s = fracp_sR_d T_N the surface air density calculated from surface pressure p_s and lowermost layer temperature T_N. Better would be to extrapolate T_N to T_s a surface air temperature assuming adiabatic descent but that is currently not implemented.","category":"page"},{"location":"surface_fluxes/#Surface-heat-fluxes","page":"Surface fluxes","title":"Surface heat fluxes","text":"","category":"section"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"The surface heat flux is proportional to the difference of the surface air temperature T_s and the land or ocean skin temperature T_skin. We currently just approximate as T_N the lowermost layer temperature although an adiabatic descent from pressure p_N to surface pressure p_s would be more accurate. We also use land and sea surface temperature to approximate T_skin although future improvements should account for faster radiative effects on T_skin compared to sea and land surface temperatures determined by a higher heat capacity of the relevant land surface layer or the mixed layer in the ocean. We then compute","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"F_T^uparrow = rho_s C V_0 c_p (T_skin - T_s)","category":"page"},{"location":"surface_fluxes/#Surface-evaporation","page":"Surface fluxes","title":"Surface evaporation","text":"","category":"section"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"The surface moisture flux, i.e. evaporation of soil moisture over land and evaporation of sea water over the ocean is proportional to the difference of the surface specific humidity q_s and the saturation specific humidity given T_skin and surface pressure p_s. This assumes that a very thin layer of air just above the ocean is saturated but over land this assumption is less well justified as it should be a function of the soil moisture and how much of that is available to evaporate given vegetation. We again make the simplification that q_s = q_N, i.e. specific humidity of the surface is the same as in the lowermost atmospheric layer above. ","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"The surface evaporative flux is then (always positive)","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"F_q^uparrow = rho_s C V_0 max(0 alpha_sw q^* - q_s)","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"with q^* the saturation specific humidity calculated from the skin temperature T_skin and surface pressure p_s. The available of soil water over land is represented by (over the ocean alpha_sw = 1)","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"alpha_sw = fracD_top W_top + f_veg D_root max(0 W_root - W_wil)\n D_topW_cap + D_root(W_cap - W_wil)","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"following the Fortran SPEEDY documentation[SPEEDY] which follows Viterbo and Beljiars 1995 [Viterbo95]. The variables (or spatially prescribed arrays) are water content in the top soil layer W_top and the root layer below W_root using the vegetation fraction f_veg = veg_high + 08 veg_low composed of a (dimensionless) high and low vegetation cover per grid cell veg_high veg_low. The constants are depth of top soil layer D_top = 7cm, depth of root layer D_root = 21cm, soil wetness at field capacity (volume fraction) W_cap = 03, and soil wetness at wilting point (volume fraction) W_wil = 017.","category":"page"},{"location":"surface_fluxes/#Land-sea-mask","page":"Surface fluxes","title":"Land-sea mask","text":"","category":"section"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"SpeedyWeather uses a fractional land-sea mask, i.e. for every grid-point","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"1 indicates land\n0 indicates ocean\na value in between indicates a grid-cell partially covered by ocean and land","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"The land-sea mask determines solely how to weight the surface fluxes coming from land or from the ocean. For the sensible heat fluxes this uses land and sea surface temperatures and weights the respective fluxes proportional to the fractional mask. Similar for evaporation. You can therefore define an ocean on top of a mountain, or a land without heat fluxes when the land-surface temperature is not defined, i.e. NaN. Let F_L F_S be the fluxes coming from land and sea, respectively. Then the land-sea mask a in 01 weights them as follows for the total flux F","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"F = aF_L + (1-a)F_S","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"but F=F_L if the sea flux is NaN (because the ocean temperature is not defined) and F=F_S if the land flux is NaN (because the land temperature or soil moisture is not defined, for sensible heat fluxes or evaporation), and F=0 if both fluxes are NaN.","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"Setting the land-sea mask to ocean therefore will disable any fluxes that may come from land, and vice versa. However, with an ocean-everywhere land-sea mask you must also define sea surface temperatures everywhere, otherwise the fluxes in those regions will be zero.","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"For more details see The land-sea mask implementation section.","category":"page"},{"location":"surface_fluxes/#References","page":"Surface fluxes","title":"References","text":"","category":"section"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"[Frierson2006]: Frierson, D. M. W., I. M. Held, and P. Zurita-Gotor, 2006: A Gray-Radiation Aquaplanet Moist GCM. Part I: Static Stability and Eddy Scale. J. Atmos. Sci., 63, 2548-2566. DOI: 10.1175/JAS3753.1. ","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"[SPEEDY]: Franco Molteni and Fred Kucharski, 20??. Description of the ICTP AGCM (SPEEDY) Version 41. https://users.ictp.it/~kucharsk/speedydescription/kmver41_appendixA.pdf","category":"page"},{"location":"surface_fluxes/","page":"Surface fluxes","title":"Surface fluxes","text":"[Viterbo95]: Viterbo, P., and A. C. M. Beljaars, 1995: An Improved Land Surface Parameterization Scheme in the ECMWF Model and Its Validation. J. Climate, 8, 2716-2748, DOI:10.1175/1520-0442(1995)008<2716:AILSPS>2.0.CO;2. ","category":"page"},{"location":"parameterizations/#Parameterizations","page":"Parameterizations","title":"Parameterizations","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"The following is an overview of how our parameterizations from a software engineering perspective are internally defined and how a new parameterization can be accordingly implemented. For the mathematical formulation and the physics they represent see","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"Vertical diffusion\nConvection\nLarge-scale condensation\nRadiation\nSurface fluxes ","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"We generally recommend reading Extending SpeedyWeather first, which explains the logic of how to extend many of the components in SpeedyWeather. The same logic applies here and we will not iterate on many of the details, but want to highlight which abstract supertype new parameterizations have to subtype respectively and which functions and signatures they have to extend.","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"In general, every parameterization \"class\" (e.g. convection) is just a conceptual class for clarity. You can define a custom convection parameterization that acts as a longwave radiation and vice versa. This also means that if you want to implement a parameterization that does not fit into any of the \"classes\" described here you can still implement it under any name and any class. From a software engineering perspective they are all the same except that they are executed in the order as outlined in Pass on to model construction. That's also why below we write for every parameterization \"expected to write into some.array_name\" as this would correspond conceptually to this class, but no hard requirement exists that a parameterization actually does that.","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"We start by highlighting some general do's and don'ts for parameterization before listing specifics for individual parameterizations.","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"info: Parameterizations for PrimitiveEquation models only\nThe parameterizations described here can only be used for the primitive equation models PrimitiveDryModel and PrimitiveWetModel as the parameterizations are defined to act on a vertical column. For the 2D models BarotropicModel and ShallowWaterModel additional terms have to be defined as a custom forcing or drag, see Extending SpeedyWeather.","category":"page"},{"location":"parameterizations/#Use-ColumnVariables-work-arrays","page":"Parameterizations","title":"Use ColumnVariables work arrays","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"When defining a new (mutable) parameterization with (mutable) fields do make sure that is constant during the model integration. While you can and are encouraged to use the initialize! function to precompute arrays (e.g. something that depends on latitude using model.geometry.latd) these should not be used as work arrays on every time step of the model integration. The reason is that the parameterization are executed in a parallel loop over all grid points and a mutating parameterization object would create a race condition with undefined behaviour.","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"Instead, column::ColumnVariables has several work arrays that you can reuse column.a and .b, .c, .d. Depending on the number of threads there will be several column objects to avoid the race condition if several threads would compute the parameterizations for several columns in parallel. An example is the Simplified Betts-Miller convection scheme which needs to compute reference profiles which should not live inside the model.convection object (as there's always only one of those). Instead this parameterization does the following inside convection!(column::ColumnVariables, ...)","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"# use work arrays for temp_ref_profile, humid_ref_profile\ntemp_ref_profile = column.a\nhumid_ref_profile = column.b","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"These work arrays have an unknown state so you should overwrite every entry and you also should not use them to retain information after that parameterization has been executed.","category":"page"},{"location":"parameterizations/#Accumulate-do-not-overwrite","page":"Parameterizations","title":"Accumulate do not overwrite","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"Every parameterization either computes tendencies directly or indirectly via fluxes (upward or downward, see Fluxes to tendencies). Both of these are arrays in which every parameterization writes into, meaning they should be accumulated not overwritten. Otherwise any parameterization that executed beforehand is effectively disabled. Hence, do","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"column.temp_tend[k] += something_you_calculated","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"not column.temp_tend[k] = something_you_calculated which would overwrite any previous tendency. The tendencies are reset to zero for every grid point at the beginning of the evaluation of the parameterization for that grid point, meaning you can do tend += a even for the first parameterization that writes into a given tendency as this translates to tend = 0 + a.","category":"page"},{"location":"parameterizations/#Pass-on-to-model-construction","page":"Parameterizations","title":"Pass on to model construction","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"After defining a (custom) parameterization it is recommended to also define a generator function that takes in the SpectralGrid object (see How to run SpeedyWeather.jl) as first (positional) argument, all other arguments can then be passed on as keyword arguments with defaults defined. Creating the default convection parameterization for example would be","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=31, nlayers=8)\nconvection = SimplifiedBettsMiller(spectral_grid, time_scale=Hour(4))","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"Further keyword arguments can be added or omitted all together (using the default setup), only the spectral_grid is required. We have chosen here the name of that parameterization to be convection but you could choose any other name too. However, with this choice one can conveniently pass on via matching the convection field inside PrimitiveWetModel, see Keyword Arguments.","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"model = PrimitiveWetModel(spectral_grid; convection)\nnothing # hide","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"otherwise we would need to write","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"my_convection = SimplifiedBettsMiller(spectral_grid)\nmodel = PrimitiveWetModel(spectral_grid, convection=my_convection)\nnothing # hide","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"The following is an overview of what the parameterization fields inside the model are called. See also Tree structure, and therein PrimitiveDryModel and PrimitiveWetModel","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"model.boundary_layer_drag\nmodel.temperature_relaxation\nmodel.vertical_diffusion\nmodel.convection\nmodel.large_scale_condensation (PrimitiveWetModel only)\nmodel.shortwave_radiation\nmodel.longwave_radiation\nmodel.surface_thermodynamics\nmodel.surface_wind\nmodel.surface_heat_flux\nmodel.surface_evaporation (PrimitiveWetModel only)","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"Note that the parameterizations are executed in the order of the list above. That way, for example, radiation can depend on calculations in large-scale condensation but not vice versa (only at the next time step).","category":"page"},{"location":"parameterizations/#Custom-boundary-layer-drag","page":"Parameterizations","title":"Custom boundary layer drag","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"A boundary layer drag can serve two purposes: (1) Actually define a tendency to the momentum equations that acts as a drag term, or (2) calculate the drag coefficient C in column.boundary_layer_drag that is used in the Surface fluxes.","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"subtype CustomDrag <: AbstractBoundaryLayer\ndefine initialize!(::CustomDrag, ::PrimitiveEquation)\ndefine boundary_layer_drag!(::ColumnVariables, ::CustomDrag, ::PrimitiveEquation)\nexpected to accumulate (+=) into column.u_tend and column.v_tend\nor calculate column.boundary_layer_drag to be used in surface fluxes","category":"page"},{"location":"parameterizations/#Custom-temperature-relaxation","page":"Parameterizations","title":"Custom temperature relaxation","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"By default, there is no temperature relaxation in the primitive equation models (i.e. temperature_relaxation = NoTemperatureRelaxation()). This parameterization exists for the Held-Suarez forcing.","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"subtype CustomTemperatureRelaxation <: AbstractTemperatureRelaxation\ndefine initialize!(::CustomTemperatureRelaxation, ::PrimitiveEquation)\ndefine temperature_relaxation!(::ColumnVariables, ::CustomTemperatureRelaxation, ::PrimitiveEquation)\nexpected to accumulate (+=) into column.temp_tend","category":"page"},{"location":"parameterizations/#Custom-vertical-diffusion","page":"Parameterizations","title":"Custom vertical diffusion","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"While vertical diffusion may be applied to temperature (usually via some form of static energy to account for adiabatic diffusion), humidity and/or momentum, they are grouped together. You can define a vertical diffusion for only one or several of these variables where you then can internally call functions like diffuse_temperature!(...) for each variable. For vertical diffusion","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"subtype CustomVerticalDiffusion <: AbstractVerticalDiffusion\ndefine initialize!(::CustomVerticalDiffusion, ::PrimitiveEquation)\ndefine vertical_diffusion!(::ColumnVariables, ::CustomVerticalDiffusion, ::PrimitiveEquation)\nexpected to accumulate (+=) into column.temp_tend, and similarly for humid, u, and/or v\nor using fluxes like column.flux_temp_upward","category":"page"},{"location":"parameterizations/#Custom-convection","page":"Parameterizations","title":"Custom convection","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"subtype CustomConvection <: AbstractConvection\ndefine initialize!(::CustomConvection, ::PrimitiveEquation)\ndefine convection!(::ColumnVariables, ::CustomConvection, ::PrimitiveEquation)\nexpected to accumulate (+=) into column.temp_tend and column.humid_tend\nor using fluxes, .flux_temp_upward or similarly for humid or downward","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"Note that we define convection here for a model of type PrimitiveEquation, i.e. both dry and moist convection. If your CustomConvection only makes sense for one of them use ::PrimitiveDry or ::PrimitiveWet instead.","category":"page"},{"location":"parameterizations/#Custom-large-scale-condensation","page":"Parameterizations","title":"Custom large-scale condensation","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"subtype CustomCondensation <: AbstractCondensation\ndefine initialize!(::CustomCondensation, ::PrimitiveWet)\ndefine condensation!(::ColumnVariables, ::CustomCondensation, ::PrimitiveWet)\nexpected to accumulate (+=) into column.humid_tend and column.temp_tend\nor using fluxes, .flux_humid_downward or similarly for temp or upward","category":"page"},{"location":"parameterizations/#Custom-radiation","page":"Parameterizations","title":"Custom radiation","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"AbstractRadiation has two subtypes, AbstractShortwave and AbstractLongwave representing two (from a software engineering perspective) independent parameterizations that are called one after another (short then long). For shortwave","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"subtype CustomShortwave <: AbstractShortwave\ndefine initialize!(::CustomShortwave, ::PrimitiveEquation)\ndefine shortwave_radiation!(::ColumnVariables, ::CustomShortwave, ::PrimitiveEquation)\nexpected to accumulate (+=) into column.flux_temp_upward, .flux_temp_downward\nor directly into the tendency .temp_tend","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"For longwave this is similar but using <: AbstractLongwave and longwave_radiation!.","category":"page"},{"location":"parameterizations/#Custom-surface-fluxes","page":"Parameterizations","title":"Custom surface fluxes","text":"","category":"section"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"Surface fluxes are the most complicated to customize as they depend on the Ocean and Land model, The land-sea mask, and by default the Bulk Richardson-based drag coefficient, see Custom boundary layer drag. The computation of the surface fluxes is split into four (five if you include the boundary layer drag coefficient in Custom boundary layer drag) components that are called one after another","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"Surface thermodynamics to calculate the surface values of lowermost layer variables","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"subtype CustomSurfaceThermodynamics <: AbstractSurfaceThermodynamics\ndefine initialize!(::CustomSurfaceThermodynamics, ::PrimitiveEquation)\ndefine surface_thermodynamics!(::ColumnVariables, ::CustomSurfaceThermodynamics, ::PrimitiveEquation)\nexpected to set column.surface_temp, .surface_humid, .surface_air_density","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"Surface wind to calculate wind stress (momentum flux) as well as surface wind used","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"subtype CustomSurfaceWind <: AbstractSurfaceWind\ndefine initialize!(::CustomSurfaceWind, ::PrimitiveEquation)\ndefine surface_wind_stress!(::ColumnVariables, ::CustomSurfaceWind, ::PrimitiveEquation)\nexpected to set column.surface_wind_speed for other fluxes\nand accumulate (+=) into column.flux_u_upward and .flux_v_upward","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"Surface (sensible) heat flux","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"subtype CustomSurfaceHeatFlux <: AbstractSurfaceHeatFlux\ndefine initialize!(::CustomSurfaceHeatFlux, ::PrimitiveEquation)\ndefine surface_heat_flux!(::ColumnVariables, ::CustomSurfaceHeatFlux, ::PrimitiveEquation)\nexpected to accumulate (+=) into column.flux_temp_upward","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"Surface evaporation","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"subtype CustomSurfaceEvaporation <: AbstractSurfaceEvaporation\ndefine initialize!(::CustomSurfaceEvaporation, ::PrimitiveEquation)\ndefine surface_evaporation!(::ColumnVariables, ::CustomSurfaceEvaporation, ::PrimitiveEquation)\nexpected to accumulate (+=) into column.flux_humid_upward","category":"page"},{"location":"parameterizations/","page":"Parameterizations","title":"Parameterizations","text":"You can customize individual components and leave the other ones as default or by setting them to NoSurfaceWind, NoSurfaceHeatFlux, NoSurfaceEvaporation, but note that without the surface wind the heat and evaporative fluxes are also effectively disabled as they scale with the column.surface_wind_speed set by default with the surface_wind_stress! in (2.) above.","category":"page"},{"location":"vertical_diffusion/#Vertical-diffusion","page":"Vertical diffusion","title":"Vertical diffusion","text":"","category":"section"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"Vertical diffusion in SpeedyWeather.jl is implemented as a Laplacian in the vertical Sigma coordinates with a diffusion coefficient K that in general depends on space and time and is flow-aware, meaning it is recalculated on every time step depending on the vertical stability of the atmospheric column.","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"Vertical diffusion can be applied to velocities u v, temperature T (done via dry static energy, see below) and specific humidity q.","category":"page"},{"location":"vertical_diffusion/#Implementations","page":"Vertical diffusion","title":"Implementations","text":"","category":"section"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"The following schemes for vertical diffusion are currently implemented","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"using InteractiveUtils # hide\nusing SpeedyWeather\nsubtypes(SpeedyWeather.AbstractVerticalDiffusion)","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"NoVerticalDiffusion disabled all vertical diffusion, BulkRichardsonDiffusion is explained in the following.","category":"page"},{"location":"vertical_diffusion/#Laplacian-in-sigma-coordinates","page":"Vertical diffusion","title":"Laplacian in sigma coordinates","text":"","category":"section"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"The vertical diffusion of a variable, say u, takes the in sigma coordinates the form","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"fracpartial upartial t = fracpartialpartial sigma K\nfracpartial upartial sigma","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"as a tendency to u with no-flux boundary conditions fracpartial upartial sigma = 0 at sigma = 0 (top of the atmosphere) and sigma = 1 (the surface). That way the diffusion preserves the integral of the variable u from sigma = 0 to sigma = 1.","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"fracpartialpartial t int_0^1 u dsigma = int_0^1 fracpartialpartial sigma K\nfracpartial upartial sigma dsigma = \nKfracpartial upartial sigma vert_sigma = 1 - Kfracpartial upartial sigma vert_sigma = 0\n= 0","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"Discretising the diffusion operator partial_sigma K partial_sigma over N vertical layers k = 1N with u_k, K_k on those layers at respective coordinates sigma_k that are generally not equally spaced using centred finite differences","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"fracpartialpartial sigma K fracpartial upartial sigma approx\nfrac\n fracK_k+1 + K_k2 fracu_k+1 - u_k sigma_k+1 - sigma_k - \n fracK_k + K_k-12 fracu_k - u_k-1sigma_k - sigma_k-1\n\n sigma_k+12 - sigma_k-12\n","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"We reconstruct K on the faces k+12 with a simple arithmetic average of K_k and K_k+1. This is necessary for the multiplication with the gradients which are only available on the faces after the centred gradients are computed. We then take the gradient again to obtain the final tendencies again at cell centres k.","category":"page"},{"location":"vertical_diffusion/#Bulk-Richardson-based-diffusion-coefficient","page":"Vertical diffusion","title":"Bulk Richardson-based diffusion coefficient","text":"","category":"section"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"We calculate the diffusion coefficient K based on the bulk Richardson number Ri [Frierson2006] which is computed as follows","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"Ri = fracgz left( Theta_v(z) - Theta_v(z_N) right)v(z)^2 Theta_v(z_N)","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"(see Bulk Richardson-based drag coefficient in comparison). Gravitational acceleration is g, height z, Theta_v the virtual potential temperature where we use the virtual dry static energy c_pT_v + gz with T_v the Virtual temperature. The boundary layer height h (vertical index k_h) is defined as the height of the lowermost layer where Ri_k_h Ri_c with Ri_c = 1 the critical Richardson number.","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"The diffusion coefficient K is for every layer k geq k_h in the boundary layer calculated depending on the height z of a layer and its bulk Richardson number Ri.","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"K(z) = begincases\n K_b(z) quad textfor quad z leq f_b h \n K_b(f_b h) fraczf_b h left( 1 - fracz - f_b h(1 - f_b)h right)^2\n quad textfor quad f_b h z leq h \nendcases","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"with f_b = 01 the fraction of the boundary layer height h above which the second case guarantees a smooth transition in K to zero at z = h. K_b(z) is then defined as","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"Kb(z) = begincases\n kappa u_N sqrtCz quad textfor quad Ri_N leq 0 \n kappa u_N sqrtCz left( 1 + fracRiRi_cfraclog(zz_0)1 - RiRi_cright)^-1\n quad textfor quad Ri_N 0 \nendcases","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"C is the surface drag coefficient as computed in Bulk Richardson-based drag coefficient. The subscript N denotes the lowermost model layer k=N.","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"As for the Bulk Richardson-based drag coefficient we also simplify this calculation here by approximating log(zz_0) approx log(Zz_0) with the height Z of the lowermost layer given resolution and a reference surface temperature, for more details see description in that section.","category":"page"},{"location":"vertical_diffusion/#Vertical-diffusion-tendencies","page":"Vertical diffusion","title":"Vertical diffusion tendencies","text":"","category":"section"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"The vertical diffusion is then computed as a tendency for u v q and temperature T via the dry static energy SE = c_p T + gz, i.e.","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"fracpartial Tpartial t = frac1c_pfracpartialpartial sigma K\nfracpartial SEpartial sigma ","category":"page"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"where we just fold the heat capacity c_p into the diffusion coefficient K to Kc_p. The other variables are diffused straight-forwardly as partial_t u = partial_sigma K partial_sigma u, etc.","category":"page"},{"location":"vertical_diffusion/#References","page":"Vertical diffusion","title":"References","text":"","category":"section"},{"location":"vertical_diffusion/","page":"Vertical diffusion","title":"Vertical diffusion","text":"[Frierson2006]: Frierson, D. M. W., I. M. Held, and P. Zurita-Gotor, 2006: A Gray-Radiation Aquaplanet Moist GCM. Part I: Static Stability and Eddy Scale. J. Atmos. Sci., 63, 2548-2566. DOI: 10.1175/JAS3753.1.","category":"page"},{"location":"large_scale_condensation/#Large-scale-condensation","page":"Large-scale condensation","title":"Large-scale condensation","text":"","category":"section"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"Large-scale condensation in an atmospheric general circulation represents the micro-physical that kicks in when an air parcel reaches saturation. Subsequently, the water vapour inside it condenses, forms droplets around condensation nuclei, which grow, become heavy and eventually fall out as precipitation. This process is never actually representable at the resolution of global (or even regional) atmospheric models as typical cloud droplets have a size of micrometers. Atmospheric models therefore rely on large-scale quantities such as specific humidity, pressure and temperature within a given grid cell, even though there might be considerably variability of these quantities within a grid cell if the resolution was higher.","category":"page"},{"location":"large_scale_condensation/#Condensation-implementations","page":"Large-scale condensation","title":"Condensation implementations","text":"","category":"section"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"Currently implemented are","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"using InteractiveUtils # hide\nusing SpeedyWeather\nsubtypes(SpeedyWeather.AbstractCondensation)","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"which are described in the following.","category":"page"},{"location":"large_scale_condensation/#Explicit-large-scale-condensation","page":"Large-scale condensation","title":"Explicit large-scale condensation","text":"","category":"section"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"We parameterize this process of large-scale condensation when relative humidity in a grid cell reaches saturation and remove the excess humidity quickly (given time integration constraints, see below) and with an implicit (in the time integration sense) latent heat release. Vertically integrating the tendency of specific humidity due to this process is then the large-scale precipitation.","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"Immediate condensation of humidity q_i q^star at time step i given its saturation q^star humidity calculated from temperature T_i is","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"beginaligned\nq_i+1 - q_i = q^star(T_i) - q_i \nT_i+1 - T_i = -fracL_vc_p( q^star(T_i) - q_i )\nendaligned","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"This condensation is explicit in the time integration sense, meaning that we only use quantities at time step i to calculate the tendency. The latent heat release of that condensation is in the second equation. However, treating this explicitly poses the problem that because the saturation humidity is calculated from the current temperature T_i, which is increased due to the latent heat release, the humidity after this time step will be undersaturated.","category":"page"},{"location":"large_scale_condensation/#Implicit-large-scale-condensation","page":"Large-scale condensation","title":"Implicit large-scale condensation","text":"","category":"section"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"Ideally, one would want to condense towards the new saturation humidity q^star(T_i+1) at i+1 so that condensation draws the relative humidity back down to 100% not below it. Taylor expansion at i of the equation above with q^star(T_i+1) and Delta T = T_i+1 - T_i (and Delta q similarly) to first order yields","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"q_i+1 - q_i = q^star(T_i+1) - q_i = q^star(T_i) + (T_i+1 - T_i)\nfracpartial q^starpartial T (T_i) + O(Delta T^2) - q_i","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"Now we make a linear approximation to the derivative and drop the O(Delta T^2) term. Inserting the (explicit) latent heat release yields","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"Delta q = q^star(T_i) + -fracL_vc_p Delta q fracpartial q^starpartial T (T_i) - q_i","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"And solving for Delta q yields","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"left 1 + fracL_vc_p fracpartial q^starpartial T (T^i) right Delta q = q^star(T_i) - q_i","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"meaning that the implicit immediate condensation can be formulated as (see also [Frierson2006])","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"beginaligned\nq_i+1 - q_i = fracq^star(T_i) - q_i1 + fracL_vc_p fracpartial q^starpartial T(T_i) \nT_i+1 - T_i = -fracL_vc_p( q_i+1 - q_i )\nendaligned","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"With Euler forward time stepping this is great, but with our leapfrog timestepping + RAW filter this is very dispersive (see #445) although the implicit formulation is already much better. We therefore introduce a time step Delta t_c which makes the implicit condensation not immediate anymore but over several time steps Delta t of the leapfrogging.","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"beginaligned\ndelta q = fracq_i+1 - q_iDelta t = fracq^star(T_i) - q_i Delta t_c\nleft( 1 + fracL_vc_p fracpartial q^starpartial T(T_i) right) \ndelta T = fracT_i+1 - T_iDelta t = -fracL_vc_p( fracq_i+1 - q_iDelta t )\nendaligned","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"For Delta t = Delta t_c we have an immediate condensation, for n = fracDelta t_cDelta t condensation takes place over n time steps. One could tie this time scale for condensation to a physical unit, like 6 hours, but because the time step here is ideally short, but cannot be too short for numerical stability, we tie it here to the time step of the numerical integration. This also means that at higher resolution condensation is more immediate than at low resolution, but the dispersive time integration of this term is in all cases similar (and not much higher at lower resolution).","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"The above implied that condensation takes place at 100% relative humidity as we are directly comparing the specific humidity to the saturation specific humidity in q^star - q_i. However, in coarse-resolution models there is a good argument that condensation may already take place below a grid-cell average of 100% relative humidity. Given some subgrid-scale variability parts of the grid cells may condense even though the average humidity is below 100%. To change this threshold one can introduce a parameter r, e.g. r=095 for condensation to take place at 95% relative humidity, as follows","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"delta q = fracq_i+1 - q_iDelta t = fracrq^star(T_i) - q_i Delta t_c\nleft( 1 + fracL_vrc_p fracpartial q^starpartial T(T_i) right)","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"r is a linear scale and therefore can be taken out of the gradient fracpartial q^starpartial T in the denominator.","category":"page"},{"location":"large_scale_condensation/#Large-scale-precipitation","page":"Large-scale condensation","title":"Large-scale precipitation","text":"","category":"section"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"The tendencies delta q in units of kg/kg/s are vertically integrated to diagnose the large-scale precipitation P in units of meters","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"P = -int fracDelta tg rho delta q dp","category":"page"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"with gravity g, water density rho and time step Delta t. P is therefore interpreted as the amount of precipitation that falls down during the time step Delta t of the time integration. Note that delta q is always negative due to the q q^star condition for saturation, hence P is positive only. It is then accumulated over several time steps, e.g. over the course of an hour to yield a typical rain rate of mm/h. The water density is taken as reference density of 1000kgm^3","category":"page"},{"location":"large_scale_condensation/#References","page":"Large-scale condensation","title":"References","text":"","category":"section"},{"location":"large_scale_condensation/","page":"Large-scale condensation","title":"Large-scale condensation","text":"[Frierson2006]: Frierson, D. M. W., I. M. Held, and P. Zurita-Gotor, 2006: A Gray-Radiation Aquaplanet Moist GCM. Part I: Static Stability and Eddy Scale. J. Atmos. Sci., 63, 2548-2566, DOI:10.1175/JAS3753.1.","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 = (u v) 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_u F_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 u v\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 u v 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 (A B)_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 u v zeta 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 l m 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 0 1. 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_l m = -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^textexplicit n_l m = -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^textimplicit n_l m = 1 - 2Delta t D^textexplicit n_l m. 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 = (u v), 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 0 Delta 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 05 1. 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":"examples_2D/#Examples","page":"Examples 2D","title":"Examples 2D","text":"","category":"section"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"The following is a collection of example model setups, starting with an easy setup of the Barotropic vorticity equation and continuing with the shallow water model.","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"See also Examples 3D for examples with the primitive equation models.","category":"page"},{"location":"examples_2D/#2D-turbulence-on-a-non-rotating-sphere","page":"Examples 2D","title":"2D turbulence on a non-rotating sphere","text":"","category":"section"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"info: Setup script to copy and paste\nusing SpeedyWeather\nspectral_grid = SpectralGrid(trunc=63, nlayers=1)\nstill_earth = Earth(spectral_grid, rotation=0)\ninitial_conditions = StartWithRandomVorticity()\nmodel = BarotropicModel(spectral_grid; initial_conditions, planet=still_earth)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(20))","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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 nlayers=1 vertical levels. The SpectralGrid object will provide us with some more information","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=63, nlayers=1)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"Next step we create a planet that's like Earth but not rotating. As a convention, we always pass on the spectral grid object as the first argument to every other model component we create.","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"still_earth = Earth(spectral_grid, rotation=0)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"using Random # hide\nRandom.seed!(1234) # hide\ninitial_conditions = StartWithRandomVorticity()","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"Now we want to construct a BarotropicModel with these","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"model = BarotropicModel(spectral_grid; initial_conditions, planet=still_earth)\nnothing # hide","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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 a unicode plot (via UnicodePlots.jl) of the surface relative vorticity. This is just to get a quick idea of what was simulated. The resolution of the plot is not necessarily representative.","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"simulation = initialize!(model)\nrun!(simulation, period=Day(20))","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"Woohoo! Something is moving! You could pick up where this simulation stopped by simply doing run!(simulation, period=Day(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":"examples_2D/#Shallow-water-with-mountains","page":"Examples 2D","title":"Shallow water with mountains","text":"","category":"section"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"info: Setup script to copy and past\nusing SpeedyWeather\nspectral_grid = SpectralGrid(trunc=63, nlayers=1)\norography = NoOrography(spectral_grid)\ninitial_conditions = ZonalJet()\nmodel = ShallowWaterModel(spectral_grid; orography, initial_conditions)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(6))","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=63, nlayers=1)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"Now as a first simulation, we want to disable any orography, so we create a NoOrography","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"orography = NoOrography(spectral_grid)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"initial_conditions = ZonalJet()","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"model = ShallowWaterModel(spectral_grid; orography, initial_conditions)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(6))","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"run!(simulation, period=Day(6), output=true)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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. In general we can check this also via","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"id = model.output.id","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"So let's plot that data properly (and not just using UnicodePlots.jl). $id in the following just means that the string is interpolated to run_0001 if this is the first unnamed run in your folder.","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"using NCDatasets\nds = NCDataset(\"run_$id/output.nc\")\nds[\"vor\"]","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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\nusing CairoMakie\nheatmap(lon, lat, vor)\nsave(\"galewsky0.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"(Image: Galewsky jet)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"You see that in comparison the unicode plot heavily coarse-grains the simulation, well it's unicode after all! Here, we have unpacked the netCDF file using NCDatasets.jl and then plotted via heatmap(lon, lat, vor). While you can do that to give you more control on the plotting, SpeedyWeather.jl also defines an extension for Makie.jl, see Extensions. Because if our matrix vor here was an AbstractGrid (see RingGrids) then all its geographic information (which grid point is where) would be implicitly known from the type. From the netCDF file, however, you would need to use the longitude and latitude dimensions.","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"So we can also just do (input_as=Matrix here as all our grids use and expect a horizontal dimension flattened into a vector by default)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"vor_grid = FullGaussianGrid(vor, input_as=Matrix)\n\nusing CairoMakie # this will load the extension so that Makie can plot grids directly\nheatmap(vor_grid, title=\"Relative vorticity [1/s]\")\nsave(\"galewsky1.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"(Image: Galewsky jet pyplot1)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"Note that here you need to know which grid the data comes on (an error is thrown if FullGaussianGrid(vor) is not size compatible). By default the output will be on the FullGaussianGrid, but if you play around with other grids, you'd need to change this here, see NetCDF output and Output grid.","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"We did want to showcase the usage of NetCDF output here, but from now on we will use heatmap to plot data on our grids directly, without storing output first. So for our current simulation, that means at time = 12 days, vorticity on the grid is stored in the diagnostic variables and can be visualised with ([:, 1] is horizontal x vertical dimension, so all grid points on the first and only vertical layer)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"vor = simulation.diagnostic_variables.grid.vor_grid[:, 1]\nheatmap(vor, title=\"Relative vorticity [1/s]\")\nsave(\"galewsky2.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"(Image: Galewsky jet)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"orography = EarthOrography(spectral_grid)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"It will read the orography from file as shown (only at initialize!(model)), 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":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"model = ShallowWaterModel(spectral_grid; orography, initial_conditions)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(12), output=true)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"This time the run got a new run id, which you see in the progress bar, but can again 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":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"id = model.output.id","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"ds = NCDataset(\"run_$id/output.nc\")","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"You could plot the NetCDF output now as before, but we'll be plotting directly from the current state of the simulation","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"vor = simulation.diagnostic_variables.grid.vor_grid[:, 1] # 1 to index surface\nheatmap(vor, title=\"Relative vorticity [1/s]\")\nsave(\"galewsky3.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"(Image: Galewsky jet)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"Interesting! One can clearly see some imprint of the orography on vorticity and there is especially more vorticity in the southern hemisphere. 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":"examples_2D/#Polar-jet-streams-in-shallow-water","page":"Examples 2D","title":"Polar jet streams in shallow water","text":"","category":"section"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"Setup script to copy and paste:","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=63, nlayers=1)\n\nforcing = JetStreamForcing(spectral_grid, latitude=60)\ndrag = QuadraticDrag(spectral_grid)\n\nmodel = ShallowWaterModel(spectral_grid; drag, forcing)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(40))\nnothing # hide","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"We want to simulate polar jet streams in the shallow water model. We add a JetStreamForcing that adds momentum at 60˚N and 60˚S an 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. Then visualize zonal wind after 40 days with","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"using CairoMakie\n\nu = simulation.diagnostic_variables.grid.u_grid[:, 1]\nheatmap(u, title=\"Zonal wind [m/s]\")\nsave(\"polar_jets.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"(Image: Polar jets pyplot)","category":"page"},{"location":"examples_2D/#Gravity-waves-on-the-sphere","page":"Examples 2D","title":"Gravity waves on the sphere","text":"","category":"section"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"Setup script to copy and paste:","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"using Random # hide\nRandom.seed!(1234) # hide\nusing SpeedyWeather\nspectral_grid = SpectralGrid(trunc=127, nlayers=1)\n\n# model components\nimplicit = ImplicitShallowWater(spectral_grid, α=0.5)\norography = EarthOrography(spectral_grid, smoothing=false)\ninitial_conditions = RandomWaves(lmin=10, lmax=30) # between wavenumber 10 and 30\n\n# construct, initialize, run\nmodel = ShallowWaterModel(spectral_grid; orography, initial_conditions, implicit)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(2))\nnothing # hide","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"RandomWaves()","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"so that the amplitude A is as desired, here 2000m. Our layer thickness in meters is by default","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"model.atmosphere.layer_thickness","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"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":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"model.time_stepping.Δt_sec","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"seconds. Note that the gravity wave speed here is sqrtgH so almost 300m/s, given the speed of gravity waves we don't have to integrate for long. Visualise the dynamic layer thickness h = eta + H + H_b (interface displacement eta, layer thickness at rest H and orography H_b) with","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"using CairoMakie\n\nH = model.atmosphere.layer_thickness\nHb = model.orography.orography\nη = simulation.diagnostic_variables.grid.pres_grid\nh = @. η + H - Hb # @. to broadcast grid + scalar - grid\n\nheatmap(h, title=\"Dynamic layer thickness h\", colormap=:oslo)\nsave(\"gravity_waves.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"(Image: Gravity waves pyplot)","category":"page"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","text":"Mountains like the Himalayas or the Andes are quite obvious because the atmospheric layer is much thinner there. The pressure gradient is relative to z=0 so in a fluid at rest the mountains would just \"reach into\" the fluid, thinning the layer the higher the mountain. As the atmosphere here is not at rest the layer thickness is not perfectly (anti-)correlated with orography but almost so.","category":"page"},{"location":"examples_2D/#References","page":"Examples 2D","title":"References","text":"","category":"section"},{"location":"examples_2D/","page":"Examples 2D","title":"Examples 2D","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":"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":"In a similar manner, other branches can be also 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 requires Julia v1.10 or later. The package is tested on Julia 1.10, and 1.11.","category":"page"},{"location":"installation/#Extensions","page":"Installation","title":"Extensions","text":"","category":"section"},{"location":"installation/","page":"Installation","title":"Installation","text":"SpeedyWeather.jl has a weak dependency on","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"Makie.jl to extend Makie.heatmap","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"This is an extension, meaning that this functionality is only loaded from SpeedyWeather when using Makie (or its backends CairoMakie.jl, GLMakie.jl, ...). Hence, if you want to make use of this extension (some Examples show this) you need to install Makie.jl manually.","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/#Creating-NetCDFOutput","page":"NetCDF output","title":"Creating NetCDFOutput","text":"","category":"section"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"using SpeedyWeather\nspectral_grid = SpectralGrid()\noutput = NetCDFOutput(spectral_grid)","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"With NetCDFOutput(::SpectralGrid, ...) one creates a NetCDFOutput writer with several options, which are explained in the following. By default, the NetCDFOutput is created when constructing the model, i.e.","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"model = ShallowWaterModel(spectral_grid)\nmodel.output","category":"page"},{"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, and they only differ in their default output.variables (e.g. the primitive models would by default output temperature which does not exist in the 2D models BarotropicModel or ShallowWaterModel). But any NetCDFOutput can be passed onto the model constructor with the output keyword argument.","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"output = NetCDFOutput(spectral_grid, Barotropic)\nmodel = ShallowWaterModel(spectral_grid, output=output)\nnothing # hide","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"Here, we created NetCDFOutput for the model class Barotropic (2nd positional argument, outputting only vorticity and velocity) but use it in the ShallowWaterModel. By default the NetCDFOutput is set to inactive, i.e. output.active is false. It is only turned on (and initialized) with run!(simulation, output=true). So you may change the NetCDFOutput as you like but only calling run!(simulation) will not trigger it as output=false is the default here.","category":"page"},{"location":"output/#Output-frequency","page":"NetCDF output","title":"Output frequency","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 =Hour(6)) like so","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"output = NetCDFOutput(spectral_grid, ShallowWater, output_dt=Hour(1))\nmodel = ShallowWaterModel(spectral_grid, output=output)\nmodel.output","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 output to the model constructor, otherwise it will not be part of your model and the default is used instead. Note that the choice of output_dt can affect the actual time step that is used for the model integration, which is explained in the following. Example, we run the model at a resolution of T42 and the time step is going to be","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"spectral_grid = SpectralGrid(trunc=42, nlayers=1)\ntime_stepping = Leapfrog(spectral_grid)\ntime_stepping.Δt_sec","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"seconds. Depending on the output frequency (we chose output_dt = Hour(1) above) this will be slightly adjusted during model initialization:","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"output = NetCDFOutput(spectral_grid, ShallowWater, output_dt=Hour(1))\nmodel = ShallowWaterModel(spectral_grid; time_stepping, output)\nsimulation = initialize!(model)\nmodel.time_stepping.Δt_sec","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"The shorter the output time step the more the model time step needs to be adjusted to match the desired output time step exactly. This is important so that for daily output at noon this does not slowly shift towards night over years of model integration. One can always disable this adjustment with","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"time_stepping = Leapfrog(spectral_grid, adjust_with_output=false)\ntime_stepping.Δt_sec","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"and a little info will be printed to explain that even though you wanted output_dt = Hour(1) you will not actually get this upon initialization:","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"model = ShallowWaterModel(spectral_grid; time_stepping, output)\nsimulation = initialize!(model)","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"The time axis of the NetCDF output will now look like","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"using NCDatasets\nrun!(simulation, period=Day(1), output=true)\nid = model.output.id\nds = NCDataset(\"run_$id/output.nc\")\nds[\"time\"][:]","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"which is a bit ugly, that's why adjust_with_output=true is the default. In that case we would have","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"time_stepping = Leapfrog(spectral_grid, adjust_with_output=true)\noutput = NetCDFOutput(spectral_grid, ShallowWater, output_dt=Hour(1))\nmodel = ShallowWaterModel(spectral_grid; time_stepping, output)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(1), output=true)\nid = model.output.id\nds = NCDataset(\"run_$id/output.nc\")\nds[\"time\"][:]","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"very neatly hourly output in the NetCDF file!","category":"page"},{"location":"output/#Output-grid","page":"NetCDF output","title":"Output 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 NetCDFOutput 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":"my_output_writer = NetCDFOutput(spectral_grid, 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 type of the grid type 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":"RingGrids.full_grid_type(OctahedralGaussianGrid)","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/#Output-variables","page":"NetCDF output","title":"Output variables","text":"","category":"section"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"One can easily add or remove variables from being output with the NetCDFOut writer. The following variables are predefined (note they are not exported so you have to prefix SpeedyWeather.)","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"using InteractiveUtils # hide\nsubtypes(SpeedyWeather.AbstractOutputVariable)","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"\"Defined\" here means that every such type contains information about a variables (long) name, its units, dimensions, any missing values and compression options. For HumidityOutput for example we have","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"SpeedyWeather.HumidityOutput()","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"You can choose name and unit as you like, e.g. SpeedyWeather.HumidityOutput(unit = \"1\") or change the compression options, e.g. SpeedyWeather.HumidityOutput(keepbits = 5) but more customisation is discussed in Customizing netCDF output.","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"We can add new output variables with add! ","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"output = NetCDFOutput(spectral_grid) # default variables\nadd!(output, SpeedyWeather.DivergenceOutput()) # output also divergence\noutput","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"If you didn't create a NetCDFOutput separately, you can also apply this directly to model, either add!(model, SpeedyWeather.DivergenceOutput()) or add!(model.output, args...), which technically also just forwards to add!(model.output.variables, args...). output.variables is a dictionary were the variable names (as Symbols) are used as keys, so output.variables[:div] just returns the SpeedyWeather.DivergenceOutput() we have just created using :div as key. With those keys one can also delete! a variable from netCDF output","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"delete!(output, :div)","category":"page"},{"location":"output/","page":"NetCDF output","title":"NetCDF output","text":"If you change the name of an output variable, i.e. SpeedyWeather.DivergenceOutput(name=\"divergence\") the key would change accordingly to :divergence.","category":"page"},{"location":"output/#Output-path-and-identification","page":"NetCDF output","title":"Output path and 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 = NetCDFOutput(spectral_grid, 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 = NetCDFOutput(spectral_grid, 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 NetCDFOutput docstring, (also accessible via julia>?NetCDFOutput 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":"@doc NetCDFOutput","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/#Core.Type-Tuple{SpectralGrid}","page":"Function and type index","title":"Core.Type","text":"Generator function pulling the resolution information from spectral_grid.\n\n\n\n\n\n","category":"method"},{"location":"functions/#Core.Type-Union{Tuple{SpectralGrid}, Tuple{Orography}} where Orography<:SpeedyWeather.AbstractOrography","page":"Function and type index","title":"Core.Type","text":"Generator function pulling the resolution information from spectral_grid for all Orography <: AbstractOrography.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.AbstractDevice","page":"Function and type index","title":"SpeedyWeather.AbstractDevice","text":"abstract type AbstractDevice\n\nSupertype of all devices SpeedyWeather.jl can run on\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.AbstractLandSeaMask","page":"Function and type index","title":"SpeedyWeather.AbstractLandSeaMask","text":"Abstract super type for land-sea masks. Custom land-sea masks have to be defined as\n\nCustomMask{NF<:AbstractFloat, Grid<:AbstractGrid{NF}} <: AbstractLandSeaMask{NF, Grid}\n\nand need to have at least a field called mask::Grid that uses a Grid as defined by the spectral grid object, so of correct size and with the number format NF. All AbstractLandSeaMask have a convenient generator function to be used like mask = CustomMask(spectral_grid, option=argument), but you may add your own or customize by defining CustomMask(args...) which should return an instance of type CustomMask{NF, Grid} with parameters matching the spectral grid. Then the initialize function has to be extended for that new mask\n\ninitialize!(mask::CustomMask, model::PrimitiveEquation)\n\nwhich generally is used to tweak the mask.mask grid as you like, using any other options you have included in CustomMask as fields or anything else (preferrably read-only, because this is only to initialize the land-sea mask, nothing else) from model. You can for example read something from file, set some values manually, or use coordinates from model.geometry.\n\nThe land-sea mask grid is expected to have values between [0, 1] as we use a fractional mask, allowing for grid points being, e.g. quarter land and three quarters sea for 0.25 with 0 (=sea) and 1 (=land). The surface fluxes will weight proportionally the fluxes e.g. from sea and land surface temperatures. Note however, that the land-sea mask can declare grid points being (at least partially) ocean even though the sea surface temperatures aren't defined (=NaN) in that grid point. In that case, not flux is applied.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.AbstractOcean","page":"Function and type index","title":"SpeedyWeather.AbstractOcean","text":"Abstract super type for ocean models, which control the sea surface temperature and sea ice concentration as boundary conditions to a SpeedyWeather simulation. A new ocean model has to be defined as\n\nCustomOceanModel <: AbstractOcean\n\nand can have parameters like CustomOceanModel{T} and fields. They need to extend the following functions\n\nfunction initialize!(ocean_model::CustomOceanModel, model::PrimitiveEquation)\n # your code here to initialize the ocean model itself\n # you can use other fields from model, e.g. model.geometry\nend\n\nfunction initialize!( \n ocean::PrognosticVariablesOcean,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n ocean_model::CustomOceanModel,\n model::PrimitiveEquation,\n)\n # your code here to initialize the prognostic variables for the ocean\n # namely, ocean.sea_surface_temperature, ocean.sea_ice_concentration, e.g.\n # ocean.sea_surface_temperature .= 300 # 300K everywhere\nend\n\nfunction ocean_timestep!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n ocean_model::CustomOceanModel,\n model::PrimitiveEquation,\n)\n # your code here to change the progn.ocean.sea_surface_temperature and/or\n # progn.ocean.sea_ice_concentration on any timestep\nend\n\nTemperatures in ocean.seasurfacetemperature have units of Kelvin, or NaN for no ocean. Note that neither sea surface temperature, land-sea mask or orography have to agree. It is possible to have an ocean on top of a mountain. For an ocean grid-cell that is (partially) masked by the land-sea mask, its value will be (fractionally) ignored in the calculation of surface fluxes (potentially leading to a zero flux depending on land surface temperatures). For an ocean grid-cell that is NaN but not masked by the land-sea mask, its value is always ignored.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.AquaPlanet","page":"Function and type index","title":"SpeedyWeather.AquaPlanet","text":"AquaPlanet sea surface temperatures that are constant in time and longitude, but vary in latitude following a coslat². To be created like\n\nocean = AquaPlanet(spectral_grid, temp_equator=302, temp_poles=273)\n\nFields and options are\n\ntemp_equator::Any: [OPTION] Temperature on the Equator [K]\ntemp_poles::Any: [OPTION] Temperature at the poles [K]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.AquaPlanetMask","page":"Function and type index","title":"SpeedyWeather.AquaPlanetMask","text":"Land-sea mask with zero = sea everywhere.\n\nmask::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.BarotropicModel","page":"Function and type index","title":"SpeedyWeather.BarotropicModel","text":"The BarotropicModel contains all model components needed for the simulation of the barotropic vorticity equations. To be constructed like\n\nmodel = BarotropicModel(spectral_grid; kwargs...)\n\nwith spectral_grid::SpectralGrid used to initalize all non-default components passed on as keyword arguments, e.g. planet=Earth(spectral_grid). Fields, representing model components, are\n\nspectral_grid::SpectralGrid\ndevice_setup::Any\ngeometry::Any\nplanet::Any\natmosphere::Any\ncoriolis::Any\nforcing::Any\ndrag::Any\nparticle_advection::Any\ninitial_conditions::Any\nrandom_process::Any\ntime_stepping::Any\nspectral_transform::Any\nimplicit::Any\nhorizontal_diffusion::Any\noutput::Any\ncallbacks::Dict{Symbol, SpeedyWeather.AbstractCallback}\nfeedback::Any\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.BulkRichardsonDrag","page":"Function and type index","title":"SpeedyWeather.BulkRichardsonDrag","text":"Boundary layer drag coefficient from the bulk Richardson number, following Frierson, 2006, Journal of the Atmospheric Sciences.\n\nκ::Any: von Kármán constant [1]\nz₀::Any: roughness length [m]\nRi_c::Any: Critical Richardson number for stable mixing cutoff [1]\ndrag_max::Base.RefValue: Maximum drag coefficient, κ²/log(zₐ/z₀) for zₐ from reference temperature\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.CPU","page":"Function and type index","title":"SpeedyWeather.CPU","text":"CPU <: AbstractDevice\n\nIndicates that SpeedyWeather.jl runs on a single CPU \n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ClausiusClapeyron","page":"Function and type index","title":"SpeedyWeather.ClausiusClapeyron","text":"Parameters for computing saturation vapour pressure of water using the Clausis-Clapeyron equation,\n\ne(T) = e₀ * exp( -Lᵥ/Rᵥ * (1/T - 1/T₀)),\n\nwhere T is in Kelvin, Lᵥ the the latent heat of condensation and Rᵥ the gas constant of water vapour\n\ne₀::AbstractFloat: Saturation water vapour pressure at 0°C [Pa]\nT₀::AbstractFloat: 0°C in Kelvin\nLᵥ::AbstractFloat: Latent heat of condensation/vaporization of water [J/kg]\ncₚ::AbstractFloat: Specific heat at constant pressure [J/K/kg]\nR_vapour::AbstractFloat: Gas constant of water vapour [J/kg/K]\nR_dry::AbstractFloat: Gas constant of dry air [J/kg/K]\nLᵥ_Rᵥ::AbstractFloat: Latent heat of condensation divided by gas constant of water vapour [K]\nT₀⁻¹::AbstractFloat: Inverse of T₀, one over 0°C in Kelvin\nmol_ratio::AbstractFloat: Ratio of molecular masses [1] of water vapour over dry air (=Rdry/Rvapour).\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ClausiusClapeyron-Tuple{NF} where NF","page":"Function and type index","title":"SpeedyWeather.ClausiusClapeyron","text":"Functor: Saturation water vapour pressure as a function of temperature using the Clausius-Clapeyron equation,\n\ne(T) = e₀ * exp( -Lᵥ/Rᵥ * (1/T - 1/T₀)),\n\nwhere T is in Kelvin, Lᵥ the the latent heat of vaporization and Rᵥ the gas constant of water vapour, T₀ is 0˚C in Kelvin.\n\n\n\n\n\n","category":"method"},{"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::DateTime: current model time\nstart::DateTime: start time of simulation\nperiod::Second: period to integrate for, set in set_period!(::Clock, ::Dates.Period)\ntimestep_counter::Int64: Counting all time steps during simulation\nn_timesteps::Int64: number of time steps to integrate for, set in initialize!(::Clock, ::AbstractTimeStepper)\nΔt::Dates.Millisecond: Time step\n\n.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Clock-Tuple{SpeedyWeather.AbstractTimeStepper}","page":"Function and type index","title":"SpeedyWeather.Clock","text":"Clock(\n time_stepping::SpeedyWeather.AbstractTimeStepper;\n kwargs...\n) -> Clock\n\n\nCreate and initialize a clock from time_stepping\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.CloudTopOutput","page":"Function and type index","title":"SpeedyWeather.CloudTopOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"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. Most column vectors have nlayers entries, from [1] at the top to [end] at the lowermost model level at the planetary boundary layer.\n\nnlayers::Int64\nnbands_shortwave::Int64\nnbands_longwave::Int64\nij::Int64\njring::Int64\nlond::Any\nlatd::Any\nland_fraction::Any\norography::Any\nu::Any\nv::Any\ntemp::Any\nhumid::Any\nln_pres::Any\npres::Any\nu_tend::Any\nv_tend::Any\ntemp_tend::Any\nhumid_tend::Any\nflux_u_upward::Any\nflux_u_downward::Any\nflux_v_upward::Any\nflux_v_downward::Any\nflux_temp_upward::Any\nflux_temp_downward::Any\nflux_humid_upward::Any\nflux_humid_downward::Any\nrandom_value::Any\nboundary_layer_depth::Int64\nboundary_layer_drag::Any\nsurface_geopotential::Any\nsurface_u::Any\nsurface_v::Any\nsurface_temp::Any\nsurface_humid::Any\nsurface_wind_speed::Any\nskin_temperature_sea::Any\nskin_temperature_land::Any\nsoil_moisture_availability::Any\nsurface_air_density::Any\nsat_humid::Any\ndry_static_energy::Any\ntemp_virt::Any\ngeopot::Any\ncloud_top::Int64\nprecip_convection::Any\nprecip_large_scale::Any\ncos_zenith::Any\nalbedo::Any\noutgoing_longwave_radiation::Any\noutgoing_shortwave_radiation::Any\noptical_depth_shortwave::Any\noptical_depth_longwave::Any\na::Any\nb::Any\nc::Any\nd::Any\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ConstantOceanClimatology","page":"Function and type index","title":"SpeedyWeather.ConstantOceanClimatology","text":"Constant ocean climatology that reads monthly sea surface temperature fields from file, and interpolates them only for the initial conditions in time to be stored in the prognostic variables. It is therefore an ocean from climatology but without a seasonal cycle that is constant in time. To be created like\n\nocean = SeasonalOceanClimatology(spectral_grid)\n\nand the ocean time is set with initialize!(model, time=time). Fields and options are\n\npath::String: [OPTION] path to the folder containing the land-sea mask file, pkg path default\nfile::String: [OPTION] filename of sea surface temperatures\nvarname::String: [OPTION] Variable name in netcdf file\nfile_Grid::Type{<:AbstractGrid}: [OPTION] Grid the sea surface temperature file comes on\nmissing_value::Float64: [OPTION] The missing value in the data respresenting land\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ConvectiveHeating","page":"Function and type index","title":"SpeedyWeather.ConvectiveHeating","text":"Convective heating as defined by Lee and Kim, 2003, JAS implemented as convection parameterization. Fields are\n\nnlat::Int64\ntime_scale::Second: [OPTION] Qmax heating strength as 1K/timescale\np₀::Any: [OPTION] Pressure of maximum heating [hPa]\nσₚ::Any: [OPTION] Vertical extent of heating [hPa]\nθ₀::Any: [OPTION] Latitude of heating [˚N]\nσθ::Any: [OPTION] Latitudinal width of heating [˚]\nlat_mask::Vector\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ConvectivePrecipitationOutput","page":"Function and type index","title":"SpeedyWeather.ConvectivePrecipitationOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\nrate::SpeedyWeather.AbstractOutputVariable\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ConvectivePrecipitationRateOutput","page":"Function and type index","title":"SpeedyWeather.ConvectivePrecipitationRateOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.DeviceSetup","page":"Function and type index","title":"SpeedyWeather.DeviceSetup","text":"Holds information about the device the model is running on and workgroup size. \n\ndevice::SpeedyWeather.AbstractDevice: ::AbstractDevice, device the model is running on.\ndevice_KA::Any: ::KernelAbstractions.Device, device for use with KernelAbstractions\nn::Int64: workgroup size\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.DiagnosticVariables","page":"Function and type index","title":"SpeedyWeather.DiagnosticVariables","text":"All diagnostic variables.\n\ntrunc::Int64: Spectral resolution: Max degree of spherical harmonics (0-based)\nnlat_half::Int64: Grid resoltion: Number of latitude rings on one hemisphere (Equator incl.)\nnlayers::Int64: Number of vertical layers\nnparticles::Int64: Number of particles for particle advection\ntendencies::Tendencies: Tendencies (spectral and grid) of the prognostic variables\ngrid::GridVariables: Gridded prognostic variables\ndynamics::DynamicsVariables: Intermediate variables for the dynamical core\nphysics::PhysicsVariables: Global fields returned from physics parameterizations\nparticles::ParticleVariables{NF, ArrayType, ParticleVector, VectorType, Grid} where {NF, ArrayType, Grid, ParticleVector, VectorType}: Intermediate variables for the particle advection\ncolumn::ColumnVariables: Vertical column for the physics parameterizations\ntemp_average::Any: Average temperature of every horizontal layer [K]\nscale::Base.RefValue: Scale applied to vorticity and divergence\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.DiagnosticVariables-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.DiagnosticVariables","text":"DiagnosticVariables(\n SG::SpectralGrid;\n nbands_shortwave,\n nbands_longwave\n) -> DiagnosticVariables\n\n\nGenerator function.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.DivergenceOutput","page":"Function and type index","title":"SpeedyWeather.DivergenceOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.DryBettsMiller","page":"Function and type index","title":"SpeedyWeather.DryBettsMiller","text":"The simplified Betts-Miller convection scheme from Frierson, 2007, https://doi.org/10.1175/JAS3935.1 but with humidity set to zero. Fields and options are\n\nnlayers::Int64: number of vertical layers/levels\ntime_scale::Second: [OPTION] Relaxation time for profile adjustment\nsurface_temp::Any: [OPTION] Surface perturbation of temp to calculate the dry adiabat\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.DynamicsVariables","page":"Function and type index","title":"SpeedyWeather.DynamicsVariables","text":"Intermediate quantities for the dynamics of a given layer.\n\ntrunc::Int64\nnlat_half::Int64\nnlayers::Int64\na::Any: Multi-purpose a, 3D work array to be reused in various places\nb::Any: Multi-purpose b, 3D work array to be reused in various places\na_grid::Any: Multi-purpose a, 3D work array to be reused in various places\nb_grid::Any: Multi-purpose b, 3D work array to be reused in various places\na_2D::Any: Multi-purpose a, work array to be reused in various places\nb_2D::Any: Multi-purpose b, work array to be reused in various places\na_2D_grid::Any: Multi-purpose a, work array to be reused in various places\nb_2D_grid::Any: Multi-purpose b, work array to be reused in various places\nuv∇lnp::Any: Pressure flux (uₖ, vₖ)⋅∇ln(pₛ)\nuv∇lnp_sum_above::Any: Sum of Δσₖ-weighted uv∇lnp above\ndiv_sum_above::Any: Sum of div_weighted from top to k\ntemp_virt::Any: Virtual temperature [K], spectral for geopotential\ngeopot::Any: Geopotential [m²/s²] on full layers\nσ_tend::Any: Vertical velocity (dσ/dt), on half levels k+1/2 below, pointing to the surface (σ=1)\n∇lnp_x::Any: Zonal gradient of log surf pressure\n∇lnp_y::Any: Meridional gradient of log surf pressure\nu_mean_grid::Any: Vertical average of zonal velocity [m/s]\nv_mean_grid::Any: Vertical average of meridional velocity [m/s]\ndiv_mean_grid::Any: Vertical average of divergence [1/s], grid\ndiv_mean::Any: Vertical average of divergence [1/s], spectral\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.DynamicsVariables-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.DynamicsVariables","text":"DynamicsVariables(\n SG::SpectralGrid\n) -> DynamicsVariables{<:AbstractFloat, <:AbstractArray, <:AbstractArray, <:AbstractArray, <:AbstractArray, <:AbstractArray}\n\n\nGenerator function.\n\n\n\n\n\n","category":"method"},{"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::AbstractFloat: angular frequency of Earth's rotation [rad/s]\ngravity::AbstractFloat: gravitational acceleration [m/s^2]\ndaily_cycle::Bool: switch on/off daily cycle\nlength_of_day::Second: Seconds in a daily rotation\nseasonal_cycle::Bool: switch on/off seasonal cycle\nlength_of_year::Second: Seconds in an orbit around the sun\nequinox::DateTime: time of spring equinox (year irrelevant)\naxial_tilt::AbstractFloat: angle [˚] rotation axis tilt wrt to orbit\nsolar_constant::AbstractFloat: Total solar irradiance at the distance of 1 AU [W/m²]\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 <: AbstractAtmosphere, with the following physical/chemical characteristics. Keyword arguments are\n\nmol_mass_dry_air::AbstractFloat: molar mass of dry air [g/mol]\nmol_mass_vapour::AbstractFloat: molar mass of water vapour [g/mol]\nheat_capacity::AbstractFloat: specific heat at constant pressure cₚ [J/K/kg]\nR_gas::AbstractFloat: universal gas constant [J/K/mol]\nR_dry::AbstractFloat: specific gas constant for dry air [J/kg/K]\nR_vapour::AbstractFloat: specific gas constant for water vapour [J/kg/K]\nmol_ratio::AbstractFloat: Ratio of gas constants: dry air / water vapour, often called ε [1]\nμ_virt_temp::AbstractFloat: Virtual temperature Tᵥ calculation, Tᵥ = T(1 + μ*q), humidity q, absolute tempereature T\nκ::AbstractFloat: = R_dry/cₚ, gas const for air over heat capacity\nwater_density::AbstractFloat: water density [kg/m³]\nlatent_heat_condensation::AbstractFloat: latent heat of condensation [J/kg] for consistency with specific humidity [kg/kg]\nlatent_heat_sublimation::AbstractFloat: latent heat of sublimation [J/kg]\nstefan_boltzmann::AbstractFloat: stefan-Boltzmann constant [W/m²/K⁴]\npres_ref::AbstractFloat: surface reference pressure [Pa]\ntemp_ref::AbstractFloat: surface reference temperature [K]\nmoist_lapse_rate::AbstractFloat: reference moist-adiabatic temperature lapse rate [K/m]\ndry_lapse_rate::AbstractFloat: reference dry-adiabatic temperature lapse rate [K/m]\nlayer_thickness::AbstractFloat: layer thickness for the shallow water model [m]\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{<: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_fraction::Float64: fraction of highest wavenumbers to smooth\norography::AbstractGrid{NF} where NF<:AbstractFloat: height [m] on grid-point space.\ngeopot_surf::LowerTriangularArray{Complex{NF}, 1, Array{Complex{NF}, 1}} where NF<:AbstractFloat: surface geopotential, height*gravity [m²/s²]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Feedback","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?, default is isinteractive(), true in interactive REPL mode\ndebug::Bool: check for NaRs in the prognostic variables\noutput::Bool: write a progress.txt file? State synced with NetCDFOutput.output\nid::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.GPU","page":"Function and type index","title":"SpeedyWeather.GPU","text":"GPU <: AbstractDevice\n\nIndicates that SpeedyWeather.jl runs on a single GPU\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\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\nnlayers::Int64: number of vertical levels\nnpoints::Int64: total number of horizontal grid points\nradius::AbstractFloat: Planet's radius [m]\ncolat::Vector{Float64}: array of colatitudes in radians (0...π)\nlat::Vector{NF} where NF<:AbstractFloat: array of latitudes in radians (π...-π)\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 (0˚...360˚) for each grid point in ring order\nlatds::Vector{NF} where NF<:AbstractFloat: latitude (-90˚...˚90) for each grid point in ring order\nlons::Vector{NF} where NF<:AbstractFloat: longitude (0...2π) for each grid point in ring order\nlats::Vector{NF} where NF<:AbstractFloat: latitude (-π/2...π/2) 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\nfull_to_half_interpolation::Vector{NF} where NF<:AbstractFloat: Full to half levels interpolation\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Geometry-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.Geometry","text":"Geometry(SG::SpectralGrid) -> Geometry\n\n\nGenerator function for Geometry struct based on spectral_grid.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.GlobalSurfaceTemperatureCallback","page":"Function and type index","title":"SpeedyWeather.GlobalSurfaceTemperatureCallback","text":"Callback that records the global mean surface temperature on every time step\n\ntimestep_counter::Int64\ntemp::Vector\n\n.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.GridVariables","page":"Function and type index","title":"SpeedyWeather.GridVariables","text":"Transformed prognostic variables (and u, v, temp_virt) into grid-point space.\n\nnlat_half::Int64\nnlayers::Int64\nvor_grid::Any: Relative vorticity of the horizontal wind [1/s]\ndiv_grid::Any: Divergence of the horizontal wind [1/s]\ntemp_grid::Any: Absolute temperature [K]\ntemp_virt_grid::Any: Virtual tempereature [K]\nhumid_grid::Any: Specific_humidity [kg/kg]\nu_grid::Any: Zonal velocity [m/s]\nv_grid::Any: Meridional velocity [m/s]\npres_grid::Any: Logarithm of surface pressure [Pa]\nrandom_pattern::Any: Random pattern controlled by random process [1]\ntemp_grid_prev::Any: Absolute temperature [K] at previous time step\nhumid_grid_prev::Any: Specific humidity [kg/kg] at previous time step\nu_grid_prev::Any: Zonal velocity [m/s] at previous time step\nv_grid_prev::Any: Meridional velocity [m/s] at previous time step\npres_grid_prev::Any: Logarithm of surface pressure [Pa] at previous time step\n\n.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.GridVariables-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.GridVariables","text":"GridVariables(\n SG::SpectralGrid\n) -> GridVariables{<:AbstractFloat, <:AbstractArray, <:AbstractArray, <:AbstractArray}\n\n\nGenerator function.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.HeldSuarez","page":"Function and type index","title":"SpeedyWeather.HeldSuarez","text":"Temperature relaxation from Held and Suarez, 1996 BAMS\n\nnlat::Int64: number of latitude rings\nnlayers::Int64: number of vertical levels\nσb::AbstractFloat: sigma coordinate below which faster surface relaxation is applied\nrelax_time_slow::Second: time scale for slow global relaxation\nrelax_time_fast::Second: time scale for faster tropical surface relaxation\nTmin::AbstractFloat: minimum equilibrium temperature [K]\nTmax::AbstractFloat: maximum equilibrium temperature [K]\nΔTy::AbstractFloat: meridional temperature gradient [K]\nΔθz::AbstractFloat: 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...) -> HeldSuarez\n\n\ncreate a HeldSuarez temperature relaxation with arrays allocated given spectral_grid\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.HumidityOutput","page":"Function and type index","title":"SpeedyWeather.HumidityOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.HyperDiffusion","page":"Function and type index","title":"SpeedyWeather.HyperDiffusion","text":"Horizontal hyper diffusion of vor, div, temp, humid; implicitly in spectral space with a power of the Laplacian (default = 4) and the strength controlled by time_scale (default = 1 hour). For vorticity and divergence, by default, the time_scale (=1/strength of diffusion) is reduced with increasing resolution through resolution_scaling and the power is linearly decreased in the vertical above the tapering_σ sigma level to power_stratosphere (default 2). \n\nFor the BarotropicModel and ShallowWaterModel no tapering or scaling is applied. Fields and options are\n\ntrunc::Int64: spectral resolution\nnlayers::Int64: number of vertical levels\npower::Any: [OPTION] power of Laplacian\ntime_scale::Second: [OPTION] diffusion time scale\ntime_scale_div::Second: [OPTION] diffusion time scale for temperature and humidity\nresolution_scaling::Any: [OPTION] stronger diffusion with resolution? 0: constant with trunc, 1: (inverse) linear with trunc, etc\npower_stratosphere::Any: [OPTION] different power for tropopause/stratosphere\ntapering_σ::Any: [OPTION] linearly scale towards power_stratosphere above this σ\nexpl::Any\nimpl::Any\nexpl_div::Any\nimpl_div::Any\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) -> HyperDiffusion{<:AbstractFloat}\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.ImplicitCondensation","page":"Function and type index","title":"SpeedyWeather.ImplicitCondensation","text":"Large scale condensation as with implicit precipitation.\n\nrelative_humidity_threshold::AbstractFloat: Relative humidity threshold [1 = 100%] to trigger condensation\ntime_scale::AbstractFloat: Time scale in multiples of time step Δt, the larger the less immediate\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ImplicitPrimitiveEquation","page":"Function and type index","title":"SpeedyWeather.ImplicitPrimitiveEquation","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\nnlayers::Int64: number of vertical layers\nα::AbstractFloat: 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 on first time step\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.ImplicitPrimitiveEquation-Tuple{SpectralGrid, Vararg{Any}}","page":"Function and type index","title":"SpeedyWeather.ImplicitPrimitiveEquation","text":"ImplicitPrimitiveEquation(\n spectral_grid::SpectralGrid,\n kwargs...\n) -> ImplicitPrimitiveEquation\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α::AbstractFloat: [OPTION] coefficient for semi-implicit computations to filter gravity waves, 0.5 <= α <= 1\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) -> ImplicitShallowWater\n\n\nGenerator using the resolution from spectral_grid.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.InterfaceDisplacementOutput","page":"Function and type index","title":"SpeedyWeather.InterfaceDisplacementOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"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(\n SG::SpectralGrid;\n kwargs...\n) -> JablonowskiRelaxation\n\n\ncreate a JablonowskiRelaxation temperature relaxation with arrays allocated given spectral_grid\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.JablonowskiTemperature","page":"Function and type index","title":"SpeedyWeather.JablonowskiTemperature","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\nσ_tropopause::Float64: Sigma coordinates of the tropopause [1]\nu₀::Float64: max amplitude of zonal wind [m/s]\nΔT::Float64: temperature difference used for stratospheric lapse rate [K], Jablonowski uses ΔT = 4.8e5 [K]\nTmin::Float64: minimum temperature [K] of profile\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.JeevanjeeRadiation","page":"Function and type index","title":"SpeedyWeather.JeevanjeeRadiation","text":"Temperature flux longwave radiation from Jeevanjee and Romps, 2018, following Seeley and Wordsworth, 2023, eq (1)\n\ndF/dT = α*(T_t - T)\n\nwith F the upward temperature flux between two layers with temperature difference dT, α = 0.025 W/m²/K² and T_t = 200K a prescribed tropopause temperature. Flux into the lowermost layer is 0. Flux out of uppermost layer also 0, but dT/dt = (T_t - T)/τ is added to relax the uppermost layer towards the tropopause temperature T_t with time scale τ = 24h (Seeley and Wordsworth, 2023 use 6h, which is unstable a low resolutions here). Fields are\n\nα::Any: Radiative forcing constant (W/m²/K²)\ntemp_tropopause::Any: Tropopause temperature [K]\ntime_scale::Second: Tropopause relaxation time scale to temp_tropopause\n\n\n\n\n\n","category":"type"},{"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\nnlayers::Int64: Number of vertical layers\nlatitude::Any: jet latitude [˚N]\nwidth::Any: jet width [˚], default ≈ 19.29˚\nsigma::Any: sigma level [1], vertical location of jet\nspeed::Any: jet speed scale [m/s]\ntime_scale::Second: time scale [days]\namplitude::Vector: precomputed amplitude vector [m/s²]\ntapering::Vector: precomputed vertical tapering\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{<:AbstractGrid}: Grid the land-sea mask file comes on\nmask::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.LargeScalePrecipitationOutput","page":"Function and type index","title":"SpeedyWeather.LargeScalePrecipitationOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\nrate::SpeedyWeather.AbstractOutputVariable\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.LargeScalePrecipitationRateOutput","page":"Function and type index","title":"SpeedyWeather.LargeScalePrecipitationRateOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"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)\nnsteps::Int64: Number of timesteps stored simultaneously in prognostic variables\nΔt_at_T31::Second: Time step in minutes for T31, scale linearly to trunc\nradius::AbstractFloat: Radius of sphere [m], used for scaling\nadjust_with_output::Bool: Adjust ΔtatT31 with the outputdt to reach outputdt exactly in integer time steps\nrobert_filter::AbstractFloat: Robert (1966) time filter coefficeint to suppress comput. mode\nwilliams_filter::AbstractFloat: Williams time filter (Amezcua 2011) coefficient for 3rd order acc\nΔt_millisec::Dates.Millisecond: time step Δt [ms] at specified resolution\nΔt_sec::AbstractFloat: time step Δt [s] at specified resolution\nΔt::AbstractFloat: time step Δt [s/m] at specified resolution, scaled by 1/radius\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...) -> Leapfrog\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\nnlayers::Int64\nσb::AbstractFloat\ntime_scale::Second\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...) -> LinearDrag\n\n\nGenerator function using nlayers from SG::SpectralGrid\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.MeridionalVelocityOutput","page":"Function and type index","title":"SpeedyWeather.MeridionalVelocityOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.NetCDFOutput","page":"Function and type index","title":"SpeedyWeather.NetCDFOutput","text":"NetCDFOutput(\n S::SpectralGrid;\n ...\n) -> NetCDFOutput{_A, _B, Interpolator} where {_A, _B, Interpolator<:(AnvilInterpolator{Float32})}\nNetCDFOutput(\n S::SpectralGrid,\n Model::Type{<:AbstractModel};\n output_Grid,\n nlat_half,\n output_NF,\n output_dt,\n kwargs...\n) -> NetCDFOutput{_A, _B, Interpolator} where {_A, _B, Interpolator<:(AnvilInterpolator{Float32})}\n\n\nConstructor for NetCDFOutput based on S::SpectralGrid and optionally the Model type (e.g. ShallowWater, PrimitiveWet) as second positional argument. The output grid is optionally determined by keyword arguments output_Grid (its type, full grid required), nlat_half (resolution) and output_NF (number format). By default, uses the full grid equivalent of the grid and resolution used in SpectralGrid S.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.NetCDFOutput-2","page":"Function and type index","title":"SpeedyWeather.NetCDFOutput","text":"Output writer for a netCDF file with (re-)gridded variables. Interpolates non-rectangular grids. Fields are\n\nactive::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::DateTime\noutput_dt::Second: [OPTION] output frequency, time step\nvariables::Dict{Symbol, SpeedyWeather.AbstractOutputVariable}: [OPTION] dictionary of variables to output, e.g. u, v, vor, div, pres, temp, humid\noutput_every_n_steps::Int64\ntimestep_counter::Int64\noutput_counter::Int64\nnetcdf_file::Union{Nothing, NCDatasets.NCDataset}\ninterpolator::Any\ngrid2D::Any\ngrid3D::Any\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.NoCallback","page":"Function and type index","title":"SpeedyWeather.NoCallback","text":"Dummy callback that doesn't do anything.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.NoConvection","page":"Function and type index","title":"SpeedyWeather.NoConvection","text":"Dummy type to disable convection.\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::AbstractGrid{NF} where NF<:AbstractFloat: height [m] on grid-point space.\ngeopot_surf::LowerTriangularArray{Complex{NF}, 1, Array{Complex{NF}, 1}} where NF<:AbstractFloat: surface geopotential, height*gravity [m²/s²]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.NoOutputVariable","page":"Function and type index","title":"SpeedyWeather.NoOutputVariable","text":"Dummy output variable that doesn't do anything.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.NoRandomProcess","page":"Function and type index","title":"SpeedyWeather.NoRandomProcess","text":"Dummy type for no random process.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.NoSurfacePerturbation","page":"Function and type index","title":"SpeedyWeather.NoSurfacePerturbation","text":"Returns the surface temperature and humidity without perturbation from the lowermost layer.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.NoSurfacePerturbation-Tuple{ColumnVariables, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.NoSurfacePerturbation","text":"Returns the surface temperature and humidity without perturbation from the lowermost layer. Used as a functor.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.OrographyOutput","page":"Function and type index","title":"SpeedyWeather.OrographyOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.OutgoingLongwaveRadiationOutput","page":"Function and type index","title":"SpeedyWeather.OutgoingLongwaveRadiationOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.OutgoingShortwaveRadiationOutput","page":"Function and type index","title":"SpeedyWeather.OutgoingShortwaveRadiationOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Particle","page":"Function and type index","title":"SpeedyWeather.Particle","text":"Particle with location lon (longitude), lat (latitude) and σ (vertical coordinate). Longitude is assumed to be in [0,360˚E), latitude in [-90˚,90˚N] and σ in [0,1] but not strictly enforced at creation, see mod(::Particle) and ismod(::Particle). A particle is either active or inactive, determined by the Boolean in it's 2nd type parameter. By default, a particle is active, of number format DEFAULT_NF and at 0˚N, 0˚E, σ=0.\n\nlon::AbstractFloat: longitude in [0,360˚]\nlat::AbstractFloat: latitude [-90˚,90˚]\nσ::AbstractFloat: vertical sigma coordinate [0 (top) to 1 (surface)]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ParticleTracker","page":"Function and type index","title":"SpeedyWeather.ParticleTracker","text":"A ParticleTracker is implemented as a callback to output the trajectories of particles from particle advection. To be added like\n\nadd!(model.callbacks, ParticleTracker(spectral_grid; kwargs...))\n\nOutput done via netCDF. Fields and options are\n\nschedule::Schedule: [OPTION] when to schedule particle tracking\nfile_name::String: [OPTION] File name for netCDF file\ncompression_level::Int64: [OPTION] lossless compression level; 1=low but fast, 9=high but slow\nshuffle::Bool: [OPTION] shuffle/bittranspose filter for compression\nkeepbits::Int64: [OPTION] mantissa bits to keep, (14, 15, 16) means at least (2km, 1km, 500m) accurate locations\nnparticles::Int64: Number of particles to track\nnetcdf_file::Union{Nothing, NCDatasets.NCDataset}: The netcdf file to be written into, will be created at initialization\nlon::Vector\nlat::Vector\nσ::Vector\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ParticleVariables","page":"Function and type index","title":"SpeedyWeather.ParticleVariables","text":"Diagnostic variables for the particle advection\n\nnparticles::Int64: Number of particles\nnlat_half::Int64: Number of latitudes on one hemisphere (Eq. incld.), resolution parameter of Grid\nlocations::Any: Work array: particle locations\nu::Any: Work array: velocity u\nv::Any: Work array: velocity v\nσ_tend::Any: Work array: velocity w = dσ/dt\ninterpolator::AnvilInterpolator{NF, Grid} where {NF, Grid}: Interpolator to interpolate velocity fields onto particle positions\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ParticleVariables-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.ParticleVariables","text":"ParticleVariables(\n SG::SpectralGrid\n) -> ParticleVariables{var\"#s252\", var\"#s251\", var\"#s146\", _A, <:AbstractGrid} where {var\"#s252\"<:AbstractFloat, var\"#s251\"<:AbstractArray, var\"#s146\"<:AbstractArray, _A}\n\n\nGenerator function.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.PhysicsVariables","page":"Function and type index","title":"SpeedyWeather.PhysicsVariables","text":"Diagnostic variables of the physical parameterizations.\n\nnlat_half::Int64\nprecip_large_scale::Any: Accumulated large-scale precipitation [m]\nprecip_convection::Any: Accumulated large-scale precipitation [m]\ncloud_top::Any: Cloud top [m]\nsoil_moisture_availability::Any: Availability of soil moisture to evaporation [1]\nsurface_flux_heat::Any: Surface flux of heat [W/m^2]\nsurface_flux_humid::Any: Surface flux of humidity [?]\noutgoing_shortwave_radiation::Any: Outgoing shortwave radiation [W/m^2]\noutgoing_longwave_radiation::Any: Outgoing longwave radiation [W/m^2]\ncos_zenith::Any: Cosine of solar zenith angle [1]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.PhysicsVariables-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.PhysicsVariables","text":"PhysicsVariables(\n SG::SpectralGrid\n) -> PhysicsVariables{<:AbstractFloat, <:AbstractArray, <:AbstractArray}\n\n\nGenerator function.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.PrimitiveDryModel","page":"Function and type index","title":"SpeedyWeather.PrimitiveDryModel","text":"The PrimitiveDryModel contains all model components (themselves structs) needed for the simulation of the primitive equations without humidity. To be constructed like\n\nmodel = PrimitiveDryModel(spectral_grid; kwargs...)\n\nwith spectral_grid::SpectralGrid used to initalize all non-default components passed on as keyword arguments, e.g. planet=Earth(spectral_grid). Fields, representing model components, are\n\nspectral_grid::SpectralGrid\ndevice_setup::Any\ndynamics::Bool\ngeometry::Any\nplanet::Any\natmosphere::Any\ncoriolis::Any\ngeopotential::Any\nadiabatic_conversion::Any\nparticle_advection::Any\ninitial_conditions::Any\nrandom_process::Any\nforcing::Any\ndrag::Any\norography::Any\nland_sea_mask::Any\nocean::Any\nland::Any\nsolar_zenith::Any\nalbedo::Any\nphysics::Bool\nboundary_layer_drag::Any\ntemperature_relaxation::Any\nvertical_diffusion::Any\nsurface_thermodynamics::Any\nsurface_wind::Any\nsurface_heat_flux::Any\nconvection::Any\noptical_depth::Any\nshortwave_radiation::Any\nlongwave_radiation::Any\nstochastic_physics::Any\ntime_stepping::Any\nspectral_transform::Any\nimplicit::Any\nhorizontal_diffusion::Any\nvertical_advection::Any\noutput::Any\ncallbacks::Dict{Symbol, SpeedyWeather.AbstractCallback}\nfeedback::Any\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.PrimitiveWetModel","page":"Function and type index","title":"SpeedyWeather.PrimitiveWetModel","text":"The PrimitiveWetModel contains all model components (themselves structs) needed for the simulation of the primitive equations with humidity. To be constructed like\n\nmodel = PrimitiveWetModel(spectral_grid; kwargs...)\n\nwith spectral_grid::SpectralGrid used to initalize all non-default components passed on as keyword arguments, e.g. planet=Earth(spectral_grid). Fields, representing model components, are\n\nspectral_grid::SpectralGrid\ndevice_setup::Any\ndynamics::Bool\ngeometry::Any\nplanet::Any\natmosphere::Any\ncoriolis::Any\ngeopotential::Any\nadiabatic_conversion::Any\nparticle_advection::Any\ninitial_conditions::Any\nrandom_process::Any\nforcing::Any\ndrag::Any\norography::Any\nland_sea_mask::Any\nocean::Any\nland::Any\nsolar_zenith::Any\nalbedo::Any\nsoil::Any\nvegetation::Any\nphysics::Bool\nclausius_clapeyron::Any\nboundary_layer_drag::Any\ntemperature_relaxation::Any\nvertical_diffusion::Any\nsurface_thermodynamics::Any\nsurface_wind::Any\nsurface_heat_flux::Any\nsurface_evaporation::Any\nlarge_scale_condensation::Any\nconvection::Any\noptical_depth::Any\nshortwave_radiation::Any\nlongwave_radiation::Any\nstochastic_physics::Any\ntime_stepping::Any\nspectral_transform::Any\nimplicit::Any\nhorizontal_diffusion::Any\nvertical_advection::Any\nhole_filling::Any\noutput::Any\ncallbacks::Dict{Symbol, SpeedyWeather.AbstractCallback}\nfeedback::Any\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.PrognosticVariables-Tuple{SpectralGrid, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.PrognosticVariables","text":"PrognosticVariables(\n SG::SpectralGrid,\n model::AbstractModel\n) -> PrognosticVariables{var\"#s252\", var\"#s251\", _A, <:AbstractArray, <:AbstractArray, <:AbstractArray, <:AbstractArray} where {var\"#s252\"<:AbstractFloat, var\"#s251\"<:AbstractArray, _A}\n\n\nGenerator function.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.PrognosticVariables-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.PrognosticVariables","text":"PrognosticVariables(\n SG::SpectralGrid;\n nsteps\n) -> PrognosticVariables{var\"#s252\", var\"#s251\", _A, <:AbstractArray, <:AbstractArray, <:AbstractArray, <:AbstractArray} where {var\"#s252\"<:AbstractFloat, var\"#s251\"<:AbstractArray, _A}\n\n\nGenerator function.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.RandomPatternOutput","page":"Function and type index","title":"SpeedyWeather.RandomPatternOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\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.RossbyHaurwitzWave","page":"Function and type index","title":"SpeedyWeather.RossbyHaurwitzWave","text":"Rossby-Haurwitz wave initial conditions as in Williamson et al. 1992, J Computational Physics with an additional cut-off amplitude c to filter out tiny harmonics in the vorticity field. Parameters are \n\nm::Int64\nω::Float64\nK::Float64\nc::Float64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Schedule","page":"Function and type index","title":"SpeedyWeather.Schedule","text":"A schedule for callbacks, to execute them periodically after a given period has passed (first timestep excluded) or on/after specific times (initial time excluded). For two consecutive time steps i, i+1, an event is scheduled at i+1 when it occurs in (i,i+1]. Similarly, a periodic schedule of period p will be executed at start+p, but p is rounded to match the multiple of the model timestep. Periodic schedules and event schedules can be combined, executing at both. A Schedule is supposed to be added into callbacks as fields\n\nBase.@kwdef struct MyCallback\n schedule::Schedule = Schedule(every=Day(1))\n other_fields\nend\n\nsee also initialize!(::Schedule,::Clock) and isscheduled(::Schedule,::Clock). Fields\n\nevery::Second: [OPTION] Execute every time period, first timestep excluded. Default=never.\ntimes::Vector{DateTime}: [OPTION] Events scheduled at times\nschedule::BitVector: Actual schedule, true=execute this timestep, false=don't\nsteps::Int64: Number of scheduled executions\ncounter::Int64: Count up the number of executions\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Schedule-Tuple{Vararg{DateTime}}","page":"Function and type index","title":"SpeedyWeather.Schedule","text":"Schedule(times::DateTime...) -> Schedule\n\n\nA Schedule based on DateTime arguments, For two consecutive time steps i, i+1, an event is scheduled at i+1 when it occurs in (i,i+1].\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SeaSurfaceTemperatureOutput","page":"Function and type index","title":"SpeedyWeather.SeaSurfaceTemperatureOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SeasonalOceanClimatology","page":"Function and type index","title":"SpeedyWeather.SeasonalOceanClimatology","text":"Seasonal ocean climatology that reads monthly sea surface temperature fields from file, and interpolates them in time regularly (default every 3 days) to be stored in the prognostic variables. Fields and options are\n\nnlat_half::Int64: number of latitudes on one hemisphere, Equator included\npath::String: [OPTION] Path to the folder containing the sea surface temperatures, pkg path default\nfile::String: [OPTION] Filename of sea surface temperatures\nvarname::String: [OPTION] Variable name in netcdf file\nfile_Grid::Type{<:AbstractGrid}: [OPTION] Grid the sea surface temperature file comes on\nmissing_value::Any: [OPTION] The missing value in the data respresenting land\nmonthly_temperature::Vector{Grid} where {NF, Grid<:AbstractGrid{NF}}: Monthly sea surface temperatures [K], interpolated onto Grid\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ShallowWaterModel","page":"Function and type index","title":"SpeedyWeather.ShallowWaterModel","text":"The ShallowWaterModel contains all model components needed for the simulation of the shallow water equations. To be constructed like\n\nmodel = ShallowWaterModel(spectral_grid; kwargs...)\n\nwith spectral_grid::SpectralGrid used to initalize all non-default components passed on as keyword arguments, e.g. planet=Earth(spectral_grid). Fields, representing model components, are\n\nspectral_grid::SpectralGrid\ndevice_setup::Any\ngeometry::Any\nplanet::Any\natmosphere::Any\ncoriolis::Any\norography::Any\nforcing::Any\ndrag::Any\nparticle_advection::Any\ninitial_conditions::Any\nrandom_process::Any\ntime_stepping::Any\nspectral_transform::Any\nimplicit::Any\nhorizontal_diffusion::Any\noutput::Any\ncallbacks::Dict{Symbol, SpeedyWeather.AbstractCallback}\nfeedback::Any\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SimplifiedBettsMiller","page":"Function and type index","title":"SpeedyWeather.SimplifiedBettsMiller","text":"The simplified Betts-Miller convection scheme from Frierson, 2007, https://doi.org/10.1175/JAS3935.1. This implements the qref-formulation in their paper. Fields and options are\n\nnlayers::Int64: number of vertical layers\ntime_scale::Second: [OPTION] Relaxation time for profile adjustment\nrelative_humidity::Any: [OPTION] Relative humidity for reference profile\nsurface_temp_humid::Any: [OPTION] Surface perturbation of temp, humid to calculate the moist pseudo adiabat\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::AbstractModel: all parameters, constant at runtime\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SinSolarDeclination","page":"Function and type index","title":"SpeedyWeather.SinSolarDeclination","text":"Coefficients to calculate the solar declination angle δ [radians] based on a simple sine function, with Earth's axial tilt as amplitude, equinox as phase shift.\n\naxial_tilt::Any\nequinox::DateTime\nlength_of_year::Second\nlength_of_day::Second\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SinSolarDeclination-Tuple{NF} where NF","page":"Function and type index","title":"SpeedyWeather.SinSolarDeclination","text":"SinSolarDeclination functor, computing the solar declination angle of angular fraction of year g [radians] using the coefficients of the SinSolarDeclination struct.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SinSolarDeclination-Tuple{SpectralGrid, SpeedyWeather.AbstractPlanet}","page":"Function and type index","title":"SpeedyWeather.SinSolarDeclination","text":"Generator function using the planet's orbital parameters to adapt the solar declination calculation.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SinSolarDeclination-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.SinSolarDeclination","text":"Generator function pulling the number format NF from a SpectralGrid.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SolarDeclination","page":"Function and type index","title":"SpeedyWeather.SolarDeclination","text":"Coefficients to calculate the solar declination angle δ from\n\nδ = 0.006918 - 0.399912*cos(g) + 0.070257*sin(g)\n - 0.006758*cos(2g) + 0.000907*sin(2g)\n - 0.002697*cos(3g) + 0.001480*sin(3g)\n\nwith g the angular fraction of the year in radians. Following Spencer 1971, Fourier series representation of the position of the sun. Search 2(5):172.\n\na::AbstractFloat\ns1::AbstractFloat\nc1::AbstractFloat\ns2::AbstractFloat\nc2::AbstractFloat\ns3::AbstractFloat\nc3::AbstractFloat\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SolarDeclination-Tuple{Any}","page":"Function and type index","title":"SpeedyWeather.SolarDeclination","text":"SolarDeclination functor, computing the solar declination angle of angular fraction of year g [radians] using the coefficients of the SolarDeclination struct.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SolarDeclination-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.SolarDeclination","text":"Generator function pulling the number format NF from a SpectralGrid.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SolarTimeCorrection","page":"Function and type index","title":"SpeedyWeather.SolarTimeCorrection","text":"Coefficients for the solar time correction (also called Equation of time) which adjusts the solar hour to an oscillation of sunrise/set by about +-16min throughout the year.\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SolarTimeCorrection-Tuple{Any}","page":"Function and type index","title":"SpeedyWeather.SolarTimeCorrection","text":"Functor that returns the time correction for a angular fraction of the year g [radians], so that g=0 for Jan-01 and g=2π for Dec-31.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SolarZenith","page":"Function and type index","title":"SpeedyWeather.SolarZenith","text":"Solar zenith angle varying with daily and seasonal cycle.\n\nlength_of_day::Second\nlength_of_year::Second\nequation_of_time::Bool\nseasonal_cycle::Bool\nsolar_declination::SpeedyWeather.SinSolarDeclination{NF} where NF<:AbstractFloat\ntime_correction::SpeedyWeather.SolarTimeCorrection\ninitial_time::Base.RefValue{DateTime}\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SolarZenithSeason","page":"Function and type index","title":"SpeedyWeather.SolarZenithSeason","text":"Solar zenith angle varying with seasonal cycle only.\n\nlength_of_day::Second\nlength_of_year::Second\nseasonal_cycle::Bool\nsolar_declination::SpeedyWeather.SinSolarDeclination{NF} where NF<:AbstractFloat\ninitial_time::Base.RefValue{DateTime}\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SpectralAR1Process","page":"Function and type index","title":"SpeedyWeather.SpectralAR1Process","text":"First-order auto-regressive random process (AR1) in spectral space, evolving wavenumbers with respectice time_scales and standard_deviations independently. Transformed after every time step to grid space with a clamp applied to limit extrema. For reproducability seed can be provided and an independent random_number_generator is used that is reseeded on every initialize!. Fields are \n\ntrunc::Int64\ntime_scale::Second: [OPTION] Time scale of the AR1 process\nwavenumber::Int64: [OPTION] Wavenumber of the AR1 process\nstandard_deviation::Any: [OPTION] Standard deviation of the AR1 process\nclamp::Tuple{NF, NF} where NF: [OPTION] Range to clamp values into after every transform into grid space\nseed::Int64: [OPTION] Random number generator seed, 0=randomly seed from Julia's GLOBAL_RNG\nrandom_number_generator::Random.Xoshiro: Independent random number generator for this random process\nautoregressive_factor::Base.RefValue: Precomputed auto-regressive factor [1], function of time scale and model time step\nnoise_factors::Any: Precomputed noise factors [1] for every total wavenumber l\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SpectralFilter","page":"Function and type index","title":"SpeedyWeather.SpectralFilter","text":"Spectral filter for horizontal diffusion. Fields are \n\ntrunc::Int64: spectral resolution\nnlayers::Int64: number of vertical levels\nshift::Any: [OPTION] shift diffusion to higher (positive shift) or lower (neg) wavenumbers, relative to trunc\nscale::Any: [OPTION] Scale-selectiveness, steepness of the sigmoid, higher is more selective\ntime_scale::Second: [OPTION] diffusion time scale\ntime_scale_div::Second: [OPTION] stronger diffusion time scale for divergence\nresolution_scaling::Any: [OPTION] resolution scaling to shorten time_scale with trunc\npower::Any: [OPTION] power of the tanh function\npower_div::Any: [OPTION] power of the tanh function for divergence\nexpl::Any\nimpl::Any\nexpl_div::Any\nimpl_div::Any\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SpectralFilter-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.SpectralFilter","text":"SpectralFilter(\n spectral_grid::SpectralGrid;\n kwargs...\n) -> SpectralFilter{<:AbstractFloat}\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.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}: [OPTION] number format used throughout the model\ndevice::SpeedyWeather.AbstractDevice: [OPTION] device archictecture to run on\nArrayType::Type{<:AbstractArray}: [OPTION] array type to use for all variables\ntrunc::Int64: [OPTION] horizontal resolution as the maximum degree of spherical harmonics\nGrid::Type{<:AbstractGrid}: [OPTION] horizontal grid used for calculations in grid-point space\ndealiasing::Float64: [OPTION] how to match spectral with grid resolution: dealiasing factor, 1=linear, 2=quadratic, 3=cubic grid\nradius::Float64: [OPTION] radius of the sphere [m]\nnparticles::Int64: [OPTION] number of particles for particle advection [1]\nnlat_half::Int64: number of latitude rings on one hemisphere (Equator incl)\nnlat::Int64: number of latitude rings on both hemispheres\nnpoints::Int64: total number of grid points in the horizontal\nnlayers::Int64: [OPTION] number of vertical levels\nvertical_coordinates::SpeedyWeather.VerticalCoordinates: [OPTION] coordinates used to discretize the vertical\nVectorType::Type{<:AbstractVector}\nMatrixType::Type{<:AbstractMatrix}\nSpectralVariable2D::Type{<:AbstractArray}\nSpectralVariable3D::Type{<:AbstractArray}\nSpectralVariable4D::Type{<:AbstractArray}\nGridVariable2D::Type{<:AbstractArray}\nGridVariable3D::Type{<:AbstractArray}\nGridVariable4D::Type{<:AbstractArray}\nParticleVector::Type{<:AbstractArray}\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.SpeedyTransforms.SpectralTransform-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.SpeedyTransforms.SpectralTransform","text":"SpectralTransform(\n spectral_grid::SpectralGrid;\n one_more_degree,\n kwargs...\n) -> SpectralTransform{NF, _A, _B, _C, _D, _E, LowerTriangularArray{NF1, 1, _A1}, LowerTriangularArray{NF2, 2, _A2}} where {NF<:AbstractFloat, _A, _B, _C, _D, _E, NF1<:AbstractFloat, _A1, NF2<:AbstractFloat, _A2}\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.SurfaceEvaporation","page":"Function and type index","title":"SpeedyWeather.SurfaceEvaporation","text":"Surface evaporation following a bulk formula with wind from model.surface_wind \n\nuse_boundary_layer_drag::Bool: Use column.boundarylayerdrag coefficient\nmoisture_exchange_land::AbstractFloat: Otherwise, use the following drag coefficient for evaporation over land\nmoisture_exchange_sea::AbstractFloat: Or drag coefficient for evaporation over sea\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SurfaceFluxHeatOutput","page":"Function and type index","title":"SpeedyWeather.SurfaceFluxHeatOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SurfaceFluxHumidOutput","page":"Function and type index","title":"SpeedyWeather.SurfaceFluxHumidOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.SurfacePressureOutput","page":"Function and type index","title":"SpeedyWeather.SurfacePressureOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.TemperatureOutput","page":"Function and type index","title":"SpeedyWeather.TemperatureOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Tendencies","page":"Function and type index","title":"SpeedyWeather.Tendencies","text":"Tendencies of the prognostic variables in spectral and grid-point space\n\ntrunc::Int64\nnlat_half::Int64\nnlayers::Int64\nvor_tend::Any: Vorticity of horizontal wind field [1/s]\ndiv_tend::Any: Divergence of horizontal wind field [1/s]\ntemp_tend::Any: Absolute temperature [K]\nhumid_tend::Any: Specific humidity [kg/kg]\nu_tend::Any: Zonal velocity [m/s]\nv_tend::Any: Meridional velocity [m/s]\npres_tend::Any: Logarithm of surface pressure [Pa]\nu_tend_grid::Any: Zonal velocity [m/s], grid\nv_tend_grid::Any: Meridinoal velocity [m/s], grid\ntemp_tend_grid::Any: Absolute temperature [K], grid\nhumid_tend_grid::Any: Specific humidity [kg/kg], grid\npres_tend_grid::Any: Logarith of surface pressure [Pa], grid\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.Tendencies-Tuple{SpectralGrid}","page":"Function and type index","title":"SpeedyWeather.Tendencies","text":"Tendencies(\n SG::SpectralGrid\n) -> Tendencies{<:AbstractFloat, <:AbstractArray, <:AbstractArray, <:AbstractArray, <:AbstractArray, <:AbstractArray}\n\n\nGenerator function.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.TetensEquation","page":"Function and type index","title":"SpeedyWeather.TetensEquation","text":"Parameters for computing saturation vapour pressure of water using the Tetens' equation,\n\neᵢ(T) = e₀ * exp(Cᵢ * (T - T₀) / (T + Tᵢ)),\n\nwhere T is in Kelvin and i = 1, 2 for saturation above and below freezing, respectively. From Tetens (1930), and Murray (1967) for below freezing.\n\ne₀::AbstractFloat: Saturation water vapour pressure at 0°C [Pa]\nT₀::AbstractFloat: 0°C in Kelvin\nT₁::AbstractFloat: Tetens denominator (water) [˚C]\nT₂::AbstractFloat: Tetens denominator following Murray (1967, below freezing) [˚C]\nC₁::AbstractFloat: Tetens numerator scaling [1], above freezing\nC₂::AbstractFloat: Tetens numerator scaling [1], below freezing\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.TetensEquation-Tuple{NF} where NF","page":"Function and type index","title":"SpeedyWeather.TetensEquation","text":"Functor: Saturation water vapour pressure as a function of temperature using the Tetens equation,\n\neᵢ(T) = e₀ * exp(Cᵢ * (T - T₀) / (T - Tᵢ)),\n\nwhere T is in Kelvin and i = 1, 2 for saturation above and below freezing, respectively.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.UniformCooling","page":"Function and type index","title":"SpeedyWeather.UniformCooling","text":"Uniform cooling following Paulius and Garner, 2006. JAS. https://doi.org/10.1175/JAS3705.1 imposing a default temperature tendency of -1.5K/day (=1K/16hours for a time_scale of 16 hours) on every level except for the stratosphere (diagnosed as temp < temp_min) where a relaxation term with time_scale_stratosphere towards temp_stratosphere is applied.\n\ndT/dt = -1.5K/day for T > 207.5K else (200K-T) / 5 days\n\nFields are\n\ntime_scale::Second: [OPTION] time scale of cooling, default = -1.5K/day = -1K/16hrs\ntemp_min::Any: [OPTION] temperature [K] below which stratospheric relaxation is applied\ntemp_stratosphere::Any: [OPTION] target temperature [K] of stratospheric relaxation\ntime_scale_stratosphere::Second: [OPTION] time scale of stratospheric relaxation\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.VorticityOutput","page":"Function and type index","title":"SpeedyWeather.VorticityOutput","text":"Defines netCDF output of vorticity. Fields are\n\nname::String: [Required] short name of variable (unique) used in netCDF file and key for dictionary\nunit::String: [Required] unit of variable\nlong_name::String: [Required] long name of variable used in netCDF file\ndims_xyzt::NTuple{4, Bool}: [Required] NetCDF dimensions the variable uses, lon, lat, layer, time\nmissing_value::Float64: [Optional] missing value for the variable, if not specified uses NaN\ncompression_level::Int64: [Optional] compression level of the lossless compressor, 1=lowest/fastest, 9=highest/slowest, 3=default\nshuffle::Bool: [Optional] bitshuffle the data for compression, false = default\nkeepbits::Int64: [Optional] number of mantissa bits to keep for compression (default: 15)\n\nCustom variable output defined similarly with required fields marked, optional fields otherwise use variable-independent defaults. Initialize with VorticityOutput() and non-default fields can always be passed on as keyword arguments, e.g. VorticityOutput(long_name=\"relative vorticity\", compression_level=0).\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ZonalJet","page":"Function and type index","title":"SpeedyWeather.ZonalJet","text":"A struct that contains all parameters for the Galewsky et al, 2004 zonal jet intitial conditions for the ShallowWaterModel. 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::AbstractGrid{NF} where NF<:AbstractFloat: height [m] on grid-point space.\ngeopot_surf::LowerTriangularArray{Complex{NF}, 1, Array{Complex{NF}, 1}} where NF<:AbstractFloat: surface geopotential, height*gravity [m²/s²]\n\n\n\n\n\n","category":"type"},{"location":"functions/#SpeedyWeather.ZonalVelocityOutput","page":"Function and type index","title":"SpeedyWeather.ZonalVelocityOutput","text":"Defines netCDF output for a specific variables, see VorticityOutput for details. Fields are \n\nname::String\nunit::String\nlong_name::String\ndims_xyzt::NTuple{4, Bool}\nmissing_value::Float64\ncompression_level::Int64\nshuffle::Bool\nkeepbits::Int64\n\n\n\n\n\n","category":"type"},{"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\n\n\n\n\n","category":"type"},{"location":"functions/#Base.copy!-Tuple{PrognosticVariables, PrognosticVariables}","page":"Function and type index","title":"Base.copy!","text":"copy!(\n progn_new::PrognosticVariables,\n progn_old::PrognosticVariables\n) -> PrognosticVariables\n\n\nCopies entries of progn_old into progn_new.\n\n\n\n\n\n","category":"method"},{"location":"functions/#Base.delete!-Tuple{NetCDFOutput, Vararg{Union{String, Symbol}}}","page":"Function and type index","title":"Base.delete!","text":"delete!(\n output::NetCDFOutput,\n keys::Union{String, Symbol}...\n) -> NetCDFOutput\n\n\nDelete output variables from output by their (short name) (Symbol or String), corresponding to the keys in the dictionary.\n\n\n\n\n\n","category":"method"},{"location":"functions/#Base.fill!-Tuple{Tendencies, Any, Type{<:Barotropic}}","page":"Function and type index","title":"Base.fill!","text":"fill!(\n tendencies::Tendencies,\n x,\n _::Type{<:Barotropic}\n) -> Tendencies\n\n\nSet the tendencies for the barotropic model to x.\n\n\n\n\n\n","category":"method"},{"location":"functions/#Base.fill!-Tuple{Tendencies, Any, Type{<:PrimitiveDry}}","page":"Function and type index","title":"Base.fill!","text":"fill!(\n tendencies::Tendencies,\n x,\n _::Type{<:PrimitiveDry}\n) -> Tendencies\n\n\nSet the tendencies for the primitive dry model to x.\n\n\n\n\n\n","category":"method"},{"location":"functions/#Base.fill!-Tuple{Tendencies, Any, Type{<:PrimitiveWet}}","page":"Function and type index","title":"Base.fill!","text":"fill!(\n tendencies::Tendencies,\n x,\n _::Type{<:PrimitiveWet}\n) -> Tendencies\n\n\nSet the tendencies for the primitive wet model to x.\n\n\n\n\n\n","category":"method"},{"location":"functions/#Base.fill!-Tuple{Tendencies, Any, Type{<:ShallowWater}}","page":"Function and type index","title":"Base.fill!","text":"fill!(\n tendencies::Tendencies,\n x,\n _::Type{<:ShallowWater}\n) -> Tendencies\n\n\nSet the tendencies for the shallow-water model to x.\n\n\n\n\n\n","category":"method"},{"location":"functions/#Base.mod-Tuple{P} where P<:Particle","page":"Function and type index","title":"Base.mod","text":"mod(p::Particle) -> Any\n\n\nModulo operator for particle locations to map them back into [0,360˚E) and [-90˚,90˚N], in the horizontal and to clamp vertical σ coordinates into [0,1].\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.CallbackDict-Tuple{Vararg{Pair{Symbol, <:SpeedyWeather.AbstractCallback}}}","page":"Function and type index","title":"SpeedyWeather.CallbackDict","text":"CallbackDict(\n pairs::Pair{Symbol, <:SpeedyWeather.AbstractCallback}...\n) -> Dict{Symbol, SpeedyWeather.AbstractCallback}\n\n\nCreate Callback dictionary like normal dictionaries.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.CallbackDict-Tuple{}","page":"Function and type index","title":"SpeedyWeather.CallbackDict","text":"CallbackDict(\n\n) -> Dict{Symbol, SpeedyWeather.AbstractCallback}\n\n\nEmpty Callback dictionary generator.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.DeviceArray-Tuple{CPU, Any}","page":"Function and type index","title":"SpeedyWeather.DeviceArray","text":"DeviceArray(_::CPU, x) -> Any\n\n\nAdapts x to an Array when device::CPU is used. Define for CPU for compatibility with adapt to CuArrays etc. Uses adapt, thus also can return SubArrays etc.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.Device_KernelAbstractions-Tuple{CPU}","page":"Function and type index","title":"SpeedyWeather.Device_KernelAbstractions","text":"Device_KernelAbstractions(\n _::CPU\n) -> Type{KernelAbstractions.CPU}\n\n\nReturn used device for use with KernelAbstractions\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SpeedyTransforms.transform!-Tuple{DiagnosticVariables, PrognosticVariables, Integer, Barotropic}","page":"Function and type index","title":"SpeedyWeather.SpeedyTransforms.transform!","text":"transform!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Integer,\n model::Barotropic;\n kwargs...\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.transform!-Tuple{DiagnosticVariables, PrognosticVariables, Integer, NoRandomProcess, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.SpeedyTransforms.transform!","text":"transform!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Integer,\n random_process::NoRandomProcess,\n spectral_transform::SpectralTransform\n)\n\n\nNoRandomProcess does not need to transform any random pattern from spectral to grid space.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.SpeedyTransforms.transform!-Tuple{DiagnosticVariables, PrognosticVariables, Integer, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.SpeedyTransforms.transform!","text":"transform!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Integer,\n model::PrimitiveEquation;\n initialize\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.transform!-Tuple{DiagnosticVariables, PrognosticVariables, Integer, ShallowWater}","page":"Function and type index","title":"SpeedyWeather.SpeedyTransforms.transform!","text":"transform!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Integer,\n model::ShallowWater;\n kwargs...\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.SpeedyTransforms.transform!-Tuple{DiagnosticVariables, PrognosticVariables, Integer, SpeedyWeather.AbstractRandomProcess, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.SpeedyTransforms.transform!","text":"transform!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Integer,\n random_process::SpeedyWeather.AbstractRandomProcess,\n spectral_transform::SpectralTransform\n)\n\n\nGeneral transform for random processes <: AbstractRandomProcess. Takes the spectral random_pattern in the prognostic variables and transforms it to spectral space in diagn.grid.random_pattern.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.WhichZenith-Tuple{SpectralGrid, SpeedyWeather.AbstractPlanet}","page":"Function and type index","title":"SpeedyWeather.WhichZenith","text":"WhichZenith(\n SG::SpectralGrid,\n P::SpeedyWeather.AbstractPlanet;\n kwargs...\n) -> Union{SolarZenith, SolarZenithSeason}\n\n\nChooses from SolarZenith (daily and seasonal cycle) or SolarZenithSeason given the parameters in model.planet. In both cases the seasonal cycle can be disabled, calculating the solar declination from the initial time instead of current time.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.activate-Union{Tuple{Particle{NF}}, Tuple{NF}} where NF","page":"Function and type index","title":"SpeedyWeather.activate","text":"activate(\n p::Particle{NF}\n) -> Particle{_A, true} where _A<:AbstractFloat\n\n\nActivate particle. Active particles can move.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.active-Union{Tuple{Particle{NF, isactive}}, Tuple{isactive}, Tuple{NF}} where {NF, isactive}","page":"Function and type index","title":"SpeedyWeather.active","text":"active(_::Particle{NF, isactive}) -> Any\n\n\nCheck whether particle is active.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.add!-Tuple{AbstractModel, Vararg{Pair{Symbol, <:SpeedyWeather.AbstractCallback}}}","page":"Function and type index","title":"SpeedyWeather.add!","text":"add!(\n model::AbstractModel,\n key_callbacks::Pair{Symbol, <:SpeedyWeather.AbstractCallback}...\n) -> Any\n\n\nAdd a or several callbacks to a model::AbstractModel. To be used like\n\nadd!(model, :my_callback => callback)\nadd!(model, :my_callback1 => callback, :my_callback2 => other_callback)\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.add!-Tuple{AbstractModel, Vararg{SpeedyWeather.AbstractCallback}}","page":"Function and type index","title":"SpeedyWeather.add!","text":"add!(\n model::AbstractModel,\n callbacks::SpeedyWeather.AbstractCallback...\n)\n\n\nAdd a or several callbacks to a mdoel without specifying the key which is randomly created like callback_????. To be used like\n\nadd!(model.callbacks, callback)\nadd!(model.callbacks, callback1, callback2).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.add!-Tuple{AbstractModel, Vararg{SpeedyWeather.AbstractOutputVariable}}","page":"Function and type index","title":"SpeedyWeather.add!","text":"add!(\n model::AbstractModel,\n outputvariables::SpeedyWeather.AbstractOutputVariable...\n)\n\n\nAdd outputvariables to the dictionary in output::NetCDFOutput of model, i.e. at model.output.variables.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.add!-Tuple{Dict{Symbol, SpeedyWeather.AbstractCallback}, Vararg{Pair{Symbol, <:SpeedyWeather.AbstractCallback}}}","page":"Function and type index","title":"SpeedyWeather.add!","text":"add!(\n D::Dict{Symbol, SpeedyWeather.AbstractCallback},\n key_callbacks::Pair{Symbol, <:SpeedyWeather.AbstractCallback}...\n)\n\n\nAdd a or several callbacks to a Dict{String, AbstractCallback} dictionary. To be used like\n\nadd!(model.callbacks, :my_callback => callback)\nadd!(model.callbacks, :my_callback1 => callback, :my_callback2 => other_callback)\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.add!-Tuple{Dict{Symbol, SpeedyWeather.AbstractCallback}, Vararg{SpeedyWeather.AbstractCallback}}","page":"Function and type index","title":"SpeedyWeather.add!","text":"add!(\n D::Dict{Symbol, SpeedyWeather.AbstractCallback},\n callbacks::SpeedyWeather.AbstractCallback...;\n verbose\n)\n\n\nAdd a or several callbacks to a Dict{Symbol, AbstractCallback} dictionary without specifying the key which is randomly created like callback_????. To be used like\n\nadd!(model.callbacks, callback)\nadd!(model.callbacks, callback1, callback2).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.add!-Tuple{Dict{Symbol, SpeedyWeather.AbstractOutputVariable}, Vararg{SpeedyWeather.AbstractOutputVariable}}","page":"Function and type index","title":"SpeedyWeather.add!","text":"add!(\n D::Dict{Symbol, SpeedyWeather.AbstractOutputVariable},\n outputvariables::SpeedyWeather.AbstractOutputVariable...\n) -> Dict{Symbol, SpeedyWeather.AbstractOutputVariable}\n\n\nAdd outputvariables to a dictionary defining the variables subject to NetCDF output.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.add!-Tuple{NetCDFOutput, Vararg{SpeedyWeather.AbstractOutputVariable}}","page":"Function and type index","title":"SpeedyWeather.add!","text":"add!(\n output::NetCDFOutput,\n outputvariables::SpeedyWeather.AbstractOutputVariable...\n) -> NetCDFOutput\n\n\nAdd outputvariables to the dictionary in output::NetCDFOutput, i.e. at output.variables.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.add_default!-Tuple{Dict{Symbol, SpeedyWeather.AbstractOutputVariable}, Type{<:Barotropic}}","page":"Function and type index","title":"SpeedyWeather.add_default!","text":"add_default!(\n output_variables::Dict{Symbol, SpeedyWeather.AbstractOutputVariable},\n Model::Type{<:Barotropic}\n) -> Dict{Symbol, SpeedyWeather.AbstractOutputVariable}\n\n\nAdd default variables to output for a Barotropic model: Vorticity, zonal and meridional velocity.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.add_default!-Tuple{Dict{Symbol, SpeedyWeather.AbstractOutputVariable}, Type{<:PrimitiveDry}}","page":"Function and type index","title":"SpeedyWeather.add_default!","text":"add_default!(\n variables::Dict{Symbol, SpeedyWeather.AbstractOutputVariable},\n Model::Type{<:PrimitiveDry}\n) -> Dict{Symbol, SpeedyWeather.AbstractOutputVariable}\n\n\nAdd default variables to output for a PrimitiveDry model, same as for a Barotropic model but also the surface pressure and temperature.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.add_default!-Tuple{Dict{Symbol, SpeedyWeather.AbstractOutputVariable}, Type{<:PrimitiveWet}}","page":"Function and type index","title":"SpeedyWeather.add_default!","text":"add_default!(\n variables::Dict{Symbol, SpeedyWeather.AbstractOutputVariable},\n Model::Type{<:PrimitiveWet}\n) -> Dict{Symbol, SpeedyWeather.AbstractOutputVariable}\n\n\nAdd default variables to output for a PrimitiveWet model, same as for a PrimitiveDry model but also the specific humidity.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.add_default!-Tuple{Dict{Symbol, SpeedyWeather.AbstractOutputVariable}, Type{<:ShallowWater}}","page":"Function and type index","title":"SpeedyWeather.add_default!","text":"add_default!(\n variables::Dict{Symbol, SpeedyWeather.AbstractOutputVariable},\n Model::Type{<:ShallowWater}\n) -> Dict{Symbol, SpeedyWeather.AbstractOutputVariable}\n\n\nAdd default variables to output for a ShallowWater model, same as for a Barotropic model but also the interface displacement.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.bernoulli_potential!-Tuple{DiagnosticVariables, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.bernoulli_potential!","text":"bernoulli_potential!(\n diagn::DiagnosticVariables,\n S::SpectralTransform\n)\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.bulk_richardson!-Tuple{ColumnVariables, SpeedyWeather.AbstractAtmosphere}","page":"Function and type index","title":"SpeedyWeather.bulk_richardson!","text":"bulk_richardson!(\n column::ColumnVariables,\n atmosphere::SpeedyWeather.AbstractAtmosphere\n) -> Any\n\n\nCalculate the bulk richardson number following Frierson, 2007. For vertical stability in the boundary layer.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.bulk_richardson_surface-Tuple{ColumnVariables, SpeedyWeather.AbstractAtmosphere}","page":"Function and type index","title":"SpeedyWeather.bulk_richardson_surface","text":"bulk_richardson_surface(\n column::ColumnVariables,\n atmosphere::SpeedyWeather.AbstractAtmosphere\n) -> Any\n\n\nCalculate the bulk richardson number following Frierson, 2007. For vertical stability in the boundary layer.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.callback!-Tuple{GlobalSurfaceTemperatureCallback, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.callback!","text":"callback!(\n callback::GlobalSurfaceTemperatureCallback,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n) -> Any\n\n\nPulls the average temperature from the lowermost layer and stores it in the next element of the callback.temp vector.\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.convection!-Union{Tuple{NF}, Tuple{ColumnVariables{NF}, DryBettsMiller, Geometry, SpeedyWeather.AbstractAtmosphere, PrimitiveEquation}} where NF","page":"Function and type index","title":"SpeedyWeather.convection!","text":"convection!(\n column::ColumnVariables{NF},\n DBM::DryBettsMiller,\n geometry::Geometry,\n atmosphere::SpeedyWeather.AbstractAtmosphere,\n model::PrimitiveEquation\n)\n\n\ncalculates temperature tendency for the dry convection scheme following the simplified Betts-Miller convection from Frierson 2007 but with zero humidity. Starts with a first-guess relaxation to determine the convective criterion, then adjusts the reference profiles for thermodynamic consistency (e.g. in dry convection the humidity profile is non-precipitating), and relaxes current vertical profiles to the adjusted references.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.convection!-Union{Tuple{NF}, Tuple{ColumnVariables{NF}, SimplifiedBettsMiller, SpeedyWeather.AbstractClausiusClapeyron, Geometry, SpeedyWeather.AbstractPlanet, SpeedyWeather.AbstractAtmosphere, SpeedyWeather.AbstractTimeStepper, PrimitiveWet}} where NF","page":"Function and type index","title":"SpeedyWeather.convection!","text":"convection!(\n column::ColumnVariables{NF},\n SBM::SimplifiedBettsMiller,\n clausius_clapeyron::SpeedyWeather.AbstractClausiusClapeyron,\n geometry::Geometry,\n planet::SpeedyWeather.AbstractPlanet,\n atmosphere::SpeedyWeather.AbstractAtmosphere,\n time_stepping::SpeedyWeather.AbstractTimeStepper,\n model::PrimitiveWet\n) -> Union{Nothing, Int64}\n\n\ncalculates temperature and humidity tendencies for the convection scheme following the simplified Betts-Miller convection. Starts with a first-guess relaxation to determine the convective criteria (none, dry/shallow or deep), then adjusts reference profiles for thermodynamic consistency (e.g. in dry convection the humidity profile is non-precipitating), and relaxes current vertical profiles to the adjusted references.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.coriolis-Tuple{Grid} where Grid<:AbstractGridArray","page":"Function and type index","title":"SpeedyWeather.coriolis","text":"coriolis(grid::AbstractGridArray; kwargs...) -> 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, Vararg{Integer}}} where Grid<:AbstractGridArray","page":"Function and type index","title":"SpeedyWeather.coriolis","text":"coriolis(grid::AbstractGridArray; kwargs...) -> 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.cos_zenith!-Union{Tuple{NF}, Tuple{AbstractGrid{NF}, SolarZenith, DateTime, SpeedyWeather.AbstractGeometry}} where NF","page":"Function and type index","title":"SpeedyWeather.cos_zenith!","text":"cos_zenith!(\n cos_zenith::AbstractGridArray{NF, 1, Array{NF, 1}},\n S::SolarZenith,\n time::DateTime,\n geometry::SpeedyWeather.AbstractGeometry\n)\n\n\nCalculate cos of solar zenith angle with a daily cycle at time time. Seasonal cycle or time correction may be disabled, depending on parameters in SolarZenith.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.cos_zenith!-Union{Tuple{NF}, Tuple{AbstractGrid{NF}, SolarZenithSeason, DateTime, SpeedyWeather.AbstractGeometry}} where NF","page":"Function and type index","title":"SpeedyWeather.cos_zenith!","text":"cos_zenith!(\n cos_zenith::AbstractGridArray{NF, 1, Array{NF, 1}},\n S::SolarZenithSeason,\n time::DateTime,\n geometry::SpeedyWeather.AbstractGeometry\n)\n\n\nCalculate cos of solar zenith angle as daily average at time time. Seasonal cycle or time correction may be disabled, depending on parameters in SolarZenithSeason.\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.deactivate-Union{Tuple{Particle{NF}}, Tuple{NF}} where NF","page":"Function and type index","title":"SpeedyWeather.deactivate","text":"deactivate(\n p::Particle{NF}\n) -> Particle{_A, false} where _A<:AbstractFloat\n\n\nDeactivate particle. Inactive particles cannot move.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.default_array_type-Tuple{SpeedyWeather.AbstractDevice}","page":"Function and type index","title":"SpeedyWeather.default_array_type","text":"default_array_type(\n device::SpeedyWeather.AbstractDevice\n) -> Type{Array}\n\n\nDefault array type on device.\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(\n nlayers::Integer\n) -> Vector{Float64}\n\n\nVertical sigma coordinates defined by their nlayers+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. Default levels are equally spaced from 0 to 1 (including).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.drag!-Tuple{DiagnosticVariables, QuadraticDrag}","page":"Function and type index","title":"SpeedyWeather.drag!","text":"drag!(diagn::DiagnosticVariables, drag::QuadraticDrag)\n\n\nQuadratic drag for the momentum equations.\n\nF = -c_D/H*|(u, v)|*(u, v)\n\nwith cD the non-dimensional drag coefficient as defined in drag::QuadraticDrag. cD and layer thickness H are precomputed in initialize!(::QuadraticDrag, ::AbstractModel) and scaled by the radius as are the momentum equations.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.dry_adiabat!-Tuple{AbstractVector, AbstractVector, Real, AbstractVector, SpeedyWeather.AbstractAtmosphere}","page":"Function and type index","title":"SpeedyWeather.dry_adiabat!","text":"dry_adiabat!(\n temp_ref_profile::AbstractVector,\n temp_environment::AbstractVector,\n temp_parcel::Real,\n σ::AbstractVector,\n atmosphere::SpeedyWeather.AbstractAtmosphere\n) -> Int64\n\n\nCalculates the moist pseudo adiabat given temperature and humidity of surface parcel. Follows the dry adiabat till condensation and then continues on the pseudo moist-adiabat with immediate condensation to the level of zero buoyancy. Levels above are skipped, set to NaN instead and should be skipped in the relaxation.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.dry_static_energy!-Tuple{ColumnVariables, SpeedyWeather.AbstractAtmosphere}","page":"Function and type index","title":"SpeedyWeather.dry_static_energy!","text":"dry_static_energy!(\n column::ColumnVariables,\n atmosphere::SpeedyWeather.AbstractAtmosphere\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!-Tuple{DiagnosticVariables, PrognosticVariables, Integer, Barotropic}","page":"Function and type index","title":"SpeedyWeather.dynamics_tendencies!","text":"dynamics_tendencies!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Integer,\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{DiagnosticVariables, PrognosticVariables, Integer, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.dynamics_tendencies!","text":"dynamics_tendencies!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Integer,\n model::PrimitiveEquation\n)\n\n\nCalculate all tendencies for the PrimitiveEquation model (wet or dry).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.dynamics_tendencies!-Tuple{DiagnosticVariables, PrognosticVariables, Integer, ShallowWater}","page":"Function and type index","title":"SpeedyWeather.dynamics_tendencies!","text":"dynamics_tendencies!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Integer,\n model::ShallowWater\n)\n\n\nCalculate all tendencies for the ShallowWaterModel.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.finalize!-Tuple{Feedback}","page":"Function and type index","title":"SpeedyWeather.finalize!","text":"finalize!(F::Feedback)\n\n\nFinalises the progress meter and the progress txt file.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.first_timesteps!-Tuple{PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.first_timesteps!","text":"first_timesteps!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\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!-Tuple{LowerTriangularArray, AbstractGridArray, DiagnosticVariables, Geometry, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.flux_divergence!","text":"flux_divergence!(\n A_tend::LowerTriangularArray,\n A_grid::AbstractGridArray,\n diagn::DiagnosticVariables,\n G::Geometry,\n S::SpectralTransform;\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, SpeedyWeather.AbstractPlanet, SpeedyWeather.AbstractAtmosphere}","page":"Function and type index","title":"SpeedyWeather.fluxes_to_tendencies!","text":"fluxes_to_tendencies!(\n column::ColumnVariables,\n geometry::Geometry,\n planet::SpeedyWeather.AbstractPlanet,\n atmosphere::SpeedyWeather.AbstractAtmosphere\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{DiagnosticVariables, JetStreamForcing}","page":"Function and type index","title":"SpeedyWeather.forcing!","text":"forcing!(\n diagn::DiagnosticVariables,\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, ::AbstractModel).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.geopotential!","page":"Function and type index","title":"SpeedyWeather.geopotential!","text":"geopotential!(\n geopot::AbstractVector,\n temp::AbstractVector,\n G::Geopotential\n)\ngeopotential!(\n geopot::AbstractVector,\n temp::AbstractVector,\n G::Geopotential,\n geopot_surf::Real\n)\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":"function"},{"location":"functions/#SpeedyWeather.geopotential!-Tuple{DiagnosticVariables, Geopotential, SpeedyWeather.AbstractOrography}","page":"Function and type index","title":"SpeedyWeather.geopotential!","text":"geopotential!(\n diagn::DiagnosticVariables,\n geopotential::Geopotential,\n orography::SpeedyWeather.AbstractOrography\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{DiagnosticVariables, LowerTriangularArray, SpeedyWeather.AbstractPlanet}","page":"Function and type index","title":"SpeedyWeather.geopotential!","text":"geopotential!(\n diagn::DiagnosticVariables,\n pres::LowerTriangularArray,\n planet::SpeedyWeather.AbstractPlanet\n)\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.get_column!-Tuple{ColumnVariables, DiagnosticVariables, PrognosticVariables, Int64, PrimitiveEquation}","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, PrognosticVariables, Integer, Integer, Geometry, SpeedyWeather.AbstractPlanet, SpeedyWeather.AbstractOrography, SpeedyWeather.AbstractLandSeaMask, SpeedyWeather.AbstractAlbedo, SpeedyWeather.AbstractImplicit}","page":"Function and type index","title":"SpeedyWeather.get_column!","text":"get_column!(\n C::ColumnVariables,\n D::DiagnosticVariables,\n P::PrognosticVariables,\n ij::Integer,\n jring::Integer,\n geometry::Geometry,\n planet::SpeedyWeather.AbstractPlanet,\n orography::SpeedyWeather.AbstractOrography,\n land_sea_mask::SpeedyWeather.AbstractLandSeaMask,\n albedo::SpeedyWeather.AbstractAlbedo,\n implicit::SpeedyWeather.AbstractImplicit\n) -> Any\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{SpeedyWeather.AbstractOutput}","page":"Function and type index","title":"SpeedyWeather.get_full_output_file_path","text":"get_full_output_file_path(\n output::SpeedyWeather.AbstractOutput\n) -> 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, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.get_thermodynamics!","text":"get_thermodynamics!(\n column::ColumnVariables,\n model::PrimitiveEquation\n)\n\n\nCalculate geopotentiala and dry static energy for the primitive equation model.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.get_Δt_millisec","page":"Function and type index","title":"SpeedyWeather.get_Δt_millisec","text":"get_Δt_millisec(\n Δt_at_T31::Dates.TimePeriod,\n trunc,\n radius,\n adjust_with_output::Bool\n) -> Any\nget_Δt_millisec(\n Δt_at_T31::Dates.TimePeriod,\n trunc,\n radius,\n adjust_with_output::Bool,\n output_dt::Dates.TimePeriod\n) -> Any\n\n\nComputes the time step in [ms]. Δt_at_T31 is always scaled with the resolution trunc of the model. In case adjust_Δt_with_output is true, the Δt_at_T31 is additionally adjusted to the closest divisor of output_dt so that the output time axis is keeping output_dt exactly.\n\n\n\n\n\n","category":"function"},{"location":"functions/#SpeedyWeather.grad-Union{Tuple{NF}, Tuple{ClausiusClapeyron{NF}, NF}} where NF","page":"Function and type index","title":"SpeedyWeather.grad","text":"grad(CC::ClausiusClapeyron{NF}, temp_kelvin) -> Any\n\n\nGradient of Clausius-Clapeyron wrt to temperature, evaluated at temp_kelvin.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.grad-Union{Tuple{NF}, Tuple{TetensEquation{NF}, NF}} where NF","page":"Function and type index","title":"SpeedyWeather.grad","text":"grad(\n TetensCoefficients::TetensEquation{NF},\n temp_kelvin\n) -> Any\n\n\nGradient of the Tetens equation wrt to temperature, evaluated at temp_kelvin.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.grad_saturation_humidity-Union{Tuple{NF}, Tuple{ClausiusClapeyron{NF}, NF, NF}} where NF","page":"Function and type index","title":"SpeedyWeather.grad_saturation_humidity","text":"grad_saturation_humidity(\n CC::ClausiusClapeyron{NF},\n temp_kelvin,\n pres\n) -> Any\n\n\nGradient of Clausius-Clapeyron wrt to temperature, evaluated at temp_kelvin.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.has-Tuple{Type{<:AbstractModel}, Symbol}","page":"Function and type index","title":"SpeedyWeather.has","text":"has(Model::Type{<:AbstractModel}, var_name::Symbol) -> Any\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::DiagnosticVariables,\n progn::PrognosticVariables,\n diffusion::SpeedyWeather.AbstractHorizontalDiffusion,\n model::PrimitiveEquation\n) -> Any\nhorizontal_diffusion!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n diffusion::SpeedyWeather.AbstractHorizontalDiffusion,\n model::PrimitiveEquation,\n lf::Integer\n) -> Any\n\n\nApply horizontal diffusion applied to vorticity, divergence, temperature, and humidity (PrimitiveWet only) in the PrimitiveEquation 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 diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n diffusion::SpeedyWeather.AbstractHorizontalDiffusion,\n model::ShallowWater\n) -> Any\nhorizontal_diffusion!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n diffusion::SpeedyWeather.AbstractHorizontalDiffusion,\n model::ShallowWater,\n lf::Integer\n) -> Any\n\n\nApply horizontal diffusion to vorticity and divergence in the ShallowWaterModel.\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::DiagnosticVariables,\n progn::PrognosticVariables,\n diffusion::SpeedyWeather.AbstractHorizontalDiffusion,\n model::Barotropic\n) -> Any\nhorizontal_diffusion!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n diffusion::SpeedyWeather.AbstractHorizontalDiffusion,\n model::Barotropic,\n lf::Integer\n) -> Any\n\n\nApply horizontal diffusion to vorticity in the BarotropicModel.\n\n\n\n\n\n","category":"function"},{"location":"functions/#SpeedyWeather.horizontal_diffusion!-Tuple{LowerTriangularArray, LowerTriangularArray, AbstractMatrix, AbstractMatrix}","page":"Function and type index","title":"SpeedyWeather.horizontal_diffusion!","text":"horizontal_diffusion!(\n tendency::LowerTriangularArray,\n var::LowerTriangularArray,\n expl::AbstractMatrix,\n impl::AbstractMatrix\n)\n\n\nApply horizontal diffusion to a 2D field var 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 var as well as its tendency tendency.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.implicit_correction!-Tuple{DiagnosticVariables, ImplicitPrimitiveEquation, PrognosticVariables}","page":"Function and type index","title":"SpeedyWeather.implicit_correction!","text":"implicit_correction!(\n diagn::DiagnosticVariables,\n implicit::ImplicitPrimitiveEquation,\n progn::PrognosticVariables\n)\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!-Tuple{DiagnosticVariables, PrognosticVariables, ImplicitShallowWater}","page":"Function and type index","title":"SpeedyWeather.implicit_correction!","text":"implicit_correction!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\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!-Tuple{AquaPlanetMask, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n land_sea_mask::AquaPlanetMask,\n model::PrimitiveEquation\n)\n\n\nSets all grid points to 0 = sea.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{Barotropic}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n model::Barotropic;\n time\n) -> Simulation{Model} where Model<:Barotropic\n\n\nCalls all initialize! functions for most fields, representing 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{Clock, SpeedyWeather.AbstractTimeStepper}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n clock::Clock,\n time_stepping::SpeedyWeather.AbstractTimeStepper\n) -> 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{EarthOrography, SpeedyWeather.AbstractPlanet, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n orog::EarthOrography,\n P::SpeedyWeather.AbstractPlanet,\n S::SpectralTransform\n)\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, Clock, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n feedback::Feedback,\n clock::Clock,\n model::AbstractModel\n) -> ProgressMeter.Progress\n\n\nInitializes the a Feedback struct.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{Geopotential, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n geopotential::Geopotential,\n model::PrimitiveEquation\n)\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.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, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(diffusion::HyperDiffusion, model::AbstractModel)\n\n\nPrecomputes the hyper diffusion terms in diffusion 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.AbstractGeometry, SpeedyWeather.AbstractTimeStepper}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n diffusion::HyperDiffusion,\n G::SpeedyWeather.AbstractGeometry,\n L::SpeedyWeather.AbstractTimeStepper\n)\n\n\nPrecomputes the hyper diffusion terms for all layers based on the model time step in L, the vertical level sigma level in G.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{ImplicitPrimitiveEquation, Real, DiagnosticVariables, SpeedyWeather.AbstractGeometry, SpeedyWeather.AbstractGeopotential, SpeedyWeather.AbstractAtmosphere, SpeedyWeather.AbstractAdiabaticConversion}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n implicit::ImplicitPrimitiveEquation,\n dt::Real,\n diagn::DiagnosticVariables,\n geometry::SpeedyWeather.AbstractGeometry,\n geopotential::SpeedyWeather.AbstractGeopotential,\n atmosphere::SpeedyWeather.AbstractAtmosphere,\n adiabatic_conversion::SpeedyWeather.AbstractAdiabaticConversion\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, SpeedyWeather.AbstractPlanet, SpeedyWeather.AbstractAtmosphere}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n implicit::ImplicitShallowWater,\n dt::Real,\n planet::SpeedyWeather.AbstractPlanet,\n atmosphere::SpeedyWeather.AbstractAtmosphere\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{LandSeaMask, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n land_sea_mask::LandSeaMask,\n model::PrimitiveEquation\n) -> AbstractGrid{NF} where NF<:AbstractFloat\n\n\nReads a high-resolution land-sea mask from file and interpolates (grid-call average) onto the model grid for a fractional sea mask.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{Leapfrog, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(L::Leapfrog, model::AbstractModel)\n\n\nInitialize leapfrogging L by recalculating the timestep given the output time step output_dt from model.output. Recalculating will slightly adjust the time step to be a divisor such that an integer number of time steps matches exactly with the output time step.\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{PrimitiveDry}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n model::PrimitiveDry;\n time\n) -> Simulation{Model} where Model<:PrimitiveDry\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!(\n model::PrimitiveWet;\n time\n) -> Simulation{Model} where Model<:PrimitiveWet\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, PressureOnOrography, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn::PrognosticVariables,\n _::PressureOnOrography,\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.initialize!-Tuple{PrognosticVariables, RossbyHaurwitzWave, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn::PrognosticVariables,\n initial_conditions::RossbyHaurwitzWave,\n model::AbstractModel\n)\n\n\nRossby-Haurwitz wave initial conditions as in Williamson et al. 1992, J Computational Physics with an additional cut-off amplitude c to filter out tiny harmonics in the vorticity field.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{PrognosticVariables, StartFromFile, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn_new::PrognosticVariables,\n initial_conditions::StartFromFile,\n model::AbstractModel\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, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn::PrognosticVariables,\n initial_conditions::ZonalJet,\n model::AbstractModel\n)\n\n\nInitial conditions from Galewsky, 2004, Tellus\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{Schedule, Clock}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(scheduler::Schedule, clock::Clock) -> Schedule\n\n\nInitialize a Schedule with a Clock (which is assumed to have been initialized). Takes both scheduler.every and scheduler.times into account, such that both periodic and events can be scheduled simulataneously. But execution will happen only once if they coincide on a given time step.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Tuple{ShallowWater}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n model::ShallowWater;\n time\n) -> Simulation{Model} where Model<:ShallowWater\n\n\nCalls all initialize! functions for most components (=fields) of model, except for model.output and model.feedback which are always initialized 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{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)\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{Interpolator}, Tuple{Grid3D}, Tuple{Grid2D}, Tuple{NetCDFOutput{Grid2D, Grid3D, Interpolator}, SpeedyWeather.AbstractFeedback, PrognosticVariables, DiagnosticVariables, AbstractModel}} where {Grid2D, Grid3D, Interpolator}","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n output::NetCDFOutput{Grid2D, Grid3D, Interpolator},\n feedback::SpeedyWeather.AbstractFeedback,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\nInitialize NetCDF output by creating a netCDF file and storing the initial conditions of diagn (and progn). To be called just before the first timesteps.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Union{Tuple{NF}, Tuple{Array{Particle{NF}, 1}, PrognosticVariables, DiagnosticVariables, ParticleAdvection2D}} where NF","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n particles::Array{Particle{NF}, 1},\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n particle_advection::ParticleAdvection2D\n) -> Any\n\n\nInitialize particle advection time integration: Store u,v interpolated initial conditions in diagn.particles.u and .v to be used when particle advection actually executed for first time.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Union{Tuple{NF}, Tuple{GlobalSurfaceTemperatureCallback{NF}, PrognosticVariables, DiagnosticVariables, AbstractModel}} where NF","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n callback::GlobalSurfaceTemperatureCallback{NF},\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n) -> Int64\n\n\nInitializes callback.temp vector that records the global mean surface temperature on every time step. Allocates vector of correct length (number of elements = total time steps plus one) and stores the global surface temperature of the initial conditions\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.initialize!-Union{Tuple{NF}, Tuple{PrognosticVariables{NF}, JablonowskiTemperature, PrimitiveEquation}} where NF","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn::PrognosticVariables{NF},\n initial_conditions::JablonowskiTemperature,\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{PrognosticVariables{NF}, RandomWaves, ShallowWater}} where NF","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn::PrognosticVariables{NF},\n initial_conditions::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}, StartWithRandomVorticity, Barotropic}} where NF","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn::PrognosticVariables{NF},\n initial_conditions::StartWithRandomVorticity,\n model::Barotropic\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}, ZonalWind, PrimitiveEquation}} where NF","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n progn::PrognosticVariables{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{P}, Tuple{Vector{P}, AbstractModel}} where P<:Particle","page":"Function and type index","title":"SpeedyWeather.initialize!","text":"initialize!(\n particles::Array{P<:Particle, 1},\n model::AbstractModel\n)\n\n\nInitialize particle locations uniformly in latitude, longitude and in the vertical σ coordinates. This uses a cosin-distribution in latitude for an equal-area uniformity.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.isdecreasing-Tuple{AbstractVector}","page":"Function and type index","title":"SpeedyWeather.isdecreasing","text":"isdecreasing(v::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(v::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.ismod-Tuple{Particle}","page":"Function and type index","title":"SpeedyWeather.ismod","text":"ismod(p::Particle) -> Bool\n\n\nCheck that a particle is in longitude [0,360˚E), latitude [-90˚,90˚N], and σ in [0,1].\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.isscheduled-Tuple{Schedule, Clock}","page":"Function and type index","title":"SpeedyWeather.isscheduled","text":"isscheduled(S::Schedule, clock::Clock) -> Bool\n\n\nEvaluate whether (e.g. a callback) should be scheduled at the timestep given in clock. Returns true for scheduled executions, false for no execution on this time step.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.large_scale_condensation!-Tuple{ColumnVariables, ImplicitCondensation, SpeedyWeather.AbstractClausiusClapeyron, Geometry, SpeedyWeather.AbstractPlanet, SpeedyWeather.AbstractAtmosphere, SpeedyWeather.AbstractTimeStepper}","page":"Function and type index","title":"SpeedyWeather.large_scale_condensation!","text":"large_scale_condensation!(\n column::ColumnVariables,\n scheme::ImplicitCondensation,\n clausius_clapeyron::SpeedyWeather.AbstractClausiusClapeyron,\n geometry::Geometry,\n planet::SpeedyWeather.AbstractPlanet,\n atmosphere::SpeedyWeather.AbstractAtmosphere,\n time_stepping::SpeedyWeather.AbstractTimeStepper\n)\n\n\nLarge-scale condensation for a column by relaxation back to 100% relative humidity. Calculates the tendencies for specific humidity and temperature from latent heat release 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!(\n device_setup::SpeedyWeather.DeviceSetup,\n kernel!,\n ndrange,\n kernel_args...\n)\n\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{LowerTriangularArray, LowerTriangularArray, LowerTriangularArray, Real, Int64, Leapfrog{NF}}} where NF","page":"Function and type index","title":"SpeedyWeather.leapfrog!","text":"leapfrog!(\n A_old::LowerTriangularArray,\n A_new::LowerTriangularArray,\n tendency::LowerTriangularArray,\n dt::Real,\n lf::Int64,\n L::Leapfrog{NF}\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{DiagnosticVariables, PrognosticVariables, Int64, SpeedyWeather.AbstractAtmosphere, ImplicitPrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.linear_pressure_gradient!","text":"linear_pressure_gradient!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Int64,\n atmosphere::SpeedyWeather.AbstractAtmosphere,\n implicit::ImplicitPrimitiveEquation\n)\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{DiagnosticVariables, PrognosticVariables, Integer, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.linear_virtual_temperature!","text":"linear_virtual_temperature!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Integer,\n model::PrimitiveEquation\n)\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, Integer, PrimitiveDry}","page":"Function and type index","title":"SpeedyWeather.linear_virtual_temperature!","text":"linear_virtual_temperature!(\n diagn::SpeedyWeather.DiagnosticVariablesLayer,\n progn::SpeedyWeather.PrognosticLayerTimesteps,\n lf::Integer,\n model::PrimitiveDry\n)\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}, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.load_trajectory","text":"load_trajectory(\n var_name::Union{String, Symbol},\n model::AbstractModel\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.AbstractClausiusClapeyron}","page":"Function and type index","title":"SpeedyWeather.moist_static_energy!","text":"moist_static_energy!(\n column::ColumnVariables,\n clausius_clapeyron::SpeedyWeather.AbstractClausiusClapeyron\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.move-Union{Tuple{NF}, Tuple{Particle{NF, false}, Vararg{Any}}} where NF","page":"Function and type index","title":"SpeedyWeather.move","text":"move(\n p::Particle{NF, false},\n args...\n) -> Particle{NF, false} where NF\n\n\nInactive particles are not moved.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.move-Union{Tuple{NF}, Tuple{Particle{NF, true}, Any, Any, Any}} where NF","page":"Function and type index","title":"SpeedyWeather.move","text":"move(\n p::Particle{NF, true},\n dlon,\n dlat,\n dσ\n) -> Particle{_A, true} where _A<:AbstractFloat\n\n\nMove a particle with increments (dlon, dlat, dσ) in those respective coordinates. Only active particles are moved.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.move-Union{Tuple{NF}, Tuple{Particle{NF, true}, Any, Any}} where NF","page":"Function and type index","title":"SpeedyWeather.move","text":"move(\n p::Particle{NF, true},\n dlon,\n dlat\n) -> Particle{_A, true} where _A<:AbstractFloat\n\n\nMove a particle with increments (dlon, dlat) in 2D. No movement in vertical σ. Only active particles are moved.\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.output!-Tuple{NetCDFOutput, DateTime}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(output::NetCDFOutput, time::DateTime)\n\n\nWrite the current time time::DateTime to the netCDF file in output.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, Dict{Symbol, SpeedyWeather.AbstractOutputVariable}, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n output_variables::Dict{Symbol, SpeedyWeather.AbstractOutputVariable},\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\nLoop over every variable in output.variables to call the respective output! method to write into the output.netcdf_file.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\nWrites the variables from progn or diagn of time step i at time time into output.netcdf_file. Simply escapes for no netcdf output or if output shouldn't be written on this time step. Interpolates onto output grid and resolution as specified in output, 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.output!-Tuple{NetCDFOutput, SpeedyWeather.CloudTopOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.CloudTopOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.ConvectivePrecipitationOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.ConvectivePrecipitationOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.DivergenceOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.DivergenceOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.HumidityOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.HumidityOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.InterfaceDisplacementOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.InterfaceDisplacementOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.LargeScalePrecipitationOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.LargeScalePrecipitationOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.MeridionalVelocityOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.MeridionalVelocityOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.OrographyOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.OrographyOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.OutgoingLongwaveRadiationOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.OutgoingLongwaveRadiationOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.OutgoingShortwaveRadiationOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.OutgoingShortwaveRadiationOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.RandomPatternOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.RandomPatternOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.SeaSurfaceTemperatureOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.SeaSurfaceTemperatureOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.SurfaceFluxHeatOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.SurfaceFluxHeatOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.SurfaceFluxHumidOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.SurfaceFluxHumidOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.SurfacePressureOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.SurfacePressureOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.TemperatureOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.TemperatureOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for variable, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.VorticityOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.VorticityOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\nOutput the vorticity field vor from diagn.grid into the netCDF file output.netcdf_file. Interpolates the vorticity field onto the output grid and resolution as specified in output. Method required for all output variables <: AbstractOutputVariable with dispatch over the second argument.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.output!-Tuple{NetCDFOutput, SpeedyWeather.ZonalVelocityOutput, PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.output!","text":"output!(\n output::NetCDFOutput,\n variable::SpeedyWeather.ZonalVelocityOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n)\n\n\noutput! method for ZonalVelocityOutput to write the zonal velocity field u from diagn.grid, see output!(::NetCDFOutput, ::VorticityOutput, ...) for details.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.parameterization_tendencies!-Tuple{ColumnVariables, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.parameterization_tendencies!","text":"parameterization_tendencies!(\n column::ColumnVariables,\n model::PrimitiveEquation\n)\n\n\nCalls for column one physics parameterization after another and convert fluxes to tendencies.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.parameterization_tendencies!-Tuple{DiagnosticVariables, PrognosticVariables, DateTime, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.parameterization_tendencies!","text":"parameterization_tendencies!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n time::DateTime,\n model::PrimitiveEquation\n)\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.physics_tendencies_only!-Tuple{DiagnosticVariables, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.physics_tendencies_only!","text":"physics_tendencies_only!(\n diagn::DiagnosticVariables,\n model::PrimitiveEquation\n)\n\n\nFor dynamics=false, after calling parameterization_tendencies! call this function to transform the physics tendencies from grid-point to spectral space including the necessary coslat⁻¹ scaling.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.pressure_gradient_flux!-Tuple{DiagnosticVariables, PrognosticVariables, Integer, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.pressure_gradient_flux!","text":"pressure_gradient_flux!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Integer,\n S::SpectralTransform\n)\n\n\nCompute the gradient ∇lnps of the logarithm of surface pressure, followed by its flux, (u,v) * ∇lnps.\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.pseudo_adiabat!-Union{Tuple{NF}, Tuple{AbstractVector, NF, Real, AbstractVector, AbstractVector, Real, AbstractVector, SpeedyWeather.AbstractClausiusClapeyron}} where NF","page":"Function and type index","title":"SpeedyWeather.pseudo_adiabat!","text":"pseudo_adiabat!(\n temp_ref_profile::AbstractVector,\n temp_parcel,\n humid_parcel::Real,\n temp_virt_environment::AbstractVector,\n geopot::AbstractVector,\n pres::Real,\n σ::AbstractVector,\n clausius_clapeyron::SpeedyWeather.AbstractClausiusClapeyron\n) -> Int64\n\n\nCalculates the moist pseudo adiabat given temperature and humidity of surface parcel. Follows the dry adiabat till condensation and then continues on the pseudo moist-adiabat with immediate condensation to the level of zero buoyancy. Levels above are skipped, set to NaN instead and should be skipped in the relaxation.\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> using SpeedyWeather: readable_secs\n\njulia> readable_secs(12345)\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.AbstractSimulation}","page":"Function and type index","title":"SpeedyWeather.run!","text":"run!(\n simulation::SpeedyWeather.AbstractSimulation;\n period,\n output\n) -> Union{UnicodePlots.Plot{T, Val{true}} where T<:UnicodePlots.HeatmapCanvas, UnicodePlots.Plot{T, Val{false}} where T<:UnicodePlots.HeatmapCanvas}\n\n\nRun a SpeedyWeather.jl simulation. The simulation.model is assumed to be initialized.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.saturation_humidity!-Tuple{ColumnVariables, SpeedyWeather.AbstractClausiusClapeyron}","page":"Function and type index","title":"SpeedyWeather.saturation_humidity!","text":"saturation_humidity!(\n column::ColumnVariables,\n clausius_clapeyron::SpeedyWeather.AbstractClausiusClapeyron\n)\n\n\nCompute the saturation water vapour pressure [Pa], the saturation humidity [kg/kg] and the relative humidity following clausius_clapeyron.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.saturation_humidity-Union{Tuple{NF}, Tuple{NF, NF, SpeedyWeather.AbstractClausiusClapeyron}} where NF","page":"Function and type index","title":"SpeedyWeather.saturation_humidity","text":"saturation_humidity(\n temp_kelvin,\n pres,\n clausius_clapeyron::SpeedyWeather.AbstractClausiusClapeyron\n) -> Any\n\n\nSaturation humidity [kg/kg] from temperature [K], pressure [Pa] via\n\nsat_vap_pres = clausius_clapeyron(temperature)\nsaturation humidity = mol_ratio * sat_vap_pres / pressure\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.saturation_humidity-Union{Tuple{NF}, Tuple{NF, NF}} where NF","page":"Function and type index","title":"SpeedyWeather.saturation_humidity","text":"saturation_humidity(sat_vap_pres, pres; mol_ratio) -> Any\n\n\nSaturation humidity from saturation vapour pressure and pressure via\n\nqsat = mol_ratio*sat_vap_pres/pres\n\nwith both pressures in same units and qsat in kg/kg.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.scale!-Tuple{DiagnosticVariables, Symbol, Real}","page":"Function and type index","title":"SpeedyWeather.scale!","text":"scale!(\n diagn::DiagnosticVariables,\n var::Symbol,\n scale::Real\n) -> Any\n\n\nScale the variable var inside diagn with scalar scale.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.scale!-Tuple{PrognosticVariables, DiagnosticVariables, Real}","page":"Function and type index","title":"SpeedyWeather.scale!","text":"scale!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n scale::Real\n) -> 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!-Tuple{PrognosticVariables, Symbol, Real}","page":"Function and type index","title":"SpeedyWeather.scale!","text":"scale!(progn::PrognosticVariables, var::Symbol, scale::Real)\n\n\nScale the variable var inside progn with scalar scale.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set!-Tuple{AbstractModel}","page":"Function and type index","title":"SpeedyWeather.set!","text":"set!(\n model::AbstractModel;\n orography,\n land_sea_mask,\n albedo,\n kwargs...\n) -> Bool\n\n\nSets a boundary condition fields for model. The input can be a function, RingGrid, LowerTriangularMatrix, or scalar as for other set! functions. If the keyword add==true the input is added to the exisiting field instead.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set!-Tuple{PrognosticVariables, Geometry}","page":"Function and type index","title":"SpeedyWeather.set!","text":"set!(\n progn::PrognosticVariables,\n geometry::Geometry;\n u,\n v,\n vor,\n div,\n temp,\n humid,\n pres,\n sea_surface_temperature,\n sea_ice_concentration,\n land_surface_temperature,\n snow_depth,\n soil_moisture_layer1,\n soil_moisture_layer2,\n lf,\n add,\n spectral_transform,\n coslat_scaling_included\n)\n\n\nSets new values for the keyword arguments (velocities, vorticity, divergence, etc..) into the prognostic variable struct progn at timestep index lf. If add==true they are added to the current value instead. If a SpectralTransform S is provided, it is used when needed to set the variable, otherwise it is recomputed. In case u and v are provied, actually the divergence and vorticity are set and coslat_scaling_included specficies whether or not the 1/cos(lat) scaling is already included in the arrays or not (default: false)\n\nThe input may be:\n\nA function or callable object f(lond, latd, σ) -> value (multilevel variables) \nA function or callable object f(lond, latd) -> value (surface level variables)\nAn instance of AbstractGridArray \nAn instance of LowerTriangularArray \nA scalar <: Number (interpreted as a constant field in grid space)\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set!-Tuple{SpeedyWeather.AbstractSimulation}","page":"Function and type index","title":"SpeedyWeather.set!","text":"set!(S::SpeedyWeather.AbstractSimulation; kwargs...) -> Any\n\n\nSets properties of the simuluation S. Convenience wrapper to call the other concrete set! methods. All kwargs are forwarded to these methods, which are documented seperately. See their documentation for possible kwargs. \n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_period!-Tuple{Clock, Dates.Period}","page":"Function and type index","title":"SpeedyWeather.set_period!","text":"set_period!(clock::Clock, period::Dates.Period) -> Second\n\n\nSet the period of the clock to a new value. Converts any Dates.Period input to Second.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.set_period!-Tuple{Clock, Real}","page":"Function and type index","title":"SpeedyWeather.set_period!","text":"set_period!(clock::Clock, period::Real) -> Any\n\n\nSet the period of the clock to a new value. Converts any ::Real input to Day.\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(nlayers::Integer, σ_half::AbstractVector) -> Bool\n\n\nCheck that nlayers and σ_half match.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.solar_hour_angle-Union{Tuple{T}, Tuple{Type{T}, DateTime, Any, Second}} where T","page":"Function and type index","title":"SpeedyWeather.solar_hour_angle","text":"solar_hour_angle(\n _::Type{T},\n time::DateTime,\n λ,\n length_of_day::Second\n) -> Any\n\n\nFraction of day as angle in radians [0...2π]. TODO: Takes length of day as argument, but a call to Dates.Time() currently have this hardcoded anyway.\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.surface_pressure_tendency!-Tuple{DiagnosticVariables, 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{DiagnosticVariables, ImplicitPrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.temperature_anomaly!","text":"temperature_anomaly!(\n diagn::DiagnosticVariables,\n implicit::ImplicitPrimitiveEquation\n)\n\n\nConvert 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{DiagnosticVariables, LowerTriangularArray, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.temperature_average!","text":"temperature_average!(\n diagn::DiagnosticVariables,\n temp::LowerTriangularArray,\n S::SpectralTransform\n)\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, HeldSuarez, SpeedyWeather.AbstractAtmosphere}","page":"Function and type index","title":"SpeedyWeather.temperature_relaxation!","text":"temperature_relaxation!(\n column::ColumnVariables,\n scheme::HeldSuarez,\n atmosphere::SpeedyWeather.AbstractAtmosphere\n)\n\n\nApply temperature relaxation following Held and Suarez 1996, BAMS.\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_tendency!-Tuple{DiagnosticVariables, SpeedyWeather.AbstractAdiabaticConversion, SpeedyWeather.AbstractAtmosphere, ImplicitPrimitiveEquation, Geometry, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.temperature_tendency!","text":"temperature_tendency!(\n diagn::DiagnosticVariables,\n adiabatic_conversion::SpeedyWeather.AbstractAdiabaticConversion,\n atmosphere::SpeedyWeather.AbstractAtmosphere,\n implicit::ImplicitPrimitiveEquation,\n G::Geometry,\n S::SpectralTransform\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.time_stepping!-Tuple{PrognosticVariables, DiagnosticVariables, AbstractModel}","page":"Function and type index","title":"SpeedyWeather.time_stepping!","text":"time_stepping!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel\n) -> Union{UnicodePlots.Plot{T, Val{true}} where T<:UnicodePlots.HeatmapCanvas, UnicodePlots.Plot{T, Val{false}} where T<:UnicodePlots.HeatmapCanvas}\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::ShallowWater\n) -> Union{Nothing, Bool}\ntimestep!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n dt::Real,\n model::ShallowWater,\n lf1::Integer\n) -> Union{Nothing, Bool}\ntimestep!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n dt::Real,\n model::ShallowWater,\n lf1::Integer,\n lf2::Integer\n) -> Union{Nothing, Bool}\n\n\nCalculate a single time step for the model <: ShallowWater.\n\n\n\n\n\n","category":"function"},{"location":"functions/#SpeedyWeather.timestep!-2","page":"Function and type index","title":"SpeedyWeather.timestep!","text":"timestep!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n dt::Real,\n model::Barotropic\n) -> Union{Nothing, Bool}\ntimestep!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n dt::Real,\n model::Barotropic,\n lf1::Integer\n) -> Union{Nothing, Bool}\ntimestep!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n dt::Real,\n model::Barotropic,\n lf1::Integer,\n lf2::Integer\n) -> Union{Nothing, Bool}\n\n\nCalculate a single time step for the barotropic model.\n\n\n\n\n\n","category":"function"},{"location":"functions/#SpeedyWeather.timestep!-3","page":"Function and type index","title":"SpeedyWeather.timestep!","text":"timestep!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n dt::Real,\n model::PrimitiveEquation\n) -> Union{Nothing, Bool}\ntimestep!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n dt::Real,\n model::PrimitiveEquation,\n lf1::Integer\n) -> Union{Nothing, Bool}\ntimestep!(\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n dt::Real,\n model::PrimitiveEquation,\n lf1::Integer,\n lf2::Integer\n) -> Union{Nothing, Bool}\n\n\nCalculate a single time step for the model<:PrimitiveEquation\n\n\n\n\n\n","category":"function"},{"location":"functions/#SpeedyWeather.tree-Tuple{AbstractModel}","page":"Function and type index","title":"SpeedyWeather.tree","text":"tree(M::AbstractModel; modules, with_size, kwargs...)\n\n\nCreate a tree of fields inside a model and fields within these fields as long as they are defined within the modules argument (default SpeedyWeather). Other keyword arguments are max_level::Integer=10, with_types::Bool=false or `with_size::Bool=false.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.tree-Tuple{Any}","page":"Function and type index","title":"SpeedyWeather.tree","text":"tree(S; modules, with_size, kwargs...)\n\n\nCreate a tree of fields inside S and fields within these fields as long as they are defined within the modules argument (default SpeedyWeather). Other keyword arguments are max_level::Integer=10, with_types::Bool=false or `with_size::Bool=false.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.tree-Union{Tuple{Simulation{M}}, Tuple{M}} where M","page":"Function and type index","title":"SpeedyWeather.tree","text":"tree(S::Simulation{M}; modules, with_size, kwargs...)\n\n\nCreate a tree of fields inside a Simulation instance and fields within these fields as long as they are defined within the modules argument (default SpeedyWeather). Other keyword arguments are max_level::Integer=10, with_types::Bool=false or `with_size::Bool=false.\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{DiagnosticVariables}","page":"Function and type index","title":"SpeedyWeather.unscale!","text":"unscale!(diagn::DiagnosticVariables) -> Int64\n\n\nUndo the radius-scaling of vorticity and divergence from scale!(diagn, scale::Real).\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!-Tuple{DiagnosticVariables, PrognosticVariables, Integer, Geometry}","page":"Function and type index","title":"SpeedyWeather.vertical_integration!","text":"vertical_integration!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n lf::Integer,\n geometry::Geometry\n)\n\n\nCalculates the vertically averaged (weighted by the thickness of the σ level) velocities (*coslat) and divergence. E.g.\n\nu_mean = ∑_k=1^nlayers Δσ_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.vertical_interpolate!-Tuple{Vector, Vector, Geometry}","page":"Function and type index","title":"SpeedyWeather.vertical_interpolate!","text":"vertical_interpolate!(\n A_half::Vector,\n A_full::Vector,\n G::Geometry\n)\n\n\nGiven a vector in column defined at full levels, do a linear interpolation in log(σ) to calculate its values at half-levels, skipping top (k=1/2), extrapolating to bottom (k=nlayers+1/2).\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.virtual_temperature!-Tuple{DiagnosticVariables, PrimitiveDry}","page":"Function and type index","title":"SpeedyWeather.virtual_temperature!","text":"virtual_temperature!(\n diagn::DiagnosticVariables,\n model::PrimitiveDry\n)\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.virtual_temperature!-Tuple{DiagnosticVariables, PrimitiveWet}","page":"Function and type index","title":"SpeedyWeather.virtual_temperature!","text":"virtual_temperature!(\n diagn::DiagnosticVariables,\n model::PrimitiveWet\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.volume_flux_divergence!-Tuple{DiagnosticVariables, SpeedyWeather.AbstractOrography, SpeedyWeather.AbstractAtmosphere, SpeedyWeather.AbstractGeometry, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.volume_flux_divergence!","text":"volume_flux_divergence!(\n diagn::DiagnosticVariables,\n orog::SpeedyWeather.AbstractOrography,\n atmosphere::SpeedyWeather.AbstractAtmosphere,\n G::SpeedyWeather.AbstractGeometry,\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{DiagnosticVariables, PrimitiveEquation}","page":"Function and type index","title":"SpeedyWeather.vordiv_tendencies!","text":"vordiv_tendencies!(\n diagn::DiagnosticVariables,\n model::PrimitiveEquation\n)\n\n\nFunction barrier to unpack model.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.vordiv_tendencies!-Tuple{DiagnosticVariables, SpeedyWeather.AbstractCoriolis, SpeedyWeather.AbstractAtmosphere, SpeedyWeather.AbstractGeometry, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.vordiv_tendencies!","text":"vordiv_tendencies!(\n diagn::DiagnosticVariables,\n coriolis::SpeedyWeather.AbstractCoriolis,\n atmosphere::SpeedyWeather.AbstractAtmosphere,\n geometry::SpeedyWeather.AbstractGeometry,\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.vorticity_flux!-Tuple{DiagnosticVariables, Barotropic}","page":"Function and type index","title":"SpeedyWeather.vorticity_flux!","text":"vorticity_flux!(\n diagn::DiagnosticVariables,\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{DiagnosticVariables, ShallowWater}","page":"Function and type index","title":"SpeedyWeather.vorticity_flux!","text":"vorticity_flux!(\n diagn::DiagnosticVariables,\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{DiagnosticVariables, SpeedyWeather.AbstractCoriolis, Geometry, SpectralTransform}","page":"Function and type index","title":"SpeedyWeather.vorticity_flux_curldiv!","text":"vorticity_flux_curldiv!(\n diagn::DiagnosticVariables,\n coriolis::SpeedyWeather.AbstractCoriolis,\n geometry::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(\n device::SpeedyWeather.AbstractDevice\n) -> Int64\n\n\nReturns a workgroup size depending on device. 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, SpeedyWeather.AbstractPlanet, Integer}","page":"Function and type index","title":"SpeedyWeather.write_column_tendencies!","text":"write_column_tendencies!(\n diagn::DiagnosticVariables,\n column::ColumnVariables,\n planet::SpeedyWeather.AbstractPlanet,\n ij::Integer\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_restart_file-Union{Tuple{T}, Tuple{SpeedyWeather.AbstractOutput, PrognosticVariables{T}}} where T","page":"Function and type index","title":"SpeedyWeather.write_restart_file","text":"write_restart_file(\n output::SpeedyWeather.AbstractOutput,\n progn::PrognosticVariables{T}\n) -> Any\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.year_angle-Union{Tuple{T}, Tuple{Type{T}, DateTime, Second, Second}} where T","page":"Function and type index","title":"SpeedyWeather.year_angle","text":"year_angle(\n _::Type{T},\n time::DateTime,\n length_of_day::Second,\n length_of_year::Second\n) -> Any\n\n\nFraction of year as angle in radians [0...2π]. TODO: Takes length of day/year as argument, but calls to Dates.Time(), Dates.dayofyear() currently have these hardcoded.\n\n\n\n\n\n","category":"method"},{"location":"functions/#SpeedyWeather.σ_interpolation_weights-Tuple{AbstractVector, AbstractVector}","page":"Function and type index","title":"SpeedyWeather.σ_interpolation_weights","text":"σ_interpolation_weights(\n σ_levels_full::AbstractVector,\n σ_levels_half::AbstractVector\n) -> Any\n\n\nInterpolation weights for full to half level interpolation on sigma coordinates. Following Fortran SPEEDY documentation eq. (1).\n\n\n\n\n\n","category":"method"},{"location":"particles/#Particle-advection","page":"Particle advection","title":"Particle advection","text":"","category":"section"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"All SpeedyWeather.jl models support particle advection. Particles are objects without mass or volume at a location mathbfx = (lambda theta sigma) (longitude lambda, latitude theta, vertical sigma coordinate sigma, see Sigma coordinates) that are moved with the wind mathbfu(mathbfx) at that location. The location of the p-th particle changes as follows","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"fracd mathbfx_pd t = mathbfu(mathbfx_p)","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"This equation applies in 2D, i.e. mathbfx = (lambda theta) and mathbfu = (u v) or in 3D, but at the moment only 2D advection is supported. In the Primitive equation model the vertical layer on which the advection takes place has to be specified. It is therefore not advected with the vertical velocity but maintains a constant pressure ratio compared to the surface pressure (sigma is constant).","category":"page"},{"location":"particles/#Discretization-of-particle-advection","page":"Particle advection","title":"Discretization of particle advection","text":"","category":"section"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"The particle advection equation has to be discretized to be numerically solved. While the particle location can generally be anywhere on the sphere, the velocity mathbfu is only available on the discrete grid points of the simulation, such that mathbfu(mathbfx_p) requires an interpolation in order to obtain a velocity at the particles location mathbfx_p to move it around. Dropping the subscript p in favour a subscript i denoting the time step, with Euler forward the equation can be discretized as ","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"mathbfx_i+1 = mathbfx_i + Delta tmathbfu_i (mathbfx_i)","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"Meaning we have used the velocity field at both departure time i and departure location mathbfx_i to update a particle's location which makes this scheme first order accurate. But only a single interpolation of the velocity field, which, in fact, is one per dimension, is necessary. Note that the time step Delta t here and the time step to solve the dynamics do not have to be identical. We could use a larger time step for the particle advection then to solve the dynamics inside the model, and because the stability criteria for these equations are different, one is encouraged to do so. Also because the particles are considered passive, meaning that their location does not influence the other prognostic variables.","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"We can write down a more accurate scheme at the cost of a second interpolation step. The Heun method, also called predictor-corrector is 2nd order accurate and uses an average of the velocity at departure time i and location mathbfx_i and at a (predicted meaning preliminary) arrival point x^star_i+1 and arrival time i+1.","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"beginaligned\nmathbfx^star_i+1 = mathbfx_i + Delta tmathbfu_i (mathbfx_i) \nmathbfx_i+1 = mathbfx_i + fracDelta t2left(\n mathbfu_i (mathbfx_i) + mathbfu_i+1 (mathbfx^star_i+1)right)\nendaligned","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"Because we don't have mathbfu_i+1 available at time i, we perform this integration retrospectively, i.e. if the other model dynamics have reached time i+1 then we let the particle advection catch up by integrating them from i to i+1. This, however, requires some storage of the velocity mathbfu_i at the previous advection time step. Remember that this does not need to be the time step for the momentum equations and could be much further in the past. We could either store mathbfu_i as a grid-point field or only its interpolated values. In the case of fewer particles than grid points the latter is more efficient and this is also what we do in SpeedyWeather. Let square brackets denote an interpolation then we perform the interpolation mathbfu_i mathbfx_i that's required to step from i to i+1 already on the time step that goes from i-1 to i. ","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"beginaligned\nmathbfx^star_i+1 = mathbfx_i + Delta tmathbfu_i (mathbfx_i) \nmathbfx_i+1 = mathbfx_i + fracDelta t2left(\n mathbfu_i (mathbfx_i) + mathbfu_i+1 mathbfx^star_i+1right) \nmathbfu_i+1 (mathbfx_i+1) = mathbfu_i+1 mathbfx_i+1\nendaligned","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"Denoted here as the last line with the left-hand side becoming the last term of the first line in the next time step i+1 to i+2. Now it becomes clearer that there are two interpolations required on every time step.","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"We use for horizontal coordinates degrees, such that we need to scale the time step Delta with frac3602pi R (radius R) for advection in latitude and with frac3602pi R cos(theta) for advection in longitude (because the distance between meridians decreases towards the poles). We move the division by the radius conveniently into the time step as are also the momentum equations scaled with the radius, see Radius scaling.","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"Technically, a particle moved with a given velocity follows a great circle in spherical coordinates. This means that","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"theta_i+1 approx theta_i + fracDelta tR frac3602pi v_i","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"becomes a bad approximation when the time step and or the velocity are large. However, for simplicity and to avoid the calculation of the great circle we currently do use this to move particles with a given velocity. We essentially assume a local cartesian coordinate system instead of the geodesics in spherical coordinates. However, for typical time steps of 1 hour and velocities not exceeding 100 m/s the error is not catastrophic and can be reduced with a shorter time step. We may switch to great circle calculations in future versions.","category":"page"},{"location":"particles/#Create-a-particle","page":"Particle advection","title":"Create a particle","text":"","category":"section"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"So much about the theory","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"A Particle at location 10˚E and 30˚N (and sigma = 0) can be created as follows,","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"using SpeedyWeather\np = Particle(lon=10, lat=30, σ=0)\np = Particle(lon=10, lat=30)\np = Particle(10, 30, 0)\np = Particle(10, 30)","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"All of the above are equivalent. Unless a keyword argument is used, longitude is the first argument, followed by latitude (necessary), followed by sigma (can be omitted). Longitudes can be -180˚E to 180˚E or 0 to 360˚E, latitudes have to be -90˚N to 90˚N. You can create a particle with coordinates outside of these ranges (and no error or warning is thrown) but during particle advection they will be wrapped into [0, 360˚E] and [-90˚N, 90˚N], using the mod(::Particle) function, which is similar to the modulo operator but with the second argument hardcoded to the coordinate ranges from above, e.g.","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"mod(Particle(lon=-30, lat=0))","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"which also takes into account pole crossings which adds 180˚ in longitude","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"mod(Particle(lon=0, lat=100))","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"as if the particle has moved across the pole. That way all real values for longitude and latitude are wrapped into the reference range [0, 360˚E] and [-90˚N, 90˚N].","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"info: Particles are immutable\nParticles are implemented as immutable struct, meaning you cannot change their position by particle.lon = value. You have to think of them as integers or floats instead. If you have a particle p and you want to change its position to the Equator for example you need to create a new one new_particle = Particle(p.lon, 0, p.σ).","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"By default Float32 is used, but providing coordinates in Float64 will promote the type accordingly. Also by default, particles are active which is indicated by the 2nd parametric type of Particle, a boolean. Active particles are moved following the equation above, but inactive particles are not. You can activate or deactivate a particle like so","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"deactivate(p)","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"and so","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"activate(p)","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"or check its activity by active(::Particle) returning true or false. The zero-element of the Particle type is","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"zero(Particle)","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"and you can also create a random particle which uses a raised cosine distribution in latitude for an equal area-weighted uniform distribution over the sphere","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"rand(Particle{Float32}) # specify number format\nrand(Particle{Float32, true}) # and active/inactive\nrand(Particle) # or not (defaults used instead)","category":"page"},{"location":"particles/#Advecting-particles","page":"Particle advection","title":"Advecting particles","text":"","category":"section"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"The Particle type can be used inside vectors, e.g.","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"zeros(Particle{Float32}, 3)\nrand(Particle{Float64}, 5)","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"which is how particles are represented inside a SpeedyWeather Simulation. Note that we have not specified whether the particles inside these vectors are active (e.g. Particle{Float32, true}) or inactive (e.g. Particle{Float64, false}) because that would generally force all particles in these vectors to be either active or inactive as specified such that","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"v = zeros(Particle{Float32, false}, 3)\nv[1] = Particle(lon = 134.0, lat = 23) # conversion to inactive Particle{Float32, false}\nv","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"would not just convert from Float64 to Float32 but also from an active to an inactive particle. In SpeedyWeather all particles can be activated or deactivated at any time.","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"First, you create a SpectralGrid with the nparticles keyword","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"spectral_grid = SpectralGrid(nparticles = 3)","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"Then the particles live as Vector{Particle} inside the prognostic variables","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"model = BarotropicModel(spectral_grid)\nsimulation = initialize!(model)\nsimulation.prognostic_variables.particles","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"Which are placed in random locations (using rand) initially. In order to change these (e.g. to set the initial conditions) you do","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"simulation.prognostic_variables.particles[1] = Particle(lon=-120, lat=45)\nsimulation.prognostic_variables.particles","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"which sets the first particle (you can think of the index as the particle identification) to some specified location, or you could deactivate a particle with","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"first_particle = simulation.prognostic_variables.particles[1]\nsimulation.prognostic_variables.particles[1] = deactivate(first_particle)\nsimulation.prognostic_variables.particles","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"To actually advect these particles inside a SpeedyWeather simulation we have to create a ParticalAdvection2D instance that lets you control the time step used for particle advection and which vertical layer to use in the 3D models.","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"particle_advection = ParticleAdvection2D(spectral_grid, layer = 1)","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"we choose the first (=top-most) layer although this is the default anyway. Now we can advect our three particles we have defined above","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"model = BarotropicModel(spectral_grid; particle_advection)\nsimulation = initialize!(model)\nsimulation.prognostic_variables.particles","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"Which are the initial conditions for our three particles. After 10 days of simulation they have changed","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"run!(simulation, period=Day(10))\nsimulation.prognostic_variables.particles","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"Woohoo! We just advected some particles. This is probably not as exciting as actually tracking the particles over the globe and being able to visualise their trajectory which we will do in the next section","category":"page"},{"location":"particles/#Tracking-particles","page":"Particle advection","title":"Tracking particles","text":"","category":"section"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"A ParticleTracker is implemented as a callback, see Callbacks, outputting the particle locations via netCDF. We can create it like","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(nparticles = 100, nlayers=1)\nparticle_tracker = ParticleTracker(spectral_grid, schedule=Schedule(every=Hour(3)))","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"which would output every 3 hours (the default). This output frequency might be slightly adjusted depending on the time step of the dynamics to output every n time steps (an @info is thrown if that is the case), see Schedules. Further options on compression are available as keyword arguments ParticleTracker(spectral_grid, keepbits=15) for example. The callback is then added after the model is created","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"particle_advection = ParticleAdvection2D(spectral_grid)\nmodel = ShallowWaterModel(spectral_grid; particle_advection)\nadd!(model.callbacks, particle_tracker)","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"which will give it a random key too in case you need to remove it again (more on this in Callbacks). If you now run the simulation the particle tracker is called on particle_tracker.every_n_timesteps and it continuously writes into particle_tracker.netcdf_file which is placed in the run folder similar to other NetCDF output. For example, the run id can be obtained after the simulation by model.output.id.","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"simulation = initialize!(model)\nrun!(simulation, period=Day(10))\nmodel.output.id","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"so that you can read the netCDF file with","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"using NCDatasets\nrun_id = \"run_$(model.output.id)\" # create a run_???? string with output id\npath = joinpath(run_id, particle_tracker.file_name) # by default \"run_????/particles.nc\"\nds = NCDataset(path)\nds[\"lon\"]\nds[\"lat\"]","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"where the last two lines are lazy loading a matrix with each row a particle and each column a time step. You may do ds[\"lon\"][:,:] to obtain the full Matrix. We had specified spectral_grid.nparticles above and we will have time steps in this file depending on the period the simulation ran for and the particle_tracker.Δt output frequency. We can visualise the particles' trajectories with","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"lon = ds[\"lon\"][:,:]\nlat = ds[\"lat\"][:,:]\nnparticles = size(lon,1)\n\nusing CairoMakie\nfig = lines(lon[1, :], lat[1, :]) # first particle only\n[lines!(fig.axis, lon[i,:], lat[i,:]) for i in 2:nparticles] # add lines for other particles\n\n# display updated figure\nfig\nsave(\"particles.png\", fig) # hide\nnothing # hide","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"(Image: Particle trajectories)","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"Instead of providing a polished example with a nice projection we decided to keep it simple here because this is probably how you will first look at your data too. As you can see, some particles in the Northern Hemisphere have been advected with a zonal jet and perform some wavy motions as the jet does too. However, there are also some horizontal lines which are automatically plotted when a particles travels across the prime meridian 0˚E = 360˚E. Ideally you would want to use a more advanced projection and plot the particle trajectories as geodetics. ","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"With GeoMakie.jl you can do","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"using GeoMakie, CairoMakie\n\nfig = Figure()\nga = GeoAxis(fig[1, 1]; dest = \"+proj=ortho +lon_0=45 +lat_0=45\")\n[lines!(ga, lon[i,:], lat[i,:]) for i in 1:nparticles]\nfig\nsave(\"particles_geomakie.png\", fig) # hide\nnothing # hide","category":"page"},{"location":"particles/","page":"Particle advection","title":"Particle advection","text":"(Image: Particle advection)","category":"page"},{"location":"forcing_drag/#Custom-forcing-and-drag","page":"Forcing and drag","title":"Custom forcing and drag","text":"","category":"section"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"beginaligned\nfracpartial zetapartial t + nabla cdot (mathbfu(zeta + f)) =\nS - rzeta - nunabla^4zeta \nS_l m^i = A(1-exp(-2tfracDelta ttau))Q^i_l m + exp(-tfracdttau)S_l m^i-1 \nendaligned","category":"page"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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 -1 1 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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"using SpeedyWeather\n\n@kwdef struct StochasticStirring{NF} <: SpeedyWeather.AbstractForcing\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::Second = Day(2)\n\n \"Stirring strength A [1/s²]\"\n strength::NF = 2e-11\n\n \"Stirring latitude [˚N]\"\n latitude::NF = 45\n\n \"Stirring width [˚]\"\n width::NF = 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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"So, first the scalar parameters, are added as fields of type NF (you could harcode Float64 too) 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 @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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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 (it's position) and a width which are therefore also added to the definition of StochasticStirring.","category":"page"},{"location":"forcing_drag/#Custom-forcing:-generator-function","page":"Forcing and drag","title":"Custom forcing: generator function","text":"","category":"section"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"Cool. Now you could create our new StochasticStirring forcing with StochasticStirring{Float64}(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 and in double precision Float64. 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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"function StochasticStirring(SG::SpectralGrid; kwargs...)\n (; trunc, nlat) = SG\n return StochasticStirring{SG.NF}(; trunc, nlat, kwargs...)\nend","category":"page"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"Which allows us to do","category":"page"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"spectral_grid = SpectralGrid(trunc=42, nlayers=1)\nstochastic_stirring = StochasticStirring(spectral_grid, latitude=30, decorrelation_time=Day(5))","category":"page"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/#Custom-forcing:-initialize","page":"Forcing and drag","title":"Custom forcing: initialize","text":"","category":"section"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"function SpeedyWeather.initialize!( forcing::StochasticStirring,\n model::AbstractModel)\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.value # in 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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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 AbstractModel 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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/#Custom-forcing:-forcing!-function","page":"Forcing and drag","title":"Custom forcing: forcing! function","text":"","category":"section"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"function SpeedyWeather.forcing!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n forcing::StochasticStirring,\n lf::Integer,\n model::AbstractModel,\n)\n # function barrier only\n forcing!(diagn, forcing, model.spectral_transform)\nend","category":"page"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"The function signature (types and number of its arguments) has to be as outlined above. The first argument has to be of type DiagnosticVariables as the diagnostic variables, are the ones you want to change (likely the tendencies within) to apply a forcing. But technically you can change anything else too, although the results may be unexpected. The diagnostic variables contain the current model state in grid-point space and the tendencies (in grid and spectral space). The second argument has to be of type PrognosticVariables because, in general, the forcing may use (information from) the prognostic variables in spectral space, which includes in progn.clock.time the current time for time-dependent forcing. But all prognostic variables should be considered read-only. The third argument has to be of the type of our new custom forcing, here StochasticStirring, so that multiple dispatch calls the correct method of forcing!. The forth argument is of type AbstractModel, so that the forcing can also make use of anything inside model, e.g. model.geometry or model.planet etc. But you can be more restrictive to define a forcing only for the BarotropicModel for example, use modelBarotropic in that case. Or you could define two methods, one for Barotropic one for all other models with AbstractModel (not Barotropic as a more specific method is prioritised with multiple dispatch). The 5th argument is the leapfrog index lf which after the first time step will be lf=2 to denote that tendencies are evaluated at the current time not at the previous time (how leapfrogging works). Unless you want to read the prognostic variables, for which you need to know whether to read lf=1 or lf=2, you can ignore this (but need to include it as argument).","category":"page"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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 and clarity reasons: model might have abstract fields which the compiler cannot optimize for, but unpacking them makes that possible. And it also tells you more clearly what a function depends on. So we define the actual forcing! function that's then called as follows","category":"page"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"function forcing!(\n diagn::DiagnosticVariables,\n forcing::StochasticStirring{NF},\n spectral_transform::SpectralTransform\n) 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.a_grid # use scratch array \"a\"\n transform!(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 transform!(vor_tend, S_grid, spectral_transform)\n\n return nothing\nend","category":"page"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"Now this is actually where we implement the equation we started from in Custom forcing and drag 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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/#Custom-forcing:-model-construction","page":"Forcing and drag","title":"Custom forcing: model construction","text":"","category":"section"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"Now that we have defined a new forcing, as well as how to initialize it and what it is supposed to execute on every time step, we also want to use it. We generally follow other Examples, start with the SpectralGrid and use that to get an instance of StochasticStirring. 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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"spectral_grid = SpectralGrid(trunc=85, nlayers=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)\n\n# visualisation\nusing CairoMakie\nvor = simulation.diagnostic_variables.grid.vor_grid[:, 1]\nheatmap(vor, title=\"Stochastically stirred vorticity\")\nsave(\"stochastic_stirring.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"(Image: Stochastic stirring)","category":"page"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/#Custom-drag","page":"Forcing and drag","title":"Custom drag","text":"","category":"section"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"From the barotropic vorticity equation in Custom forcing and drag 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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"struct MyDrag <: SpeedyWeather.AbstractDrag\n # parameters and arrays\nend","category":"page"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"In general, these are the fields you can write into for new terms","category":"page"},{"location":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","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":"forcing_drag/","page":"Forcing and drag","title":"Forcing and drag","text":"These space restrictions exist because of the way how SpeedyWeather transforms between spaces to obtain tendencies.","category":"page"},{"location":"initial_conditions/#Initial-conditions","page":"Initial conditions","title":"Initial conditions","text":"","category":"section"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"The following showcases some examples of how to set the initial conditions for the prognostic variables in SpeedyWeather.jl. In essence there are three ways to do this","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"Change the arrays in simulation.prognostic_variables\nUse the set! function\nSet the initial_conditions component of a model","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"where 1 is a rather low-level and largely requires you to directly set the complex coefficients of the spherical harmonics (advanced!). So the set! function builds a convenient interface around 1 such that you don't have to know about details of grid or spectral space. 3 then collects method 1 or 2 (or a combination of both) into a single struct to \"save\" some initial conditions for one or several variables. This lets you use predefined (inside SpeedyWeather or externally) initial conditions as easy as initial_conditions = RossbyHaurwitzWave(). Let us illustrate this with some examples where we will refer back those methods simply as 1, 2, 3.","category":"page"},{"location":"initial_conditions/#Rossby-Haurwitz-wave-in-a-BarotropicModel","page":"Initial conditions","title":"Rossby-Haurwitz wave in a BarotropicModel","text":"","category":"section"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"We define a BarotropicModel of some resolution but keep all its components as default","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=63, nlayers=1)\nmodel = BarotropicModel(spectral_grid)\nsimulation = initialize!(model)","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"Now simulation.prognostic_variables contains already some initial conditions as defined by model.initial_conditions (that's method 3). Regardless of what those are, we can still mutate them before starting a simulation, but if you (re-)initialize the model, the initial conditions will be set back to what is defined in model.initial_conditions. We will illustrate the set! function now that conveniently lets you (re)set the current state of the prognostic variables:","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"The Rossby-Haurwitz wave[Williamson92] is defined as an initial condition for vorticity zeta (which is the sole prognostic variable in the barotropic vorticity model) as","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"ζ(λ θ) = 2ω*sin(θ) - K*sin(θ)*cos(θ)^m*(m^2 + 3m + 2)*cos(m*λ)","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"with longitude lambda and latitude theta. The parameters are zonal wavenumber (order) m = 4, frequencies omega = 7848e-6s^-1 K = 7848e-6s^-1. Now setting these initial conditions is as simple as","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"m = 4\nω = 7.848e-6\nK = 7.848e-6\n\nζ(λ, θ, σ) = 2ω*sind(θ) - K*sind(θ)*cosd(θ)^m*(m^2 + 3m + 2)*cosd(m*λ)\nset!(simulation, vor=ζ)","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"with only two difference from the mathematical notation. (1) SpeedyWeather's coordinates are in degrees, so we replaced sin cos with sind and cosd; and (2) To generalise to vertical coordinates, the function ζ(λ, θ, σ) takes exactly three arguments, with σ denoting the vertical Sigma coordinates. This is important so that we can use the same definition of initial conditions for the 2D barotropic vorticity model also for the 3D primitive equations.","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"One may filter out low values of spectral vorticity with some cut-off amplitude c = 10^-10, just to illustrate how you would do this (example for method 1)","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"c = 1e-10 # cut-off amplitude\n\n# 1 = first leapfrog timestep of spectral vorticity\nvor = simulation.prognostic_variables.vor[1]\nlow_values = abs.(vor) .< c\nvor[low_values] .= 0\nnothing # hide","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"which is just treating vor as an array of something and tweaking the values within!","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"Let us illustrate these initial conditions. set! will set the initial conditions in spectral space, taking care of the transform from the equation defined in grid coordinates. So to show vorticity again in grid space we transform back","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"# [1] for first leapfrog time step, [:, 1] for all values on first layer\nvor = simulation.prognostic_variables.vor[1][:, 1]\nvor_grid = transform(vor)\n\nusing CairoMakie\nheatmap(vor_grid, title=\"Relative vorticity [1/s] of Rossby-Haurwitz wave\")\n\nsave(\"haurwitz.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"(Image: Rossby-Haurwitz wave)","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"That's the Rossby-Haurwitz wave! This wave is supposed to travel (without changing its shape) eastward around the globe, so let us run a simulation for some days","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"run!(simulation, period=Day(3))\n\n# a running simulation always transforms spectral variables\n# so we don't have to do the transform manually but just pull \n# layer 1 (there's only 1) from the diagnostic variables\nvor = simulation.diagnostic_variables.grid.vor_grid[:, 1]\n\nheatmap(vor, title=\"Relative vorticity [1/s], Rossby Haurwitz wave after 3 days\")\nsave(\"haurwitz_day10.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"(Image: Rossby-Haurwitz wave after day 10)","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"Looks like before, but shifted eastward! That's the Rossby-Haurwitz wave. The set! function accepts not just a function as outlined above, but also scalars, like set!(simulation, div=0) (which is always the case in the BarotropicModel) or grids, or LowerTriangularArrays representing variables in the spectral space. See ?set!, the set! docstring for more details.","category":"page"},{"location":"initial_conditions/#Rossby-Haurwitz-wave-in-a-ShallowWater-model","page":"Initial conditions","title":"Rossby-Haurwitz wave in a ShallowWater model","text":"","category":"section"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"For the shallow water model Williamson et al., 1992[Williamson92] also give initial conditions for the prognostic variable height h = h_0 + eta (equivalent to geopotential). The layer thickness is h_0 and eta is the interface displacement of that layer. SpeedyWeather however, uses as prognostic variable eta for which the initial conditions are","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"beginalign\nη(λ θ) = fracR^2g left( A(θ) + B(θ) cos(mλ) + C(θ) cos(2mλ) right) \n\nA(θ) = fracω(2Ω + ω)2cos(θ)^2 + frac14K^2cos(θ)^2mleft((m+1)cos(θ)^2 + (2m^2 - m - 2) - frac2m^2cos(θ)^2right) \n\nB(θ) = frac2(Ω + ω)K(m+1)(m+2) cos(θ)^mleft((m^2 + 2m + 2) - (m+1)^2cos(θ)^2right) \n\nC(θ) = frac14K^2 cos(θ)^2mleft((m+1)cos(θ)^2 - (m + 2)right)\n\nendalign","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"Where R is the radius of the planet on which we consider the Rossby-Haurwitz wave, this value can be found in model.spectral_grid.radius and Ω and g are the rotation and the gravity constant of the planet, which can be found in model.planet.rotation and model.planet.gravity.","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"The interface displacement eta in SpeedyWeather's ShallowWaterModel is stored in the variable pres in analogy to the actual pressure in the PrimitiveEquation model. So we can set eta using set!(simulation, pres=η) for an appropriate implementation of the above equations, similar to how ζ(λ, θ, σ) = is defined above. However, we also already defined RossbyHaurwitzWave to do exactly that. With the following we can do a test run of the Rossby-Haurwitz wave in the shallow water model without any influences from orography.","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"spectral_grid = SpectralGrid(trunc=63, nlayers=1)\ninitial_conditions = RossbyHaurwitzWave()\norography = NoOrography(spectral_grid)\nmodel = ShallowWaterModel(; spectral_grid, initial_conditions, orography)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(8))\n\nvor = simulation.diagnostic_variables.grid.vor_grid[:, 1]\nheatmap(vor, title=\"Relative vorticity [1/s], shallow water Rossby Haurwitz wave after 8 days\")\nsave(\"haurwitz_sw.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"There is a noticable difference from the result in the barotropic model, where the wave moves around the globe keeping its shape. Here, it deforms around the poles and the vorticity patches develop an internal structure.","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"(Image: Rossby-Haurwitz wave in the shallow water model)","category":"page"},{"location":"initial_conditions/#Rossby-Haurwitz-wave-in-primitive-equations","page":"Initial conditions","title":"Rossby-Haurwitz wave in primitive equations","text":"","category":"section"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"You can use set! or the predefined RossbyHaurwitzWave also in other models. For the BarotropicModel or the PrimitiveDryModel (or Wet) the definition for eta is skipped. The barotropic model does not have a pressure variable, the primitive equation model uses the logarithm of surface pressure, which is incompatible with eta being defined as an interface displacement. So here, the RossbyHaurwitzWave only includes relative vorticity in the initial conditions.","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"The following shows how you can use RossbyHaurwitzWave in a PrimitiveDryModel (or Wet) but you probably also want to set initial conditions for temperature and pressure to not start at zero Kelvin and zero pressure. Also no orography, and let's switch off all physics parameterizations with physics=false.","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"spectral_grid = SpectralGrid(trunc=42, nlayers=8)\ninitial_conditions = InitialConditions(\n vordiv=RossbyHaurwitzWave(),\n temp=JablonowskiTemperature(),\n pres=PressureOnOrography())\n\norography = NoOrography(spectral_grid)\ntime_stepping = Leapfrog(spectral_grid, Δt_at_T31=Minute(30)) # 30min timestep scaled linearly\n\nmodel = PrimitiveDryModel(spectral_grid; time_stepping, initial_conditions, orography, physics=false)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(5))\nnothing # hide","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"Note that we chose a lower resolution here (T42) as we are simulating 8 vertical layers now too. Let us visualise the surface vorticity ([:, 8] is the lowermost layer)","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"vor = simulation.diagnostic_variables.grid.vor_grid[:, 8]\nheatmap(vor, title=\"Relative vorticity [1/s], primitive Rossby-Haurwitz wave\")\n\nsave(\"haurwitz_primitive.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"(Image: Rossby-Haurwitz wave in primitive equations)","category":"page"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"As you can see the actual Rossby-Haurwitz wave is not as stable anymore (because those initial conditions are not a stable solution of the primitive equations) and so the 3-day integration looks already different from the barotropic model!","category":"page"},{"location":"initial_conditions/#References","page":"Initial conditions","title":"References","text":"","category":"section"},{"location":"initial_conditions/","page":"Initial conditions","title":"Initial conditions","text":"[Williamson92]: DL Williamson, JB Drake, JJ Hack, R Jakob, PN Swarztrauber, 1992. A standard test set for numerical approximations to the shallow water equations in spherical geometry, Journal of Computational Physics, 102, 1, DOI: 10.1016/S0021-9991(05)80016-6","category":"page"},{"location":"how_to_run_speedy/#How-to-run-SpeedyWeather.jl","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather.jl","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","text":"In the following we will describe these steps in more detail. If you want to start with some examples first, see Examples 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","title":"SpectralGrid","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","text":"The default SpectralGrid is","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"using SpeedyWeather\nspectral_grid = SpectralGrid()","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","text":"spectral_grid = SpectralGrid(trunc=85)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","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","title":"Vertical coordinates and resolution","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"The number of vertical layers or levels (we use both terms often interchangeably) is determined through the nlayers 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","title":"How to run SpeedyWeather","text":"spectral_grid = SpectralGrid(nlayers=1)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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 0 1","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"vertical_coordinates = SigmaCoordinates(0:0.2:1)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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","title":"Creating model components","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","text":"model = ShallowWaterModel(spectral_grid)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","text":"model.time_stepping","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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. All time inputs are supposed to be given with the help of Dates (e.g. Minute(), Hour(), ...). But remember that (almost) every model component depends on a SpectralGrid as first argument.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"spectral_grid = SpectralGrid(trunc=63, nlayers=1)\ntime_stepping = Leapfrog(spectral_grid, Δt_at_T31=Minute(15))","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","text":"model = ShallowWaterModel(spectral_grid; time_stepping)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"This logic continues for all model components, see 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","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","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","title":"Creating a model","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"SpeedyWeather.jl currently includes 4 different models:","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"BarotropicModel for the 2D barotropic vorticity equation,\nShallowWaterModel for the 2D shallow water equations,\nPrimitiveDryModel for the 3D primitive equations without humidity, and\nPrimitiveWetModel for the 3D primitive equations with humidity.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"Overall, all above-mentioned models 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","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","text":"or model = ShallowWaterModel(spectral_grid; ...), etc.","category":"page"},{"location":"how_to_run_speedy/#initialize","page":"How to run SpeedyWeather","title":"Model initialization","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","text":"simulation = initialize!(model)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"and we have initialized the ShallowWaterModel we have defined earlier. As initialize!(model) also initializes the prognostic (and diagnostic) variables, it also initializes the clock in simulation.prognostic_variables.clock. To initialize with a specific time, do","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"simulation = initialize!(model, time=DateTime(2020,5,1))\nsimulation.prognostic_variables.clock.time","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"to set the time to 1st May, 2020 (but you can also do that manually). This time is used by components that depend on time, e.g. the solar zenith angle calculation.","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"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 like so","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"simulation.model.output.output_dt = Second(3600)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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!(::AbstractModel), but you can also change them now, before the model runs ","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"simulation.prognostic_variables.vor[1][1, 1] = 0","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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","title":"Run a simulation","text":"","category":"section"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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","title":"How to run SpeedyWeather","text":"run!(simulation)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"By default this runs for 10 days without output and returns a unicode plot of surface relative vorticity (see Shallow water with mountains for more on this). Now period and output are the only options to change, so with","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","text":"model.output.id = \"test\" # hide\nrun!(simulation, period=Day(5), output=true)","category":"page"},{"location":"how_to_run_speedy/","page":"How to run SpeedyWeather","title":"How to run SpeedyWeather","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":"Spectral transforms","title":"SpeedyTransforms","text":"","category":"section"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","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/#Notation:-Spectral-resolution","page":"Spectral transforms","title":"Notation: Spectral resolution","text":"","category":"section"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"There are different ways to describe the spectral resolution, the truncation wavenumber (e.g. T31), the maximum degree l and order m of the spherical harmonics (e.g. l_max=31, m_max = 31), or the size of the lower triangular matrix, e.g. 32x32. In this example, they are all equivalent. We often use the truncation, i.e. T31, for brevity but sometimes it is important to describe degree and order independently (see for example One more degree for spectral fields). Note also how truncation, degree and order are 0-based, but matrix sizes are 1-based. ","category":"page"},{"location":"speedytransforms/#Example-transform","page":"Spectral transforms","title":"Example transform","text":"","category":"section"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","text":"using SpeedyWeather.RingGrids\nusing SpeedyWeather.LowerTriangularMatrices\nusing SpeedyWeather.SpeedyTransforms","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"As an example, we want to transform the l=m=1 spherical harmonic from spectral space in alms to grid-point space. Note, the +1 on both degree (first index) and order (second index) for 0-based harmonics versus 1-based matrix indexing, see Size of LowerTriangularArray. Create a LowerTriangularMatrix for T5 resolution, i.e. 6x6 matrix size","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"alms = zeros(LowerTriangularMatrix{ComplexF64}, 6, 6) # spectral coefficients T5\nalms[2, 2] = 1 # only l=1, m=1 harmonic\nalms","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"Now transform is the function that takes spectral coefficients alms and converts them a grid-point space map (or vice versa)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"map = transform(alms)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"By default, the transforms 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":"Spectral transforms","title":"Spectral transforms","text":"import SpeedyWeather.RingGrids: plot # not necessary when `using SpeedyWeather`\nplot(map)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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 transform","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"alms2 = transform(map)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","text":"alms ≈ alms2","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Transform onto another grid","text":"","category":"section"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","text":"map = transform(alms, Grid=HEALPixGrid)\nplot(map)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"which, if transformed back, however, can yield a larger transform error as discussed above","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"transform(map)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"On such a coarse grid the transform error (absolute and relative) is about 10^-2, this decreases for higher resolution. The transform function 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":"Spectral transforms","title":"Spectral transforms","text":"spectral_truncation(alms, 2)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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 you want to transform onto, use the keyword dealiasing (default: 2 for quadratic, see Matching spectral and grid resolution). But you can also provide a SpectralTransform instance to reuse a precomputed spectral transform. More on that now.","category":"page"},{"location":"speedytransforms/#SpectralTransform","page":"Spectral transforms","title":"The SpectralTransform struct","text":"","category":"section"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"The function transform only with arguments as shown above, will 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":"Spectral transforms","title":"Spectral transforms","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(NF=Float32, trunc=5, Grid=OctahedralGaussianGrid, dealiasing=3)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","text":"S = SpectralTransform(spectral_grid)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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.","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"Passing on S the SpectralTransform now allows us to transform directly on the grid defined therein. Note that we recreate alms to be of size 7x6 instead of 6x6 for T5 spectral resolution because SpeedyWeather uses internally One more degree for spectral fields meaning also that's the default when creating a SpectralTransform from a SpectralGrid. But results don't change if the last degree (row) contains only zeros.","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"alms = zeros(LowerTriangularMatrix{ComplexF64}, 7, 6) # spectral coefficients\nalms[2, 2] = 1 # only l=1, m=1 harmonic\n\nmap = transform(alms, S)\nplot(map)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","text":"alms2 = transform(map, S)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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). While 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":"Spectral transforms","title":"Spectral transforms","text":"spectral_truncation(alms2, 5, 5)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","text":"S = SpectralTransform(spectral_grid, one_more_degree=false)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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/#SpectralTransform-generators","page":"Spectral transforms","title":"SpectralTransform generators","text":"","category":"section"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"While you can always create a SpectralTransform from a SpectralGrid (which defines both spectral and grid space) there are other constructors/generators available:","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"SpectralTransform(alms)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"Now we have defined the resolution of the spectral space through alms but create a SpectralTransform by making assumption about the grid space. E.g. Grid=FullGaussianGrid by default, dealiasing=2 and nlat_half correspondingly. But you can also pass them on as keyword arguments, for example","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"SpectralTransform(alms, Grid=OctahedralClenshawGrid, nlat_half=24)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"Only note that you don't want to specify both nlat_half and dealiasing as you would otherwise overspecify the grid resolution (dealiasing will be ignored in that case). This also works starting from the grid space","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"grid = rand(FullClenshawGrid, 12)\nSpectralTransform(grid)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"where you can also provide spectral resolution trunc or dealiasing. You can also provide both a grid and a lower triangular matrix to describe both spaces","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"SpectralTransform(grid, alms)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"and you will precompute the transform between them. For more generators see the docstrings at ?SpectralTransform.","category":"page"},{"location":"speedytransforms/#Power-spectrum","page":"Spectral transforms","title":"Power spectrum","text":"","category":"section"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","text":"alms = randn(LowerTriangularMatrix{Complex{Float32}}, 32, 32) # hide\nspectral_truncation!(alms, 10) # hide\nmap = transform(alms, Grid=FullClenshawGrid) # hide\nm = Matrix(map) # hide\nm","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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 (in analogy to the Gaussian grid based on the Gaussian quadrature). Note that for the spectral transform this should not include the poles, so the 96x47 matrix size here corresponds to 23 latitudes north and south of the Equator respectively plus the equator (=47).","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"We now wrap this matrix into a FullClenshawGrid (input_as=Matrix is required because all grids organise their data as vectors, see Creating data on a RingGrid) therefore to associate it with the necessary grid information like its coordinates","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"map = FullClenshawGrid(m, input_as=Matrix)\n\nusing CairoMakie\nheatmap(map)\nsave(\"random_pattern.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"(Image: Random pattern)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"Now we transform into spectral space and call power_spectrum(::LowerTriangularMatrix)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"alms = transform(map)\npower = power_spectrum(alms)\nnothing # hide","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Example: Creating k^n-distributed noise","text":"","category":"section"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","text":"k = 1:32\nA = randn(Complex{Float32}, 32, 32)\nA .*= k.^-2\nalms = LowerTriangularArray(A)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"We first create a Julia Matrix so that the matrix-vector broadcasting .*= k is correctly applied across dimensions of A and then convert to a LowerTriangularMatrix.","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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":"Spectral transforms","title":"Spectral transforms","text":"map = transform(alms)\n\nusing CairoMakie\nheatmap(map, title=\"k⁻²-distributed noise\")\nsave(\"random_noise.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"(Image: Random noise)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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/#Precomputed-polynomials-and-allocated-memory","page":"Spectral transforms","title":"Precomputed polynomials and allocated memory","text":"","category":"section"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"info: Reuse `SpectralTransform`s wherever possible\nDepending on horizontal and vertical resolution of spectral and grid space, a SpectralTransform can be become very large in memory. Also the recomputation of the polynomials and the planning of the FFTs are expensive compared to the actual transform itself. Therefore reuse a SpectralTransform wherever possible.","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","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. It is therefore advised to reuse a precomputed SpectralTransform object wherever possible. This prevents transforms to allocate large memory which would otherwise be garbage collected again after the transform.","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"You get information about the size of that memory (both polynomials and required scratch memory) in the terminal \"show\" of a SpectralTransform object, e.g. at T127 resolution with 8 layers these are","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"spectral_grid = SpectralGrid(trunc=127, nlayers=8)\nSpectralTransform(spectral_grid)","category":"page"},{"location":"speedytransforms/#Batched-Transforms","page":"Spectral transforms","title":"Batched Transforms","text":"","category":"section"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"SpeedyTransforms also supports batched transforms. With batched input data the transform is performed along the leading dimension, and all further dimensions are interpreted as batch dimensions. Take for example ","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"alms = randn(LowerTriangularMatrix{Complex{Float32}}, 32, 32, 5) \ngrids = transform(alms)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"In this case we first randomly generated five (32x32) LowerTriangularMatrix that hold the coefficients and then transformed all five matrices batched to the grid space with the transform command, yielding 5 RingGrids with each 48-rings. ","category":"page"},{"location":"speedytransforms/#Batched-power-spectra","page":"Spectral transforms","title":"Batched power spectra","text":"","category":"section"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"SpeedyTransforms also supports power spectra calculated over any additional dimensions to the leading spherical harmonic dimension (it is unravelled as a vector so the first only, not the first two...). But the power spectrum is always calculated along that first spherical-harmonic dimension. For example","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"alms = randn(LowerTriangularMatrix{Complex{Float32}}, 5, 5, 2) \npower_spectrum(alms)","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"returns the power spectrum for [..., 1] in the first column and [..., 2] in the second. This avoids to loop over these additional dimensions, but the result would be the same:","category":"page"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"power_spectrum(alms[:, 1])","category":"page"},{"location":"speedytransforms/#Functions-and-type-index","page":"Spectral transforms","title":"Functions and type index","text":"","category":"section"},{"location":"speedytransforms/","page":"Spectral transforms","title":"Spectral transforms","text":"Modules = [SpeedyWeather.SpeedyTransforms]","category":"page"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.AbstractLegendreShortcut","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.AbstractLegendreShortcut","text":"Legendre shortcut is the truncation of the loop over the order m of the spherical harmonics in the Legendre transform. For reduced grids with as few as 4 longitudes around the poles (HEALPix grids) or 20 (octahedral grids) the higher wavenumbers in large orders m do not project (significantly) onto such few longitudes. For performance reasons the loop over m can therefore but truncated early. A Legendre shortcut <: AbstractLegendreShortcut is implemented as a functor that returns the 0-based maximum order m to retain per latitude ring, i.e. to be used for m in 0:mmax_truncation.\n\nNew shortcuts can be added by defining struct LegendreShortcutNew <: AbstractLegendreShortcut end and the functor method LegendreShortcutNew(nlon::Integer, lat::Real) = ..., with nlon the number of longitude points on that ring, and latd its latitude in degrees (-90˚ to 90˚N). Many implementations may not use the latitude latd but it is included for compatibility. If unused set to default value to 0. Also define short_name(::Type{<:LegendreShortcutNew}) = \"new\".\n\nImplementions are LegendreShortcutLinear, LegendreShortcutQuadratic, LegendreShortcutCubic, LegendreShortcutLinQuadCosLat² and LegendreShortcutLinCubCoslat.\n\n\n\n\n\n","category":"type"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.AssociatedLegendrePolArray","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.AssociatedLegendrePolArray","text":"AssociatedLegendrePolArray{T, N, M, V} <: AbstractArray{T,N}\n\nType that wraps around a LowerTriangularArray{T,M,V} but is a subtype of AbstractArray{T,M+1}. This enables easier use with AssociatedLegendrePolynomials.jl which otherwise couldn't use the \"matrix-style\" (l, m) indexing of LowerTriangularArray. This type however doesn't support any other operations than indexing and is purerly intended for internal purposes. \n\ndata::LowerTriangularArray{T, M, V} where {T, M, V}\n\n\n\n\n\n","category":"type"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.AssociatedLegendrePolMatrix","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.AssociatedLegendrePolMatrix","text":"2-dimensional AssociatedLegendrePolArray of type Twith its non-zero entries unravelled into aVector{T}`\n\n\n\n\n\n","category":"type"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.LegendreShortcutCubic","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.LegendreShortcutCubic","text":"LegendreShortcutCubic(nlon::Integer) -> Any\nLegendreShortcutCubic(nlon::Integer, latd::Real) -> Any\n\n\nCubic Legendre shortcut, truncates the Legendre loop over order m to nlon/4.\n\n\n\n\n\n","category":"type"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.LegendreShortcutLinCubCoslat-Tuple{Integer, Real}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.LegendreShortcutLinCubCoslat","text":"LegendreShortcutLinCubCoslat(\n nlon::Integer,\n latd::Real\n) -> Any\n\n\nLinear-Cubic Legendre shortcut, truncates the Legendre loop over order m to nlon/(2 + 2cosd(latd)).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.LegendreShortcutLinQuadCoslat²-Tuple{Integer, Real}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.LegendreShortcutLinQuadCoslat²","text":"LegendreShortcutLinQuadCoslat²(\n nlon::Integer,\n latd::Real\n) -> Any\n\n\nLinear-Quadratic Legendre shortcut, truncates the Legendre loop over order m to nlon/(2 + cosd(latd)^2).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.LegendreShortcutLinear","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.LegendreShortcutLinear","text":"LegendreShortcutLinear(nlon::Integer) -> Any\nLegendreShortcutLinear(nlon::Integer, latd::Real) -> Any\n\n\nLinear Legendre shortcut, truncates the Legendre loop over order m to nlon/2.\n\n\n\n\n\n","category":"type"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.LegendreShortcutQuadratic","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.LegendreShortcutQuadratic","text":"LegendreShortcutQuadratic(nlon::Integer) -> Any\nLegendreShortcutQuadratic(nlon::Integer, latd::Real) -> Any\n\n\nQuadratic Legendre shortcut, truncates the Legendre loop over order m to nlon/3.\n\n\n\n\n\n","category":"type"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.SpectralTransform","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.SpectralTransform","text":"SpectralTransform struct that contains all parameters and precomputed arrays to perform a spectral transform. Fields are\n\nGrid::Type{<:AbstractGridArray}\nnlat_half::Int64\nnlayers::Int64\nlmax::Int64\nmmax::Int64\nnfreq_max::Int64\nLegendreShortcut::Type{<:SpeedyWeather.SpeedyTransforms.AbstractLegendreShortcut}\nmmax_truncation::Vector{Int64}\nnlon_max::Int64\nnlons::Vector{Int64}\nnlat::Int64\ncoslat::Any\ncoslat⁻¹::Any\nlon_offsets::Any\nnorm_sphere::Any\nrfft_plans::Vector{AbstractFFTs.Plan}\nbrfft_plans::Vector{AbstractFFTs.Plan}\nrfft_plans_1D::Vector{AbstractFFTs.Plan}\nbrfft_plans_1D::Vector{AbstractFFTs.Plan}\nlegendre_polynomials::Any\nscratch_memory_north::Any\nscratch_memory_south::Any\nscratch_memory_grid::Any\nscratch_memory_spec::Any\nscratch_memory_column_north::Any\nscratch_memory_column_south::Any\njm_index_size::Int64\nkjm_indices::Any\nsolid_angles::Any\ngrad_y1::Any\ngrad_y2::Any\ngrad_y_vordiv1::Any\ngrad_y_vordiv2::Any\nvordiv_to_uv_x::Any\nvordiv_to_uv1::Any\nvordiv_to_uv2::Any\neigenvalues::Any\neigenvalues⁻¹::Any\n\n\n\n\n\n","category":"type"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.SpectralTransform-Union{Tuple{AbstractGridArray{NF, N, ArrayType}}, Tuple{ArrayType}, Tuple{N}, Tuple{NF}} where {NF, N, ArrayType}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.SpectralTransform","text":"SpectralTransform(\n grids::AbstractGridArray{NF, N, ArrayType};\n trunc,\n dealiasing,\n one_more_degree,\n kwargs...\n) -> SpectralTransform{NF, _A, _B, _C, _D, _E, LowerTriangularArray{NF1, 1, _A1}, LowerTriangularArray{NF2, 2, _A2}} where {NF, _A, _B, _C, _D, _E, NF1, _A1, NF2, _A2}\n\n\nGenerator function for a SpectralTransform struct based on the size and grid type of grids. Use keyword arugments trunc, dealiasing (ignored if trunc is used) or one_more_degree to define the spectral truncation.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.SpectralTransform-Union{Tuple{ArrayType2}, Tuple{ArrayType1}, Tuple{N}, Tuple{NF2}, Tuple{NF1}, Tuple{AbstractGridArray{NF1, N, ArrayType1}, LowerTriangularArray{NF2, N, ArrayType2}}} where {NF1, NF2, N, ArrayType1, ArrayType2}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.SpectralTransform","text":"SpectralTransform(\n grids::AbstractGridArray{NF1, N, ArrayType1},\n specs::LowerTriangularArray{NF2, N, ArrayType2};\n kwargs...\n) -> SpectralTransform{NF, _A, _B, _C, _D, _E, LowerTriangularArray{NF1, 1, _A1}, LowerTriangularArray{NF2, 2, _A2}} where {NF, _A, _B, _C, _D, _E, NF1, _A1, NF2, _A2}\n\n\nGenerator function for a SpectralTransform struct to transform between grids and specs.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.SpectralTransform-Union{Tuple{LowerTriangularArray{NF, N, ArrayType}}, Tuple{ArrayType}, Tuple{N}, Tuple{NF}} where {NF, N, ArrayType}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.SpectralTransform","text":"SpectralTransform(\n specs::LowerTriangularArray{NF, N, ArrayType};\n nlat_half,\n dealiasing,\n kwargs...\n) -> SpectralTransform{NF, _A, _B, _C, _D, _E, LowerTriangularArray{NF1, 1, _A1}, LowerTriangularArray{NF2, 2, _A2}} where {NF, _A, _B, _C, _D, _E, NF1, _A1, NF2, _A2}\n\n\nGenerator function for a SpectralTransform struct based on the size of the spectral coefficients specs. Use keyword arguments nlat_half, Grid or deliasing (if nlat_half not provided) to define the grid.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.SpectralTransform-Union{Tuple{NF}, Tuple{Type{NF}, Integer, Integer, Integer}} where NF","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.SpectralTransform","text":"SpectralTransform(\n ::Type{NF},\n lmax::Integer,\n mmax::Integer,\n nlat_half::Integer;\n Grid,\n ArrayType,\n nlayers,\n LegendreShortcut\n) -> SpectralTransform{T, Array, Vector{T1}, Array{Complex{T2}, 1}, Array{Complex{T3}, 2}, Array{Complex{T4}, 3}, LowerTriangularArray{T5, 1, Vector{T6}}, LowerTriangularArray{T7, 2, Matrix{T8}}} where {T, T1, T2, T3, T4, T5, T6, T7, T8}\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 and quadrature weights.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.RingGrids.get_nlat_half","page":"Spectral transforms","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!-Tuple{LowerTriangularArray, LowerTriangularArray, LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.UV_from_vor!","text":"UV_from_vor!(\n U::LowerTriangularArray,\n V::LowerTriangularArray,\n vor::LowerTriangularArray,\n S::SpectralTransform;\n radius\n) -> Tuple{LowerTriangularArray, LowerTriangularArray}\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. Acts on the unit sphere, i.e. it omits any radius scaling as all inplace gradient operators, unless the radius keyword argument is provided.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.UV_from_vordiv!-Tuple{LowerTriangularArray, LowerTriangularArray, LowerTriangularArray, LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.UV_from_vordiv!","text":"UV_from_vordiv!(\n U::LowerTriangularArray,\n V::LowerTriangularArray,\n vor::LowerTriangularArray,\n div::LowerTriangularArray,\n S::SpectralTransform;\n radius\n) -> Tuple{LowerTriangularArray, LowerTriangularArray}\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. Acts on the unit sphere, i.e. it omits any radius scaling as all inplace gradient operators.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms._divergence!-Tuple{Any, LowerTriangularArray, LowerTriangularArray, LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms._divergence!","text":"_divergence!(\n kernel,\n div::LowerTriangularArray,\n u::LowerTriangularArray,\n v::LowerTriangularArray,\n S::SpectralTransform;\n radius\n) -> LowerTriangularArray\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. Acts on the unit sphere, i.e. it omits 1/radius scaling as all gradient operators, unless the radius keyword argument is provided.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms._fourier_batched!-Tuple{AbstractArray{<:Complex, 3}, AbstractArray{<:Complex, 3}, AbstractGridArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms._fourier_batched!","text":"_fourier_batched!(\n f_north::AbstractArray{<:Complex, 3},\n f_south::AbstractArray{<:Complex, 3},\n grids::AbstractGridArray,\n S::SpectralTransform\n)\n\n\n(Forward) Fast Fourier transform (grid to spectral) in zonal direction of grids, stored in scratch memories f_north, f_south to be passed on to the Legendre transform. Batched version that requires the number of vertical layers to be the same as precomputed in S. Not to be called directly, use transform! instead.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms._fourier_batched!-Tuple{AbstractGridArray, AbstractArray{<:Complex, 3}, AbstractArray{<:Complex, 3}, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms._fourier_batched!","text":"_fourier_batched!(\n grids::AbstractGridArray,\n g_north::AbstractArray{<:Complex, 3},\n g_south::AbstractArray{<:Complex, 3},\n S::SpectralTransform\n)\n\n\nInverse fast Fourier transform (spectral to grid) of Legendre-transformed inputs g_north and g_south to be stored in grids. Not to be called directly, use transform! instead.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms._fourier_serial!-Tuple{AbstractArray{<:Complex, 3}, AbstractArray{<:Complex, 3}, AbstractGridArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms._fourier_serial!","text":"_fourier_serial!(\n f_north::AbstractArray{<:Complex, 3},\n f_south::AbstractArray{<:Complex, 3},\n grids::AbstractGridArray,\n S::SpectralTransform\n)\n\n\n(Forward) Fast Fourier transform (grid to spectral) in zonal direction of grids, stored in scratch memories f_north, f_south to be passed on to the Legendre transform. Serial version that does not require the number of vertical layers to be the same as precomputed in S. Not to be called directly, use transform! instead.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms._fourier_serial!-Tuple{AbstractGridArray, AbstractArray{<:Complex, 3}, AbstractArray{<:Complex, 3}, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms._fourier_serial!","text":"_fourier_serial!(\n grids::AbstractGridArray,\n g_north::AbstractArray{<:Complex, 3},\n g_south::AbstractArray{<:Complex, 3},\n S::SpectralTransform\n)\n\n\n(Inverse) Fast Fourier transform (spectral to grid) of Legendre-transformed inputs g_north and g_south to be stored in grids. Serial version that does not require the number of vertical layers to be the same as precomputed in S. Not to be called directly, use transform! instead.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms._legendre!-Tuple{AbstractArray{<:Complex, 3}, AbstractArray{<:Complex, 3}, LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms._legendre!","text":"_legendre!(\n g_north::AbstractArray{<:Complex, 3},\n g_south::AbstractArray{<:Complex, 3},\n specs::LowerTriangularArray,\n S::SpectralTransform;\n unscale_coslat\n)\n\n\nInverse Legendre transform, batched in the vertical. Not to be used directly, but called from transform!.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms._legendre!-Tuple{LowerTriangularArray, AbstractArray{<:Complex, 3}, AbstractArray{<:Complex, 3}, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms._legendre!","text":"_legendre!(\n specs::LowerTriangularArray,\n f_north::AbstractArray{<:Complex, 3},\n f_south::AbstractArray{<:Complex, 3},\n S::SpectralTransform\n)\n\n\n(Forward) Legendre transform, batched in the vertical. Not to be used directly, but called from transform!.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.curl!-Tuple{LowerTriangularArray, LowerTriangularArray, LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.curl!","text":"curl!(\n curl::LowerTriangularArray,\n u::LowerTriangularArray,\n v::LowerTriangularArray,\n S::SpectralTransform;\n flipsign,\n add,\n kwargs...\n) -> LowerTriangularArray\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, otherwise curl is scaled. Acts on the unit sphere, i.e. it omits 1/radius scaling as all gradient operators unless the radius keyword argument is provided. 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{LowerTriangularArray, LowerTriangularArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.curl","text":"curl(\n u::LowerTriangularArray,\n v::LowerTriangularArray;\n kwargs...\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). Acts on the unit sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided. An example usage is therefore\n\nRingGrids.scale_coslat⁻¹!(u_grid)\nRingGrids.scale_coslat⁻¹!(v_grid)\nu = transform(u_grid)\nv = transform(v_grid)\nvor = curl(u, v, radius=6.371e6)\nvor_grid = transform(div)\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.curl-Union{Tuple{Grid}, Tuple{Grid, Grid}} where Grid<:AbstractGridArray","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.curl","text":"curl(\n u::AbstractGridArray,\n v::AbstractGridArray;\n kwargs...\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. Acts on the unit sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.divergence!-Tuple{LowerTriangularArray, LowerTriangularArray, LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.divergence!","text":"divergence!(\n div::LowerTriangularArray,\n u::LowerTriangularArray,\n v::LowerTriangularArray,\n S::SpectralTransform;\n flipsign,\n add,\n kwargs...\n) -> LowerTriangularArray\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, otherwise div is scaled. Acts on the unit sphere, i.e. it omits 1/radius scaling as all gradient operators, unless the radius keyword argument is provided. 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{LowerTriangularArray, LowerTriangularArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.divergence","text":"divergence(\n u::LowerTriangularArray,\n v::LowerTriangularArray;\n kwargs...\n) -> LowerTriangularArray\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). Acts on the unit sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided. An example usage is therefore\n\nRingGrids.scale_coslat⁻¹!(u_grid)\nRingGrids.scale_coslat⁻¹!(v_grid)\nu = transform(u_grid, one_more_degree=true)\nv = transform(v_grid, one_more_degree=true)\ndiv = divergence(u, v, radius = 6.371e6)\ndiv_grid = transform(div)\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.divergence-Union{Tuple{Grid}, Tuple{Grid, Grid}} where Grid<:AbstractGridArray","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.divergence","text":"divergence(\n u::AbstractGridArray,\n v::AbstractGridArray;\n kwargs...\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. Acts on the unit sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided.\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","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.get_recursion_factors","text":"get_recursion_factors(\n _::Type{NF},\n lmax::Int64,\n mmax::Int64\n) -> LowerTriangularArray\n\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":"Spectral transforms","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.is_power_2-Tuple{Integer}","page":"Spectral transforms","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.ismatching-Tuple{SpectralTransform, AbstractGridArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.ismatching","text":"ismatching(\n S::SpectralTransform,\n grid::AbstractGridArray\n) -> Any\n\n\nSpectral transform S and grid match if the resolution nlat_half and the type of the grid match and the number of vertical layers is equal or larger in the transform (constraints due to allocated scratch memory size).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.ismatching-Tuple{SpectralTransform, LowerTriangularArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.ismatching","text":"ismatching(\n S::SpectralTransform,\n L::LowerTriangularArray\n) -> Any\n\n\nSpectral transform S and lower triangular matrix L match if the spectral dimensions (lmax, mmax) match and the number of vertical layers is equal or larger in the transform (constraints due to allocated scratch memory size).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.plan_FFTs!-Union{Tuple{N}, Tuple{NF}, Tuple{Vector{AbstractFFTs.Plan}, Vector{AbstractFFTs.Plan}, Vector{AbstractFFTs.Plan}, Vector{AbstractFFTs.Plan}, AbstractGridArray{NF, N, <:AbstractArray{NF}}, AbstractArray{Complex{NF}}, AbstractArray, Vector{<:Int64}}} where {NF<:AbstractFloat, N}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.plan_FFTs!","text":"plan_FFTs!(\n rfft_plans::Vector{AbstractFFTs.Plan},\n brfft_plans::Vector{AbstractFFTs.Plan},\n rfft_plans_1D::Vector{AbstractFFTs.Plan},\n brfft_plans_1D::Vector{AbstractFFTs.Plan},\n fake_grid_data::AbstractGridArray{NF<:AbstractFloat, N, <:AbstractArray{NF<:AbstractFloat}},\n scratch_memory_north::AbstractArray{Complex{NF<:AbstractFloat}},\n rings::AbstractArray,\n nlons::Vector{<:Int64}\n) -> NTuple{4, Vector{AbstractFFTs.Plan}}\n\n\nUtil function to generate FFT plans based on the array type of the fake Grid data provided. Uses views, which is less allocate-y than indexing but breaks when using CuArrays (see CUDA extension for alternative implementation for CuArrays).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.power_spectrum-Tuple{LowerTriangularArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.power_spectrum","text":"power_spectrum(spec::LowerTriangularArray; normalize) -> Any\n\n\nCompute the power spectrum of the spherical harmonic coefficients spec (lower triangular matrix/array) of type Complex{NF}. For any additional dimensions in spec, the power spectrum is computed along the first/spherical harmonic dimension.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.recursion_factor-Tuple{Int64, Int64}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.recursion_factor","text":"recursion_factor(l::Int64, m::Int64) -> Float64\n\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)).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.roundup_fft-Union{Tuple{Integer}, Tuple{T}} where T<:Integer","page":"Spectral transforms","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_interpolation-Union{Tuple{ArrayType}, Tuple{N}, Tuple{T}, Tuple{NF}, Tuple{Type{NF}, LowerTriangularArray{T, N, ArrayType}, Integer, Integer}} where {NF, T, N, ArrayType}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.spectral_interpolation","text":"spectral_interpolation(\n _::Type{NF},\n alms::LowerTriangularArray{T, N, ArrayType},\n ltrunc::Integer,\n mtrunc::Integer\n) -> Any\n\n\nReturns a LowerTriangularArray that is interpolated from alms to the size (ltrunc+1) x (mtrunc+1), both inputs are 0-based, by padding zeros for higher wavenumbers. If ltrunc or mtrunc are smaller than the corresponding size ofalms than spectral_truncation is automatically called instead, returning a smaller LowerTriangularArray.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_smoothing!-Tuple{LowerTriangularArray, Real}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.spectral_smoothing!","text":"spectral_smoothing!(\n L::LowerTriangularArray,\n c::Real;\n power,\n truncation\n)\n\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{LowerTriangularArray, Real}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.spectral_smoothing","text":"spectral_smoothing(\n A::LowerTriangularArray,\n c::Real;\n power\n) -> Any\n\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}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.spectral_truncation!","text":"spectral_truncation!(\n A::AbstractMatrix\n) -> LowerTriangularArray{T, 2, ArrayType} where {T, ArrayType<:AbstractMatrix{T}}\n\n\nSets the upper triangle of A to zero.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_truncation!-Tuple{LowerTriangularArray, Integer, Integer}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.spectral_truncation!","text":"spectral_truncation!(\n alms::LowerTriangularArray,\n ltrunc::Integer,\n mtrunc::Integer\n) -> LowerTriangularArray\n\n\nTriangular truncation to degree ltrunc and order mtrunc (both 0-based). 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.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_truncation!-Tuple{LowerTriangularArray, Integer}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.spectral_truncation!","text":"spectral_truncation!(\n alms::LowerTriangularArray,\n trunc::Integer\n) -> LowerTriangularArray\n\n\nTriangular truncation of alms to degree and order trunc in-place.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_truncation!-Tuple{LowerTriangularArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.spectral_truncation!","text":"spectral_truncation!(\n alms::LowerTriangularArray\n) -> LowerTriangularArray\n\n\nTriangular truncation of alms to the size of it, sets additional rows to zero.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.spectral_truncation-Union{Tuple{ArrayType}, Tuple{N}, Tuple{T}, Tuple{NF}, Tuple{Type{NF}, LowerTriangularArray{T, N, ArrayType}, Integer, Integer}} where {NF, T, N, ArrayType}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.spectral_truncation","text":"spectral_truncation(\n _::Type{NF},\n alms::LowerTriangularArray{T, N, ArrayType},\n ltrunc::Integer,\n mtrunc::Integer\n) -> Any\n\n\nReturns a LowerTriangularArray that is truncated from alms to the size (ltrunc+1) x (mtrunc+1), both inputs are 0-based. If ltrunc or mtrunc is larger than the corresponding size ofalms than spectral_interpolation is automatically called instead, returning a LowerTriangularArray padded zero coefficients for higher wavenumbers.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.transform!-Tuple{AbstractGridArray, LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.transform!","text":"transform!(\n grids::AbstractGridArray,\n specs::LowerTriangularArray,\n S::SpectralTransform;\n unscale_coslat\n) -> AbstractGridArray\n\n\nSpectral transform (spectral to grid space) from n-dimensional array specs of spherical harmonic coefficients to an n-dimensional array grids of ring grids. Uses FFT in the zonal direction, and a Legendre Transform in the meridional direction exploiting symmetries. The spectral transform is number format-flexible but grids and the spectral transform S have to have the same number format. Uses the precalculated arrays, FFT plans and other constants in the SpectralTransform struct S. The spectral transform is grid-flexible as long as the typeof(grids)<:AbstractGridArray and S.Grid matches.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.transform!-Tuple{LowerTriangularArray, AbstractGridArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.transform!","text":"transform!(\n specs::LowerTriangularArray,\n grids::AbstractGridArray,\n S::SpectralTransform\n) -> LowerTriangularArray\n\n\nSpectral transform (grid to spectral space) from n-dimensional array of grids to an n-dimensional array specs of spherical harmonic coefficients. Uses FFT in the zonal direction, and a Legendre Transform in the meridional direction exploiting symmetries. The spectral transform is number format-flexible but grids and the spectral transform S have to have the same number format. Uses the precalculated arrays, FFT plans and other constants in the SpectralTransform struct S. The spectral transform is grid-flexible as long as the typeof(grids)<:AbstractGridArray and S.Grid matches.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.transform-Tuple{AbstractGridArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.transform","text":"transform(grids::AbstractGridArray; kwargs...) -> Any\n\n\nSpectral transform (grid to spectral space) from grids to a newly allocated LowerTriangularArray. Based on the size of grids and the keyword dealiasing the spectral resolution trunc is retrieved. SpectralTransform struct S is allocated to execute transform(grids, S).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.transform-Tuple{LowerTriangularArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.transform","text":"transform(\n specs::LowerTriangularArray;\n unscale_coslat,\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 transform(alms, S).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.transform-Union{Tuple{NF}, Tuple{AbstractGridArray, SpectralTransform{NF}}} where NF","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.transform","text":"transform(\n grids::AbstractGridArray,\n S::SpectralTransform{NF}\n) -> Any\n\n\nSpherical harmonic transform from grids to a newly allocated specs::LowerTriangularArray using the precomputed spectral transform S.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.transform-Union{Tuple{NF}, Tuple{LowerTriangularArray, SpectralTransform{NF}}} where NF","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.transform","text":"transform(\n specs::LowerTriangularArray,\n S::SpectralTransform{NF};\n kwargs...\n) -> Any\n\n\nSpherical harmonic transform from specs to a newly allocated grids::AbstractGridArray using the precomputed spectral transform S.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.zero_imaginary_zonal_modes!-Tuple{LowerTriangularArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.zero_imaginary_zonal_modes!","text":"zero_imaginary_zonal_modes!(\n alms::LowerTriangularArray\n) -> LowerTriangularArray\n\n\nSet imaginary component of m=0 modes (the zonal modes in the first column) to 0.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇!-Tuple{LowerTriangularArray, LowerTriangularArray, LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.∇!","text":"∇!(\n dpdx::LowerTriangularArray,\n dpdy::LowerTriangularArray,\n p::LowerTriangularArray,\n S::SpectralTransform;\n radius\n) -> Tuple{LowerTriangularArray, LowerTriangularArray}\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 1/radius scaling unless radius keyword argument is provided.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇-Tuple{AbstractGridArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.∇","text":"∇(\n grid::AbstractGridArray,\n S::SpectralTransform;\n kwargs...\n) -> Tuple{Any, Any}\n\n\nThe zonal and meridional gradient of grid. Transform to spectral space, takes the gradient and unscales the 1/coslat scaling in the gradient. Acts on the unit-sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided. Makes use of an existing spectral transform S.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇-Tuple{AbstractGridArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.∇","text":"∇(grid::AbstractGridArray; kwargs...) -> Tuple{Any, Any}\n\n\nThe zonal and meridional gradient of grid. Transform to spectral space, takes the gradient and unscales the 1/coslat scaling in the gradient. Acts on the unit-sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇-Tuple{LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.∇","text":"∇(\n p::LowerTriangularArray,\n S::SpectralTransform;\n kwargs...\n) -> Tuple{Any, Any}\n\n\nThe zonal and meridional gradient of p using an existing SpectralTransform S. Acts on the unit sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇-Tuple{LowerTriangularArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.∇","text":"∇(p::LowerTriangularArray; kwargs...) -> Tuple{Any, Any}\n\n\nThe zonal and meridional gradient of p. Precomputes a SpectralTransform S. Acts on the unit-sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇²!-Tuple{LowerTriangularArray, LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.∇²!","text":"∇²!(\n ∇²alms::LowerTriangularArray,\n alms::LowerTriangularArray,\n S::SpectralTransform;\n add,\n flipsign,\n inverse,\n radius\n) -> LowerTriangularArray\n\n\nLaplace operator ∇² applied to the spectral coefficients alms in spherical coordinates. The eigenvalues which are precomputed in S. ∇²! is the in-place version which directly stores the output in the first argument ∇²alms. Acts on the unit sphere, i.e. it omits any radius scaling as all inplace gradient operators, unless the radius keyword argument is provided.\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{LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.∇²","text":"∇²(\n alms::LowerTriangularArray,\n S::SpectralTransform;\n kwargs...\n) -> Any\n\n\nLaplace operator ∇² applied to input alms, using precomputed eigenvalues from S. Acts on the unit sphere, i.e. it omits 1/radius^2 scaling unless radius keyword argument is provided.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇²-Tuple{LowerTriangularArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.∇²","text":"∇²(alms::LowerTriangularArray; kwargs...) -> Any\n\n\nReturns the Laplace operator ∇² applied to input alms. Acts on the unit sphere, i.e. it omits 1/radius^2 scaling unless radius keyword argument is provided.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇⁻²!-Tuple{LowerTriangularArray, LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.∇⁻²!","text":"∇⁻²!(\n ∇⁻²alms::LowerTriangularArray,\n alms::LowerTriangularArray,\n S::SpectralTransform;\n add,\n flipsign,\n kwargs...\n) -> LowerTriangularArray\n\n\nCalls ∇²!(∇⁻²alms, alms, S; add, flipsign, inverse=true).\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇⁻²-Tuple{LowerTriangularArray, SpectralTransform}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.∇⁻²","text":"∇⁻²(\n ∇²alms::LowerTriangularArray,\n S::SpectralTransform;\n kwargs...\n) -> Any\n\n\nInverseLaplace operator ∇⁻² applied to input alms, using precomputed eigenvalues from S. Acts on the unit sphere, i.e. it omits radius^2 scaling unless radius keyword argument is provided.\n\n\n\n\n\n","category":"method"},{"location":"speedytransforms/#SpeedyWeather.SpeedyTransforms.∇⁻²-Tuple{LowerTriangularArray}","page":"Spectral transforms","title":"SpeedyWeather.SpeedyTransforms.∇⁻²","text":"∇⁻²(∇²alms::LowerTriangularArray; kwargs...) -> Any\n\n\nReturns the inverse Laplace operator ∇⁻² applied to input alms. Acts on the unit sphere, i.e. it omits radius^2 scaling unless radius keyword argument is provided.\n\n\n\n\n\n","category":"method"},{"location":"analysis/#Analysing-a-simulation","page":"Analysis","title":"Analysing a simulation","text":"","category":"section"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"While you can analyze a SpeedyWeather simulation through its NetCDF output (i.e., offline analysis), as most users will be used to with other models, you can also reuse a lot of functionality from SpeedyWeather interactively for analysis. This makes SpeedyWeather beyond being a model for the atmospheric general circulation also a library with many functions for the analysis of simulations. Often this also avoids the two-language problem that you will face if you run a simulation with a model in one language but then do the data analysis in another, treating the model as a blackbox although it likely has many of the functions you will need for analysis already defined. With SpeedyWeather we try to avoid this and are working towards a more unified approach in atmospheric modelling where simulation and analysis are done interactively with the same library: SpeedyWeather.jl.","category":"page"},{"location":"analysis/#Advantages-of-online-analysis","page":"Analysis","title":"Advantages of online analysis","text":"","category":"section"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Now you could run a SpeedyWeather simulation, and analyse the NetCDF output but that comes with several issues related to accuracy","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"If you use a reduced grid for the simulation, then the output will (by default) be interpolated on a full grid. This interpolation introduces an error.\nComputing integrals over gridded data on the sphere by weighting every grid point according to its area is not the most accurate numerical integration.\nComputing gradients over gridded data comes with similar issues. While our RingGrids are always equidistant in longitude, they are not necessarily in latitude.","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"The first point you can avoid by running a simulation on one of the full grids that are implemented, see SpectralGrid. But that also impacts the simulation and for various reasons we don't run on full grids by default.","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"The second point you can address by defining a more advanced numerical integration scheme, but that likely requires you to depend on external libraries and then, well, you could also just depend on SpeedyWeather.jl directly, because we have to do these computations internally anyway. Similar for the third point, many gradients have to be computed on every time step and we do that with spectral transforms to reduce the discretization error.","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"The following contains a (hopefully growing) list of examples of how a simulation can be analysed interactively. We call this online analysis because you are directly using the functionality from the model as if it was a library. For simplicity, we use the shallow water model to demonstrate this.","category":"page"},{"location":"analysis/#Mass-conservation","page":"Analysis","title":"Mass conservation","text":"","category":"section"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"In the absence of sources and sinks for the interface displacement eta in the shallow water equations, total mass (or equivalently volume as density is constant) is conserved. The total volume is defined as the integral of the dynamic layer thickness h = eta + H - H_b (H is the layer thickness at rest, H_b is orography) over the surface A of the sphere","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"M = iint h dA = iint left(eta + H - H_bright) dA","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"to check for conservation we want to assess that","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"fracpartial Mpartial t = 0","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"And because V = iint H dA - iint H_b dA, the total volume at rest, is a constant (H is a global constant, the orography H_b does not change with time) we can just check whether iint eta dA changes over time. Instead of computing this integral in grid-point space, we use the spectral eta whereby the coefficient of the first spherical harmonic (the l = m = 0 mode, or wavenumber 0, see Spherical Harmonic Transform) encodes the global average.","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=31, nlayers=1)\nmodel = ShallowWaterModel(spectral_grid)\nsimulation = initialize!(model)","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Now we check eta_00 the l = m = 0 coefficent of the inital conditions of that simulation with","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"simulation.prognostic_variables.pres[1][1]","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"[1][1] pulls the first Leapfrog time step and of that the first element of the underlying LowerTriangularMatrix which is the coefficient of the l = m = 0 harmonic. Its imaginary part is always zero (which is true for any zonal harmonic m=0 as its imaginary part would just unnecessarily rotate something zonally constant in zonal direction), so you can real it. Also for spherical harmonic transforms there is a norm of the sphere by which you have to divide to get your mean value in the original units","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"a = model.spectral_transform.norm_sphere # = 2√π = 3.5449078\nη_mean = real(simulation.prognostic_variables.pres[1][1]) / a","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"So the initial conditions in this simulation are such that the global mean interface displacement is that value in meters. You would need to multiply by the area of the sphere 4pi R^2 (radius R) to get the actual integral from above, but because that doesn't change with time either, we just want to check that η_mean doesn't change with time. Which is equivalent to partial_t iint eta dA = 0 and so volume conservation and because density is constant also mass conservation. Let's check what happens after the simulation ran for some days","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"run!(simulation, period=Day(10))\n\n# now we check η_mean again\nη_mean_later = real(simulation.prognostic_variables.pres[1][1]) / a","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"which is exactly the same. So mass is conserved, woohoo. ","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Insight from a numerical perspective: The tendency of eta is partial_t eta = -nabla cdot (mathbfu h) which is a divergence of a flux. Calculating the divergence in spherical harmonics always sets the l=m=0 mode to zero exactly (the gradient of a periodic variable has zero mean) so the time integration here is always with an exactly zero tendency.","category":"page"},{"location":"analysis/#Energy","page":"Analysis","title":"Energy","text":"","category":"section"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"The total energy in the shallow water equations is the sum of the kinetic energy density frac12(u^2 + v^2) and the potential energy density gz integrated over the total volume (times h=eta+H-H_b for the vertical then integrated over the sphere iint dA).","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"beginalign\nE = iint left int_H_b^etafrac12left(u^2 + v^2 + gzright)dz rightdA \n = iint frac12left(u^2 + v^2 + ghright)h dA\nendalign","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"In contrast to the Mass conservation which, with respect to the spectral transform, is a linear calculation, here we need to multiply variables, which has to be done in grid-point space. Then we can transform to spectral space for the global integral as before. Let us define a total_energy function as","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"using SpeedyWeather\nfunction total_energy(u, v, η, model)\n H = model.atmosphere.layer_thickness\n Hb = model.orography.orography\n g = model.planet.gravity\n \n h = @. η + H - Hb # layer thickness between the bottom and free surface\n E = @. h/2*(u^2 + v^2) + g*h^2 # vertically-integrated mechanical energy\n\n # transform to spectral, take l=m=0 mode at [1] and normalize for mean\n return E_mean = real(transform(E)[1]) / model.spectral_transform.norm_sphere\nend","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"So at the current state of our simulation we have a total energy (per square meter as we haven't multiplied by the surface area of the planet).","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"# flat copies for convenience\nu = simulation.diagnostic_variables.grid.u_grid[:, 1]\nv = simulation.diagnostic_variables.grid.v_grid[:, 1]\nη = simulation.diagnostic_variables.grid.pres_grid\n\nTE = total_energy(u, v, η, model)","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"with units of m^3 s^-2 (multiplying by surface area of the sphere and density of the fluid would turn it into joule = kg m^2 s^-2). To know in general where to find the respective variables u v eta inside our simulation object see Prognostic variables and Diagnostic variables. Now let us continue the simulation","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"run!(simulation, period=Day(10))\n\n# we don't need to reassign u, v, η as they were flat copies\n# pointing directly to the diagnostic variables inside the simulation\n# which got updated during run!\nTE_later = total_energy(u, v, η, model)","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"So the total energy has somewhat changed, it decreased to","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"TE_later/TE","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"of its previous value over 10 days.","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"note: Energy conservation\nWhile technically energy should be conserved in an unforced system, numerically this is rarely exactly the case. We need some Horizontal diffusion for numerical stability and also the time integration is dissipative due to temporal filtering, see Time integration. Note that the energy here is inversely cascading to larger scales, and this makes the dissipation of energy through Horizontal diffusion really inefficient, because in spectral space, standard Laplacian diffusion is proportional to k^2 where k is the wavenumber. By default, we use a 4th power Laplacian, so the diffusion here is proportional to k^8.","category":"page"},{"location":"analysis/#Potential-vorticity","page":"Analysis","title":"Potential vorticity","text":"","category":"section"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Potential vorticity in the shallow water equations is defined as","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"q = fracf + zetah","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"with f the Coriolis parameter, zeta the relative vorticity, and h the layer thickness as before. We can calculate this conveniently directly on the model grid (whichever you chose) as","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"# vorticity\nζ = simulation.diagnostic_variables.grid.vor_grid[:,1]\nf = coriolis(ζ) # create f on that grid\n\n# layer thickness\nη = simulation.diagnostic_variables.grid.pres_grid\nH = model.atmosphere.layer_thickness\nHb = model.orography.orography\nh = @. η + H - Hb\n\n# potential vorticity\nq = @. (f + ζ) / h\nnothing # hide","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"and we can compare the relative vorticity field to","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"using CairoMakie\nheatmap(ζ, title=\"Relative vorticity [1/s]\")\nsave(\"analysis_vor.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"(Image: Relative vorticity)","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"the potential vorticity","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"heatmap(q, title=\"Potential vorticity [1/m/s]\")\nsave(\"analysis_pv.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"(Image: Potential vorticity)","category":"page"},{"location":"analysis/#Absolute-angular-momentum","page":"Analysis","title":"Absolute angular momentum","text":"","category":"section"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Similar to the total mass, in the absence of sources and sinks for momentum, total absolute angular momentum (AAM) defined as","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Lambda = iint left(ur + Omega r^2right)h dA","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"should be conserved (partial_tLambda = 0). Here u is the zonal velocity, Omega the angular velocity of the Earth, r = R cosphi the momentum arm at latitude phi, and R the radius of Earth.","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Following previous examples, let us define a total_angular_momentum function as","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"using SpeedyWeather\n\nfunction total_angular_momentum(u, η, model)\n H = model.atmosphere.layer_thickness\n Hb = model.orography.orography\n R = model.spectral_grid.radius\n Ω = model.planet.rotation\n\n r = R * cos.(model.geometry.lats) # momentum arm for every grid point\n \n h = @. η + H - Hb # layer thickness between the bottom and free surface\n Λ = @. (u*r + Ω*r^2) * h # vertically-integrated AAM\n\n # transform to spectral, take l=m=0 mode at [1] and normalize for mean\n return Λ_mean = real(transform(Λ)[1]) / model.spectral_transform.norm_sphere\nend","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Anytime we stop the simulation, we can calculate Lambda using this function (ignoring the multiplication by 4pi R^2 to get total Lambda).","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"# use u, η from current state of simulation\nΛ_current = total_angular_momentum(u, η, model)","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"So after some days of integration, we would get another Lambda with","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"run!(simulation, period=Day(10))\n\n# u, η got updated during run!\nΛ_later = total_angular_momentum(u, η, model)\nΛ_later / Λ_current","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"and measure its relative change. Similar to energy, in the numerical integration, Λ is not exactly conserved due to Horizontal diffusion.","category":"page"},{"location":"analysis/#Circulation","page":"Analysis","title":"Circulation","text":"","category":"section"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Total circulation is defined as the area-integrated absolute vorticity:","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"C = iint left(zeta + fright) dA","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Following previous fashion, we define a function total_circulation for this","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"function total_circulation(ζ, model)\n f = coriolis(ζ) # create f on the grid of ζ\n C = ζ .+ f # absolute vorticity\n # transform to spectral, take l=m=0 mode at [1] and normalize for mean\n return C_mean = real(transform(C)[1]) / model.spectral_transform.norm_sphere\nend\n\ntotal_circulation(ζ, model)","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"note: Global-integrated circulation\nNote that the area integral of relative vorticity zeta and planetary vorticity f over the whole surface of a sphere are analytically exactly zero. Numerically, C_mean should be a small number but may not be exactly zero due to numerical precision and errors in the spectral transform.","category":"page"},{"location":"analysis/#Potential-enstrophy","page":"Analysis","title":"Potential enstrophy","text":"","category":"section"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"The total potential enstrophy is defined as the second-moment of potential vorticity q","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Q = iint frac12q^2 dA","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"In the absence of source and sink for potential vorticiy, this quantity should also conserve during the integration.","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"We define a function total_enstrophy for this","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"function total_enstrophy(ζ, η, model)\n # constants from model\n H = model.atmosphere.layer_thickness\n Hb = model.orography.orography\n f = coriolis(ζ) # create f on the grid\n \n h = @. η + H - Hb # thickness\n q = @. (ζ + f) / h # Potential vorticity\n Q = @. q^2 / 2 # Potential enstrophy\n\n # transform to spectral, take l=m=0 mode at [1] and normalize for mean\n return Q_mean = real(transform(Q)[1]) / model.spectral_transform.norm_sphere\nend","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Then by evaluating Q_mean at different time steps, one can similarly check how Q is changing over time.","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Q = total_enstrophy(ζ, η, model)\nrun!(simulation, period=Day(10))\nQ_later = total_enstrophy(ζ, η, model)\nQ_later/Q","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"note: Less conservative enstrophy\nNote that the turbulent nature of the shallow water model (or generally 2D turbulence) cascades enstrophy to smaller scales where it is removed by Horizontal diffusion for numerical stability. As a result, it is decreasing more quickly than energy.","category":"page"},{"location":"analysis/#Online-diagnostics","page":"Analysis","title":"Online diagnostics","text":"","category":"section"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Now we want to calculate all the above global diagnostics periodically during a simulation. For that we will use Callbacks, which let us inject code into a simulation that is executed after every time step (or at any other scheduled time, see Schedules).","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"So we define a function global_diagnostics to calculate the integrals together. We could reuse the functions like total_enstrophy from above but we also want to show how to global integral iint dV can be written more efficiently","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"# define a global integral, reusing a precomputed SpectralTransform S\n# times surface area of sphere omitted\nfunction ∬dA(v, h, S::SpectralTransform)\n return real(transform(v .* h, S)[1]) / S.norm_sphere\nend\n\n# use SpectralTransform from model\n∬dA(v, h, model::AbstractModel) = ∬dA(v, h, model.spectral_transform)","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"By reusing model.spectral_transform we do not have to re-precompute the spectral tranform on every call to transform. Providing the spectral transform from model as the 2nd argument simply reuses a previously precomputed spectral transform which is much faster and uses less memory.","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Now the global_diagnostics function is defined as","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"function global_diagnostics(u, v, ζ, η, model)\n \n # constants from model\n NF = model.spectral_grid.NF # number format used\n H = model.atmosphere.layer_thickness\n Hb = model.orography.orography\n R = model.spectral_grid.radius\n Ω = model.planet.rotation\n g = model.planet.gravity\n\n r = NF.(R * cos.(model.geometry.lats)) # create r on that grid\n f = coriolis(u) # create f on that grid\n \n h = @. η + H - Hb # thickness\n q = @. (ζ + f) / h # potential vorticity\n λ = @. u * r + Ω * r^2 # angular momentum (in the right number format NF)\n k = @. (u^2 + v^2) / 2 # kinetic energy\n p = @. g * h / 2 # potential energy\n z = @. q^2 / 2 # potential enstrophy\n\n M = ∬dA(1, h, model) # mean mass\n C = ∬dA(q, h, model) # mean circulation\n Λ = ∬dA(λ, h, model) # mean angular momentum\n K = ∬dA(k, h, model) # mean kinetic energy\n P = ∬dA(p, h, model) # mean potential energy\n Q = ∬dA(z, h, model) # mean potential enstrophy\n\n return M, C, Λ, K, P, Q\nend\n\n# unpack diagnostic variables and call global_diagnostics from above\nfunction global_diagnostics(diagn::DiagnosticVariables, model::AbstractModel)\n u = diagn.grid.u_grid[:, 1]\n v = diagn.grid.v_grid[:, 1]\n ζR = diagn.grid.vor_grid[:, 1]\n η = diagn.grid.pres_grid\n \n # vorticity during simulation is scaled by radius R, unscale here\n ζ = ζR ./ diagn.scale[]\n\n return global_diagnostics(u, v, ζ, η, model)\nend","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"note: Radius scaling of vorticity and divergence\nThe prognostic variables vorticity and divergence are scaled with the radius of the sphere during a simulation, see Radius scaling. This did not apply above because we only analyzed vorticity before or after the simulation, i.e. outside of the run!(simulation) call. The radius scaling is only applied just before the time integration and is undone directly after it. However, because now we are accessing the vorticity during the simulation we need to unscale the vorticity (and divergence) manually. General recommendation is to divide by diagn.scale[] (and not radius) as diagn.scale[] always reflects whether a vorticity and divergence are currently scaled (scale = radius) or not (scale = 1).","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Then we define a new callback GlobalDiagnostics subtype of SpeedyWeather's AbstractCallback and define new methods of initialize!, callback! and finalize! for it (see Callbacks for more details)","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"# define a GlobalDiagnostics callback and the fields it needs\nBase.@kwdef mutable struct GlobalDiagnostics <: SpeedyWeather.AbstractCallback\n timestep_counter::Int = 0\n \n time::Vector{DateTime} = []\n M::Vector{Float64} = [] # mean mass per time step\n C::Vector{Float64} = [] # mean circulation per time step\n Λ::Vector{Float64} = [] # mean angular momentum per time step\n K::Vector{Float64} = [] # mean kinetic energy per time step\n P::Vector{Float64} = [] # mean potential energy per time step\n Q::Vector{Float64} = [] # mean enstrophy per time step\nend\n\n# define how to initialize a GlobalDiagnostics callback\nfunction SpeedyWeather.initialize!(\n callback::GlobalDiagnostics,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel,\n)\n # replace with vector of correct length\n n = progn.clock.n_timesteps + 1 # +1 for initial conditions\n callback.time = zeros(DateTime, n)\n callback.M = zeros(n)\n callback.C = zeros(n)\n callback.Λ = zeros(n)\n callback.K = zeros(n)\n callback.P = zeros(n)\n callback.Q = zeros(n)\n \n M, C, Λ, K, P, Q = global_diagnostics(diagn, model)\n \n callback.time[1] = progn.clock.time\n callback.M[1] = M # set initial conditions\n callback.C[1] = C # set initial conditions\n callback.Λ[1] = Λ # set initial conditions\n callback.K[1] = K # set initial conditions\n callback.P[1] = P # set initial conditions\n callback.Q[1] = Q # set initial conditions\n \n callback.timestep_counter = 1 # (re)set counter to 1\n \n return nothing\nend\n\n# define what a GlobalDiagnostics callback does on every time step\nfunction SpeedyWeather.callback!(\n callback::GlobalDiagnostics,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel,\n)\n callback.timestep_counter += 1 \n i = callback.timestep_counter\n \n M, C, Λ, K, P, Q = global_diagnostics(diagn, model)\n \n # store current time and diagnostics for timestep i\n callback.time[i] = progn.clock.time\n callback.M[i] = M \n callback.C[i] = C \n callback.Λ[i] = Λ \n callback.K[i] = K \n callback.P[i] = P \n callback.Q[i] = Q \nend\n\nusing NCDatasets\n\n# define how to finalize a GlobalDiagnostics callback after simulation finished\nfunction SpeedyWeather.finalize!(\n callback::GlobalDiagnostics,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel,\n)\n n_timesteps = callback.timestep_counter\n\n # create a netCDF file in current path\n ds = NCDataset(joinpath(pwd(), \"global_diagnostics.nc\"), \"c\")\n \n # save diagnostics variables within\n defDim(ds, \"time\", n_timesteps)\n defVar(ds, \"time\", callback.time, (\"time\",))\n defVar(ds, \"mass\", callback.M, (\"time\",))\n defVar(ds, \"circulation\", callback.C, (\"time\",))\n defVar(ds, \"angular momentum\", callback.Λ, (\"time\",))\n defVar(ds, \"kinetic energy\", callback.K, (\"time\",))\n defVar(ds, \"potential energy\", callback.P, (\"time\",))\n defVar(ds, \"potential enstrophy\", callback.Q, (\"time\",))\n \n close(ds)\n\n return nothing\nend","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Note that callback! will execute every time step. If execution is only desired periodically, you can use Schedules. At finalize! we decide to write the timeseries of our global diagnostics as netCDF file via NCDatasets.jl to the current path pwd(). We need to add using NCDatasets here, as SpeedyWeather does not re-export the functionality therein.","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Now we create a GlobalDiagnostics callback, add it to the model with key :global_diagnostics (you get a random key if not provided) and reinitialize the simulation to start from the initial conditions.","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"# don't name it global_diagnostics because that's a function already!\ndiagnostics_recorder = GlobalDiagnostics()\nadd!(model.callbacks, :diagnostics_recorder => diagnostics_recorder)\nsimulation = initialize!(model)\n\nrun!(simulation, period=Day(20))\nnothing # hide","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"Then one could check the output file global_diagnostics.nc, or directly use the callback through its key :diagnostics_recorder as we do here","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"using CairoMakie\n\n# unpack callback\n(; M, C, Λ, K, P, Q) = model.callbacks[:diagnostics_recorder]\nt = model.callbacks[:diagnostics_recorder].time\ndays = [Second(ti - t[1]).value/3600/24 for ti in t]\n\nfig = Figure();\naxs = [Axis(fig[row, col]) for row in 1:3, col in 1:2]\n\nlines!(axs[1,1], days, M)\naxs[1,1].title = \"Mass\"\nhidexdecorations!(axs[1,1])\n\nlines!(axs[2,1], days, Λ)\naxs[2,1].title = \"Angular momentum\"\nhidexdecorations!(axs[2,1])\n\nlines!(axs[3,1], days, P)\naxs[3,1].title = \"Potential energy\"\naxs[3,1].xlabel = \"time [day]\"\n\nlines!(axs[1,2], days, C)\naxs[1,2].title = \"Circulation\"\nhidexdecorations!(axs[1,2])\n\nlines!(axs[2,2], days, K)\naxs[2,2].title = \"Kinetic energy\"\nhidexdecorations!(axs[2,2])\n\nlines!(axs[3,2], days, Q)\naxs[3,2].title = \"Potential enstrophy\"\naxs[3,2].xlabel = \"time [day]\"\nfig\nsave(\"global_diagnostics.png\", fig) # hide\nnothing # hide","category":"page"},{"location":"analysis/","page":"Analysis","title":"Analysis","text":"(Image: Global diagnostics)","category":"page"},{"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/#Interactively-exploring-the-grids","page":"Grids","title":"Interactively exploring the grids","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"Based on GeoMakie.jl SpeedyWeather.jl has an extension (meaning only loaded when also using GeoMakie) that defines the globe function which visualises the implemented grids at the desired resolution. With the CairoMakie backend these visualisations are static, but using GLMakie they are interactive. For example","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"using SpeedyWeather\nusing GLMakie, GeoMakie\n\n# grid type, resolution parameter nlat_half\nglobe(FullGaussianGrid, 24)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"This will open a window with interactive zoom and rotation, visualising a full Gaussian grid at a resolution of nlat_half = 24 (i.e. 96x48 grid points). You can visualise all grids at a wide range of resolutions for non-interactive plotting use interactive=false, which is also what one should do when using CairoMakie. Additional keyword arguments are coastlines, background among others, check ?globe. You also can visualise data on a grid directly this way which will draw polygons for the cell faces, e.g.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"grid = rand(FullGaussianGrid, 24)\nglobe(grid)","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":"using CairoMakie, GeoMakie # when using GLMakie, use interactive=true (default) for zoom and rotation\nglobe(FullGaussianGrid, 24, interactive=false)\nsave(\"full_gaussian_grid.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"(Image: FullGaussianGrid)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"The full Gaussian grid is a grid that uses regularly spaced longitudes with 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 = 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","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 0 1 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":"using CairoMakie, GeoMakie # when using GLMakie, use interactive=true (default) for zoom and rotation\nglobe(OctahedralGaussianGrid, 24, interactive=false)\nsave(\"octahedral_gaussian_grid.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"(Image: 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 = 1 16+4j on the j-th latitude ring (starting with 1 around the north pole), j=1 tfracN_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/#OctaminimalGaussianGrid","page":"Grids","title":"Octaminimal Gaussian grid","text":"","category":"section"},{"location":"grids/","page":"Grids","title":"Grids","text":"(called OctaminimalGaussianGrid)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"using CairoMakie, GeoMakie # when using GLMakie, use interactive=true (default) for zoom and rotation\nglobe(OctaminimalGaussianGrid, 24, interactive=false)\nsave(\"octaminimal_gaussian_grid.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"(Image: OctaminimalGaussianGrid)","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"The OctaminimalGaussianGrid is similar to the OctahedralGaussianGrid but starts with 4 points around the poles. It therefore minimizes the number of grid points for the Gaussian grids at the cost of a (somewhat) inexact spectral transform. But for nlat_half = 24 (i.e. 48 latitude rings) this grid reduces the number of horizontal grid points from 3168 to 2400, i.e. -24% which speeds up the computation on the grid (dynamics and physics) as well as the spectral transform. The octaminimal Gaussian grid is intended to be used for low resolutions as for higher resolutions the relative increase in the number of grid points with the octahedral Gaussian grid becomes negligible. ","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"Furthermore the longitudes are rotated: Instead of no offset where the first point on every ring starts at 0˚E, an offset of 3602n degrees is chosen similar to how the longitudinal points in the HEALPix grids (HEALPixGrid and OctaHEALPixGrid) are chosen. This allows for a more even distribution of grid points near the poles.","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":"using CairoMakie, GeoMakie # when using GLMakie, use interactive=true (default) for zoom and rotation\nglobe(FullClenshawGrid, 24, interactive=false)\nsave(\"full_clenshaw_grid.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"(Image: 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 = 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.","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":"using CairoMakie, GeoMakie # when using GLMakie, use interactive=true (default) for zoom and rotation\nglobe(OctahedralClenshawGrid, 24, interactive=false)\nsave(\"octahedral_clenshaw_grid.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"(Image: 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":"using CairoMakie, GeoMakie # when using GLMakie, use interactive=true (default) for zoom and rotation\nglobe(HEALPixGrid, 24, interactive=false)\nsave(\"healpix_grid.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"(Image: 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=1 N_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=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.","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"In the north equatorial belt j=N_side 2N_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 = 1 4N_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(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)","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":"using CairoMakie, GeoMakie # when using GLMakie, use interactive=true (default) for zoom and rotation\nglobe(OctaHEALPixGrid, 24, interactive=false)\nsave(\"octahealpix_grid.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"grids/","page":"Grids","title":"Grids","text":"(Image: 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=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","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 = (u v), 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 u v T q 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 q T 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_0 0frac1sqrt4pi 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(x y z t) 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(x y eta t) = Psi(x y z(x y eta t) 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 x y t 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 = 0 1 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 u v 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 tfrac12 1 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_l m 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_l m, but the surface k=N only on the temperature mode at the surface. mathbfU takes the surface value of the l m mode of the logarithm of surface pressure (ln p_s)_l m 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 l m 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 mathbfR W L 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 mathbfR U L W 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 U V\nUnscale the cos(theta) factor to obtain u v","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 u v D vertically to obtain baru barv barD 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 u v T q and add it to the respective tendency.\nFor 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 mathcalD_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 zeta mathcalD T q\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 u v zeta mathcalD T q ln 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":"orography/#Orography","page":"Orography","title":"Orography","text":"","category":"section"},{"location":"orography/","page":"Orography","title":"Orography","text":"Orography (in height above the surface) forms the surface boundary of the lowermost layer in SpeedyWeather. ","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"In the shallow-water equations the orography H_b enters the equations when computing the layer thickness h = eta + H_0 - H_b for the volume fluxes mathbfuh in the continuity equation. Here, the orography is used in meters above the surface which shortens h over mountains. The orography here is needed in grid-point space.","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"In the primitive equations the orography enters the equations when computing the Geopotential. So actually required here is the surface geopotential Phi_s = gz_s where z_s is the orography height in meters as used in the shallow-water equations too z_s = H_b. However, the primitive equations require the orography in spectral space as the geopotential calculation is a linear operation in the horizontal and can therefore be applied in either grid-point or spectral space. The latter is more convenient as SpeedyWeather solves the equations to avoid additional transforms.","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"In the current formulation of the barotropic vorticity equations there is no orography. In fact, the field model.orography is not defined for model::BarotropicModel.","category":"page"},{"location":"orography/#Orographies-implemented","page":"Orography","title":"Orographies implemented","text":"","category":"section"},{"location":"orography/","page":"Orography","title":"Orography","text":"Currently implemented are","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"using InteractiveUtils # hide\nusing SpeedyWeather\nsubtypes(SpeedyWeather.AbstractOrography)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"which are ","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"Phi_s = z_s = H_b = 0 for NoOrography\nFor ZonalRidge the zonal ridge from the Jablonowski and Williamson initial conditions, see Jablonowski-Williamson baroclinic wave\nFor EarthOrography a high-resolution orography is loaded and interpolated to the resolution as defined by spectral_grid.","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"all orographies need to be created with spectral_grid::SpectralGrid as the first argument, so that the respective fields for geopot_surf, i.e. Phi_s and orography, i.e. H_b can be allocated in the right size and number format.","category":"page"},{"location":"orography/#Earth's-orography","page":"Orography","title":"Earth's orography","text":"","category":"section"},{"location":"orography/","page":"Orography","title":"Orography","text":"Earth's orography can be created with (here we use a resolution of T85, about 165km globally)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=85)\norography = EarthOrography(spectral_grid)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"but note that only allocates the orography, it does not actually load and interpolate the orography which happens at the initialize! step. Visualised with","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"model = PrimitiveDryModel(spectral_grid; orography)\ninitialize!(orography, model) # happens also in simulation = initialize!(model)\n\nusing CairoMakie\nheatmap(orography.orography, title=\"Earth's orography at T85 resolution, no smoothing\")\nsave(\"earth_orography.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"(Image: EarthOrography)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"typing ?EarthOrography shows the various options that are provided. An orogaphy at T85 resolution that is as smooth as it would be at T42 (controlled by the smoothing_fraction, the fraction of highest wavenumbers which are the top half here, about T43 to T85) for example can be created with","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"orography = EarthOrography(spectral_grid, smoothing=true, smoothing_fraction=0.5)\ninitialize!(orography, model)\n\nheatmap(orography.orography, title=\"Earth's orography at T85 resolution, smoothed to T42\")\nsave(\"earth_orography_smooth.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"(Image: EarthOrography_smooth)","category":"page"},{"location":"orography/#Load-orography-from-file","page":"Orography","title":"Load orography from file","text":"","category":"section"},{"location":"orography/","page":"Orography","title":"Orography","text":"The easiest to load another orography from a netCDF file is to reuse the EarthOrography, e.g.","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"mars_orography = EarthOrography(spectal_grid, \n path=\"path/to/my/orography\",\n file=\"mars_orography.nc\",\n file_Grid=FullClenshawGrid)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"the orography itself need to come on one of the full grids SpeedyWeather defines, i.e. FullGaussianGrid or FullClenshawGrid (a regular lat-lon grid, see FullClenshawGrid), which you can specify. Best to inspect the correct orientation with plot(mars_orography.orography) (or heatmap after using CairoMakie; the scope mars_orography. is whatever name you chose here). You can use smoothing as above.","category":"page"},{"location":"orography/#Changing-orography-manually","page":"Orography","title":"Changing orography manually","text":"","category":"section"},{"location":"orography/","page":"Orography","title":"Orography","text":"You can also change orography manually, that means by mutating the elements in either orography.orography (to set it for the shallow-water model) or orography.geopot_surf (for the primitive equations, but this is in spectral space, advanced!). This should be done after the orography has been initialised which will overwrite these arrays (again). You can just initialize orography with initialize!(orography, model) but that also automatically happens in simulation = initialize!(model). Orography is just stored as an array, so you can do things like sort!(orography.orography) (sorting all mountains towards the south pole). But for most practical purposes, the set! function is more convenient, for example you can do","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"set!(model, orography=(λ,φ) -> 2000*cosd(φ) + 300*sind(λ) + 100*randn())\n\nusing CairoMakie\nheatmap(model.orography.orography, title=\"Zonal 2000m ridge [m] with noise\")\nsave(\"orography_set.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"(Image: Orography with set!)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"passing on a function with arguments longitude (0 to 360˚E in that unit, so use cosd, sind etc.) and latitude (-90 to 90˚N). But note that while model.orography is still of type EarthOrography we have now muted the arrays within - so do not be confused that it is not the Earth's orography anymore.","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"The set! function automatically propagates the grid array in orography.orography to spectral space in orography.geopot_surf to synchronize those two arrays that are supposed to hold essentially the same information just one in grid the other in spectral space. set! also allows for the add keyword, making it possible to add (or remove) mountains, e.g. imagine Hawaii would suddenly increase massively in size, covering a 5˚x5˚ area with a 4000m \"peak\" (given that horizontal extent it is probably more a mountain range...)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"model.orography = EarthOrography(spectral_grid) # reset orography\ninitialize!(model.orography, model) # initially that reset orography\n\n# blow up Hawaii by adding a 4000m peak on a 10˚x10˚ large island\nH, λ₀, φ₀, σ = 4000, 200, 20, 5 # height, lon, lat position, and width\nset!(model, orography=(λ,φ) -> H*exp((-(λ-λ₀)^2 - (φ-φ₀)^2)/2σ^2), add=true)\nheatmap(model.orography.orography, title=\"Super Hawaii orography [m]\")\nsave(\"orography_hawaii.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"(Image: Orography with super Hawaii)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"If you don't use set!, you want to reflect any changes to orography.orography in the surface geopotential orography.geopot_surf (which is used in the primitive equations) manually by","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"transform!(orography.geopot_surf, orography.orography, model.spectral_transform)\norography.geopot_surf .*= model.planet.gravity\nspectral_truncation!(orography.geopot_surf)\nnothing # hide","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"In the first line, the surface geopotential is still missing the gravity, which is multiplied in the second line. The spectral_truncation! removes the l_max+1 degree of the spherical harmonics as illustrated in the spectral representation or the surface geopotential here. This is because scalar fields do not use that last degree, see One more degree for spectral fields.","category":"page"},{"location":"orography/#Spherical-distance","page":"Orography","title":"Spherical distance","text":"","category":"section"},{"location":"orography/","page":"Orography","title":"Orography","text":"In the example above we have defined the \"Super Hawaii orography\" as","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"orography=(λ,φ) -> H*exp((-(λ-λ₀)^2 - (φ-φ₀)^2)/2σ^2)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"note however, that this approximates the distance on the sphere with Cartesian coordinates which is here not too bad as we are not too far north (where longitudinal distances would become considerably shorter) and also as we are far away from the prime meridian. If lambda_0 = 0 in the example above then calculating lambda - lambda_0 for lambda = 359E yields a really far distance even though lambda is actually relatively close to the prime meridian. To avoid this problem, SpeedyWeather (actually RingGrids) defines a function called spherical_distance (inputs in degrees, output in meters) to actually calculate the great-circle distance or spherical distance. Compare","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"λ₀ = 0 # move \"Super Hawaii\" mountain onto the prime meridian\nset!(model, orography=(λ,φ) -> H*exp((-(λ-λ₀)^2 - (φ-φ₀)^2)/2σ^2))\nheatmap(model.orography.orography, title=\"Mountain [m] on prime meridian, cartesian coordinates\")\nsave(\"mountain_cartesian.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"(Image: Mountain in cartesian coordinates)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"which clearly shows that the mountain is only on the Eastern hemisphere – probably not what you wanted. Note also that because we did not provide add=true the orography we set through set! overwrites the previous orography (add=false is the default). Rewrite this as","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"λ₀ = 0 # move \"Super Hawaii\" mountain onto the prime meridian\nset!(model, orography=(λ,φ) -> H*exp(-spherical_distance((λ,φ), (λ₀,φ₀), radius=360/2π)^2/2σ^2))\nheatmap(model.orography.orography, title=\"Mountain [m] on prime meridian, spherical distance\")\nsave(\"mountain_spherical.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"(Image: Mountain in spherical coordinates)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"And the mountain also shows up on the western hemisphere! Note that we could have defined the width sigma of the mountain in meters, or we keep using degrees as before but then use radius = 360/2π to convert radians into degrees. If you set radius=1 then radians are returned and so we could have defined sigma in terms of radians too.","category":"page"},{"location":"orography/#Defining-a-new-orography-type","page":"Orography","title":"Defining a new orography type","text":"","category":"section"},{"location":"orography/","page":"Orography","title":"Orography","text":"You can also define a new orography like we defined ZonalRidge or EarthOrography. The following explains what's necessary for this. The new MyOrography has to be defined as (mutable or not, but always with @kwdef)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"@kwdef struct MyOrography{NF, Grid<:RingGrids.AbstractGrid{NF}} <: SpeedyWeather.AbstractOrography\n # optional, any parameters as fields here, e.g.\n constant_height::Float64 = 100\n # add some other parameters with default values\n\n # mandatory, every <:AbstractOrography needs those (same name, same type)\n orography::Grid # in grid-point space [m]\n geopot_surf::LowerTriangularMatrix{Complex{NF}} # in spectral space *gravity [m^2/s^2]\nend","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"for convenience with a generator function is automatically defined for all AbstractOrography","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"my_orography = MyOrography(spectral_grid, constant_height=200)","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"Now we have to extend the initialize! function. The first argument has to be ::MyOrography i.e. the new type we just defined, the second argument has to be ::AbstractModel although you could constrain it to ::ShallowWater for example but then it cannot be used for primitive equations.","category":"page"},{"location":"orography/","page":"Orography","title":"Orography","text":"function SpeedyWeather.initialize!(\n orog::MyOrography, # first argument as to be ::MyOrography, i.e. your new type\n model::AbstractModel, # second argument, use anything from model read-only\n)\n (; orography, geopot_surf) = orog # unpack\n\n # maybe use lat, lon coordinates (in degree or radians)\n (; latds, londs, lats, lons) = model.geometry\n\n # change here the orography grid [m], e.g.\n orography .= orography.constant_height\n\n # then also calculate the surface geopotential for primitive equations\n # given orography we just set\n transform!(geopot_surf, orography, model.spectral_transform)\n geopot_surf .*= model.planet.gravity\n spectral_truncation!(geopot_surf)\n return nothing\nend","category":"page"},{"location":"lowertriangularmatrices/#lowertriangularmatrices","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"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":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"This module defines LowerTriangularArray, a lower triangular matrix format, which in contrast to LinearAlgebra.LowerTriangular does not store the entries above the diagonal. SpeedyWeather.jl uses LowerTriangularArray which is defined as a subtype of AbstractArray to store the spherical harmonic coefficients (see Spectral packing). For 2D LowerTriangularArray the alias LowerTriangularMatrix exists. Higher dimensional LowerTriangularArray are 'batches' of 2D LowerTriangularMatrix. So, for example a (10times 10times 10) LowerTriangularArray holds 10 LowerTriangularMatrix of size (10times 10) in one array. ","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"warn: LowerTriangularMatrix is actually a vector\nLowerTriangularMatrix and LowerTriangularArray can in many ways be used very much like a Matrix or Array, however, because they unravel the lower triangle into a vector their dimensionality is one less than their Array counterparts. A LowerTriangularMatrix should therefore be treated as a vector rather than a matrix with some (limited) added functionality to allow for matrix-indexing (vector or flat-indexing is the default though). More details below.","category":"page"},{"location":"lowertriangularmatrices/#Creation-of-LowerTriangularArray","page":"LowerTriangularMatrices","title":"Creation of LowerTriangularArray","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"A LowerTriangularMatrix and LowerTriangularArray can be created using zeros, ones, rand, or randn","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"using SpeedyWeather.LowerTriangularMatrices\n\nL = rand(LowerTriangularMatrix{Float32}, 5, 5)\nL2 = rand(LowerTriangularArray{Float32}, 5, 5, 5)","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"or the undef initializer LowerTriangularMatrix{Float32}(undef, 3, 3). The element type is arbitrary though, you can use any type T too.","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"Note how for a matrix both the upper triangle and the lower triangle are shown in the terminal. The zeros are evident. However, for higher dimensional LowerTriangularArray we fall back to show the unravelled first two dimensions. Hence, here, the first column is the first matrix with 15 elements forming a 5x5 matrix, but the zeros are not shown.","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"Alternatively, it can be created through conversion from Array, which drops the upper triangle entries and sets them to zero (which are not stored however)","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"M = rand(Float16, 3, 3)\nL = LowerTriangularMatrix(M)\n\nM2 = rand(Float16, 3, 3, 2)\nL2 = LowerTriangularArray(M2)","category":"page"},{"location":"lowertriangularmatrices/#Size-of-LowerTriangularArray","page":"LowerTriangularMatrices","title":"Size of LowerTriangularArray","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"There are three different ways to describe the size of a LowerTriangularArray. For example with L","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"L = rand(LowerTriangularMatrix, 5, 5)","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"we have (additional dimensions follow naturally thereafter)","category":"page"},{"location":"lowertriangularmatrices/#1-based-vector-indexing-(default)","page":"LowerTriangularMatrices","title":"1-based vector indexing (default)","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"size(L) # equivalently size(L, OneBased, as=Vector)","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"The lower triangle is unravelled hence the number of elements in the lower triangle is returned.","category":"page"},{"location":"lowertriangularmatrices/#1-based-matrix-indexing","page":"LowerTriangularMatrices","title":"1-based matrix indexing","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"size(L, as=Matrix) # equivalently size(L, OneBased, as=Matrix)","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"If you think of a LowerTriangularMatrix as a matrix this is the most intuitive size of L, which, however, does not agree with the size of the underlying data array (hence it is not the default).","category":"page"},{"location":"lowertriangularmatrices/#0-based-matrix-indexing","page":"LowerTriangularMatrices","title":"0-based matrix indexing","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"Because LowerTriangularArrays are used to represent the coefficients of spherical harmonics which are commonly indexed based on zero (i.e. starting with the zero mode representing the mean), we also add ZeroBased to get the corresponding size.","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"size(L, ZeroBased, as=Matrix)","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"which is convenient if you want to know the maximum degree and order of the spherical harmonics in L. 0-based vector indexing is not implemented.","category":"page"},{"location":"lowertriangularmatrices/#Indexing-LowerTriangularArray","page":"LowerTriangularMatrices","title":"Indexing LowerTriangularArray","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"We illustrate the two types of indexing LowerTriangularArray supports.","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"Matrix indexing, by denoting two indices, column and row [l, m, ..]\nVector/flat indexing, by denoting a single index [lm, ..].","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"The matrix index works as expected","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"L\n\nL[2, 2]","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"But the single index skips the zero entries in the upper triangle, i.e. a 2, 2 index points to the same element as the index 6","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"L[6]","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"which, important, is different from single indices of an AbstractMatrix","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"Matrix(L)[6]","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"which would point to the first element in the upper triangle (hence zero).","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"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 matrix index is somewhat slower as it first has to be converted to the corresponding single index. ","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"Consequently, many loops in SpeedyWeather.jl are build with the following structure","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"n, m = size(L, as=Matrix)\n\nij = 0\nfor j in 1:m, i in j:n\n ij += 1\n L[ij] = i+j\nend","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"which loops over all lower triangle entries of L::LowerTriangularArray 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).","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"warn: `end` doesn't work for matrix indexing\nIndexing LowerTriangularMatrix and LowerTriangularArray in matrix style ([i, j]) with end doesn't work. It either returns an error or wrong results as the end is lowered by Julia to the size of the underlying flat array dimension.","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"The setindex! functionality of matrixes will throw a BoundsError when trying to write into the upper triangle of a LowerTriangularArray, for example","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"L[2, 1] = 0 # valid index\n\nL[1, 2] = 0 # invalid index in the upper triangle","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"But reading from it will just return a zero","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"L[2, 3] # in the upper triangle","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"Higher dimensional LowerTriangularArray can be indexed with multidimensional array indices like most other arrays types. Both the vector index and the matrix index for the lower triangle work as shown here","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"L = rand(LowerTriangularArray{Float32}, 3, 3, 5)\n\nL[2, 1] # second lower triangle element of the first lower triangle matrix \n\nL[2, 1, 1] # (2,1) element of the first lower triangle matrix ","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"The setindex! functionality follows accordingly. ","category":"page"},{"location":"lowertriangularmatrices/#Iterators","page":"LowerTriangularMatrices","title":"Iterators","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"An iterator over all entries in the array can be created with eachindex","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"L = rand(LowerTriangularArray, 5, 5, 5)\nfor ij in eachindex(L)\n # do something\nend\n\neachindex(L)","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"In order to only loop over the harmonics (essentially the horizontal, ignoring other dimensions) use eachharmonic","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"eachharmonic(L)","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"If you only want to loop over the other dimensions use eachmatrix","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"eachmatrix(L)","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"together they can be used as","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"for k in eachmatrix(L)\n for lm in eachharmonic(L)\n L[lm, k]\n end\nend","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"Note that k is a CartesianIndex that will loop over all other dimensions, whether there's only 1 (representing a 3D variable) or 5 (representing a 6D variable with the first two dimensions being a lower triangular matrix).","category":"page"},{"location":"lowertriangularmatrices/#Linear-algebra-with-LowerTriangularArray","page":"LowerTriangularMatrices","title":"Linear algebra with LowerTriangularArray","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"The LowerTriangularMatrices module's main purpose is not linear algebra, and typical matrix operations will not work with LowerTriangularMatrix because it's treated as a vector not as a matrix, meaning that the following will not work as expected","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"L = rand(LowerTriangularMatrix{Float32}, 3, 3)\nL * L\ninv(L)","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"And many other operations that require L to be a AbstractMatrix which it isn't. In contrast, typical vector operations like a scalar product between two \"LowerTriangularMatrix\" vectors does work","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"L' * L","category":"page"},{"location":"lowertriangularmatrices/#Broadcasting-with-LowerTriangularArray","page":"LowerTriangularMatrices","title":"Broadcasting with LowerTriangularArray","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"In contrast to linear algebra, many element-wise operations work as expected thanks to broadcasting, so operations that can be written in . notation whether implicit +, 2*, ... or explicitly written .+, .^, ... or via the @. macro","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"L + L\n2L\n\n@. L + 2L - 1.1*L / L^2","category":"page"},{"location":"lowertriangularmatrices/#GPU","page":"LowerTriangularMatrices","title":"GPU","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"LowerTriangularArray{T, N, ArrayType} wraps around an array of type ArrayType. If this array is a GPU array (e.g. CuArray), all operations are performed on GPU as well (work in progress). The implementation was written so that scalar indexing is avoided in almost all cases, so that GPU operation should be performant. To use LowerTriangularArray on GPU you can e.g. just adapt an existing LowerTriangularArray.","category":"page"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"using Adapt\nL = rand(LowerTriangularArray{Float32}, 5, 5, 5)\nL_gpu = adapt(CuArray, L)","category":"page"},{"location":"lowertriangularmatrices/#Function-and-type-index","page":"LowerTriangularMatrices","title":"Function and type index","text":"","category":"section"},{"location":"lowertriangularmatrices/","page":"LowerTriangularMatrices","title":"LowerTriangularMatrices","text":"Modules = [SpeedyWeather.LowerTriangularMatrices]","category":"page"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.LowerTriangularArray","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.LowerTriangularArray","text":"A lower triangular array implementation that only stores the non-zero entries explicitly. L<:AbstractArray{T,N-1} although we do allow both \"flat\" N-1-dimensional indexing and additional N-dimensional or \"matrix-style\" indexing.\n\nSupports n-dimensional lower triangular arrays, so that for all trailing dimensions L[:, :, ..] is a matrix in lower triangular form, e.g. a (5x5x3)-LowerTriangularArray would hold 3 lower triangular matrices.\n\n\n\n\n\n","category":"type"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.LowerTriangularArray-Union{Tuple{ArrayType}, Tuple{N}, Tuple{T}} where {T, N, ArrayType<:AbstractArray{T, N}}","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.LowerTriangularArray","text":"LowerTriangularArray(\n M::AbstractArray{T, N}\n) -> LowerTriangularArray\n\n\nCreate a LowerTriangularArray L from Array M by copying over the non-zero elements in M.\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.LowerTriangularMatrix","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.LowerTriangularMatrix","text":"2-dimensional LowerTriangularArray of type Twith its non-zero entries unravelled into aVector{T}`\n\n\n\n\n\n","category":"type"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.LowerTriangularMatrix-Union{Tuple{Matrix{T}}, Tuple{T}} where T","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.LowerTriangularMatrix","text":"LowerTriangularMatrix(M::Array{T, 2}) -> Any\n\n\nCreate a LowerTriangularArray L from Matrix M by copying over the non-zero elements in M.\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.OneBased","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.OneBased","text":"Abstract type to dispatch for 1-based indexing of the spherical harmonic degree l and order m, i.e. l=m=1 is the mean, the zonal modes are m=1 etc. This indexing matches Julia's 1-based indexing for arrays.\n\n\n\n\n\n","category":"type"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.ZeroBased","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.ZeroBased","text":"Abstract type to dispatch for 0-based indexing of the spherical harmonic degree l and order m, i.e. l=m=0 is the mean, the zonal modes are m=0 etc. This indexing is more common in mathematics.\n\n\n\n\n\n","category":"type"},{"location":"lowertriangularmatrices/#Base.fill!-Tuple{LowerTriangularArray, Any}","page":"LowerTriangularMatrices","title":"Base.fill!","text":"fill!(L::LowerTriangularArray, x) -> LowerTriangularArray\n\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/#Base.length-Tuple{LowerTriangularArray}","page":"LowerTriangularMatrices","title":"Base.length","text":"length(L::LowerTriangularArray) -> Any\n\n\nLength of a LowerTriangularArray defined as number of non-zero elements.\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#Base.size","page":"LowerTriangularMatrices","title":"Base.size","text":"size(L::LowerTriangularArray; ...) -> Any\nsize(\n L::LowerTriangularArray,\n base::Type{<:SpeedyWeather.LowerTriangularMatrices.IndexBasis};\n as\n) -> Any\n\n\nSize of a LowerTriangularArray defined as size of the flattened array if as <: AbstractVector and as if it were a full matrix when as <: AbstractMatrix` .\n\n\n\n\n\n","category":"function"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.eachharmonic-Tuple{LowerTriangularArray, Vararg{LowerTriangularArray}}","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.eachharmonic","text":"eachharmonic(\n L1::LowerTriangularArray,\n Ls::LowerTriangularArray...\n) -> Any\n\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.eachharmonic-Tuple{LowerTriangularArray}","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.eachharmonic","text":"eachharmonic(L::LowerTriangularArray) -> Any\n\n\ncreates unit_range::UnitRange to loop over all non-zeros/spherical harmonics numbers in a LowerTriangularArray L. Like eachindex but skips the upper triangle with zeros in L.\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.eachmatrix-Tuple{LowerTriangularArray, Vararg{LowerTriangularArray}}","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.eachmatrix","text":"eachmatrix(\n L1::LowerTriangularArray,\n Ls::LowerTriangularArray...\n) -> Any\n\n\nIterator for the non-horizontal dimensions in LowerTriangularArrays. Checks that the LowerTriangularArrays match according to lowertriangular_match.\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.eachmatrix-Tuple{LowerTriangularArray}","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.eachmatrix","text":"eachmatrix(L::LowerTriangularArray) -> Any\n\n\nIterator for the non-horizontal dimensions in LowerTriangularArrays. To be used like\n\nfor k in eachmatrix(L)\n L[1, k]\n\nto loop over every non-horizontal dimension of L.\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.find_L-Tuple{Base.Broadcast.Broadcasted}","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.find_L","text":"L = find_L(Ls) returns the first LowerTriangularArray among the arguments. Adapted from Julia documentation of Broadcast interface\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.ij2k-Tuple{Integer, Integer, Integer}","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.ij2k","text":"ij2k(i::Integer, j::Integer, m::Integer) -> Any\n\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":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.k2ij-Tuple{Integer, Integer}","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.k2ij","text":"k2ij(k::Integer, m::Integer) -> Tuple{Any, Any}\n\n\nConverts the linear index k in the lower triangle into a pair (i, j) of indices of the matrix in column-major form. (Formula taken from Angeletti et al, 2019, https://hal.science/hal-02047514/document)\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.lowertriangular_match-Tuple{LowerTriangularArray, LowerTriangularArray}","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.lowertriangular_match","text":"lowertriangular_match(\n L1::LowerTriangularArray,\n L2::LowerTriangularArray;\n horizontal_only\n) -> Any\n\n\nTrue if both L1 and L2 are of the same size (as matrix), but ignores singleton dimensions, e.g. 5x5 and 5x5x1 would match. With horizontal_only=true (default false) ignore the non-horizontal dimensions, e.g. 5x5, 5x5x1, 5x5x2 would all match.\n\n\n\n\n\n","category":"method"},{"location":"lowertriangularmatrices/#SpeedyWeather.LowerTriangularMatrices.lowertriangular_match-Tuple{LowerTriangularArray, Vararg{LowerTriangularArray}}","page":"LowerTriangularMatrices","title":"SpeedyWeather.LowerTriangularMatrices.lowertriangular_match","text":"lowertriangular_match(\n L1::LowerTriangularArray,\n Ls::LowerTriangularArray...;\n kwargs...\n) -> Any\n\n\nTrue if all lower triangular matrices provided as arguments match according to lowertriangular_match wrt to L1 (and therefore all).\n\n\n\n\n\n","category":"method"},{"location":"radiation/#Radiation","page":"Radiation","title":"Radiation","text":"","category":"section"},{"location":"radiation/#Longwave-radiation-implementations","page":"Radiation","title":"Longwave radiation implementations","text":"","category":"section"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"Currently implemented is","category":"page"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"using InteractiveUtils # hide\nusing SpeedyWeather\nsubtypes(SpeedyWeather.AbstractLongwave)","category":"page"},{"location":"radiation/#Uniform-cooling","page":"Radiation","title":"Uniform cooling","text":"","category":"section"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"Following Paulius and Garner[PG06], the uniform cooling of the atmosphere is defined as ","category":"page"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"fracpartial Tpartial t = begincases - tau^-1quadtextforquad T T_min \n fracT_strat - Ttau_strat quad textelse endcases","category":"page"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"with tau = 16h resulting in a cooling of -1.5K/day for most of the atmosphere, except below temperatures of T_min = 2075K in the stratosphere where a relaxation towards T_strat = 200K with a time scale of tau_strat = 5days is present.","category":"page"},{"location":"radiation/#Jeevanjee-radiation","page":"Radiation","title":"Jeevanjee radiation","text":"","category":"section"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"Jeevanjee and Zhou [JZ22] (eq. 2) define a longwave radiative flux F for atmospheric cooling as (following Seeley and Wordsworth [SW23], eq. 1)","category":"page"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"fracdFdT = α*(T_t - T)","category":"page"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"The flux F (in Wm^2K) is a vertical upward flux between two layers (vertically adjacent) of temperature difference dT. The change of this flux across layers depends on the temperature T and is a relaxation term towards a prescribed stratospheric temperature T_t = 200K with a radiative forcing constant alpha = 0025 Wm^2K^2. Two layers of identical temperatures T_1 = T_2 would have no net flux between them, but a layer below at higher temperature would flux into colder layers above as long as its temperature T T_t. This flux is applied above the lowermost layer and above, leaving the surface fluxes unchanged. The uppermost layer is tied to T_t through a relaxation at time scale tau = 6h","category":"page"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"fracpartial Tpartial t = fracT_t - Ttau","category":"page"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"The flux F is converted to temperature tendencies at layer k via","category":"page"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"fracpartial T_kpartial t = (F_k+12 - F_k-12)fracgDelta p c_p","category":"page"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"The term in parentheses is the absorbed flux in layer k of the upward flux from below at interface k+12 (k increases downwards, see Vertical coordinates and resolution and Sigma coordinates). Delta p = p_k+12 - p_k-12 is the pressure thickness of layer k, gravity g and heat capacity c_p.","category":"page"},{"location":"radiation/#Shortwave-radiation","page":"Radiation","title":"Shortwave radiation","text":"","category":"section"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"Currently implemented is","category":"page"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"subtypes(SpeedyWeather.AbstractShortwave)","category":"page"},{"location":"radiation/#References","page":"Radiation","title":"References","text":"","category":"section"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"[PG06]: Paulius and Garner, 2006. JAS. DOI:10.1175/JAS3705.1","category":"page"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"[SW23]: Seeley, J. T. & Wordsworth, R. D. Moist Convection Is Most Vigorous at Intermediate Atmospheric Humidity. Planet. Sci. J. 4, 34 (2023). DOI:10.3847/PSJ/acb0cb","category":"page"},{"location":"radiation/","page":"Radiation","title":"Radiation","text":"[JZ22]: Jeevanjee, N. & Zhou, L. On the Resolution‐Dependence of Anvil Cloud Fraction and Precipitation Efficiency in Radiative‐Convective Equilibrium. J Adv Model Earth Syst 14, e2021MS002759 (2022). DOI:10.1029/2021MS002759","category":"page"},{"location":"convection/#Convection","page":"Convection","title":"Convection","text":"","category":"section"},{"location":"convection/","page":"Convection","title":"Convection","text":"Convection is the atmospheric process of rising motion because of positively buoyant air parcels compared to its surroundings. In hydrostatic models like the primitive equation model in SpeedyWeather.jl convection has to be parameterized as the vertical velocity is not a prognostic variable that depends on vertical stability but rather diagnosed to satisfy horizontal divergence. Convection can be shallow and non-precipitating denoting that buoyant air masses can rise but do not reach saturation until they reach a level of zero buoyancy. But convection can also be deep denoting that saturation has been reached during ascent whereby the latent heat release from condensation provides additional energy for further ascent. Deep convection is therefore usually also precipitating as the condensed humidity forms cloud droplets that eventually fall down as convective precipitation. See also Large-scale condensation in comparison.","category":"page"},{"location":"convection/#Convection-implementations","page":"Convection","title":"Convection implementations","text":"","category":"section"},{"location":"convection/","page":"Convection","title":"Convection","text":"Currently implemented are","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"using InteractiveUtils # hide\nusing SpeedyWeather\nsubtypes(SpeedyWeather.AbstractConvection)","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"which are described in the following.","category":"page"},{"location":"convection/#BettsMiller","page":"Convection","title":"Simplified Betts-Miller convection","text":"","category":"section"},{"location":"convection/","page":"Convection","title":"Convection","text":"We follow the simplification of the Betts-Miller convection scheme [Betts1986][BettsMiller1986] as studied by Frierson, 2007 [Frierson2007]. The central idea of this scheme is to represent the effect of convection as an adjustment towards a (pseudo-) moist adiabat reference profile and its associated humidity profile. Meaning that conceptually for every vertical column in the atmosphere we","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"Diagnose the vertical temperature and humidity profile of the environment relative to the adiabat up to the level of zero buoyancy.\nDecide whether convection should take place and whether it is deep (precipitating) or shallow (non-precipitating).\nRelax temperature and humidity towards (corrected) profiles from 1.","category":"page"},{"location":"convection/#Reference-profiles","page":"Convection","title":"Reference profiles","text":"","category":"section"},{"location":"convection/","page":"Convection","title":"Convection","text":"The dry adiabat is","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"T = T_0 (fracpp_0)^fracRc_p","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"The temperature T of an air parcel at pressure p is determined by the temperature T_0 it had at pressure p_0 (this can be surface but it does not have to be) and the gas constant for dry air R = 28704 JKkg and the heat capacity c_p = 100464 JKkg. The pseudo adiabat follows the dry adiabat until saturation is reached (the lifting condensation level, often abbreviated to LCL), q q^star. Then it follows the pseudoadiabatic lapse rate","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"Gamma = -fracdTdz = fracgc_pleft(\n frac 1 + fracq^star L_v (1-q^star)^2 R_d T_v\n 1 + fracq^star L_v^2(1-q^star)^2 c_p R_v T^2right)","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"with gravity g, heat capacity c_p, the saturation specific humidity of the parcel q^star (which is its specific humidity given that it has already reached saturation), latent heat of vaporization L_v, dry gas constant R_d, water vapour gas constant R_v, and Virtual temperature T_v. Starting with a temperature T and humidity q = q^star at the lifting condensation level temperature aloft changes with dT = -fracdPhic_p() between two layers separated dPhi in geopotential Phi apart. On that new layer, q^star is recalculated as well as the virtual temperature T_v = T(1 + mu q^star). mu is derived from the ratio of dry to vapour gas constants see Virtual temperature. Note that the pseudoadiabatic ascent is independent of the environmental temperature and humidity and function of temperature and humidity of the parcel only (although that one starts with surface temperature and humidity from the environment). Solely the level of zero buoyancy is determined by comparing the parcel's virtual temperature T_v to the virtual temperature of the environment T_ve at that level. Level of zero buoyancy is reached when T_v = T_ve but continues for T_v T_ve which means that the parcel is still buoyant. Note that the virtual temperature includes the effect that humidity has on its density.","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"The (absolute) temperature a lifted parcel has during ascent (following its pseudoadiabat, dry and/ or moist, until reaching the level of zero buoyancy) is then taken as the reference temperature profile T_ref that the Betts-Miller convective parameterization relaxes towards as a first guess (with a following adjustment as discussed below). The humidity profile is taken as q_ref = RH_SBMT_ref with a parameter RH_SBM (default RH_SBM = 07) of the scheme (Simplified Betts-Miller, SBM) that determines a constant relative humidity of the reference profile.","category":"page"},{"location":"convection/#First-guess-relaxation","page":"Convection","title":"First-guess relaxation","text":"","category":"section"},{"location":"convection/","page":"Convection","title":"Convection","text":"With the Reference profiles T_ref q_ref obtained, we relax the actual environmental temperature T and specific humidity q in the column","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"beginaligned\ndelta q = - fracq - q_reftau_SBM \ndelta T = - fracT - T_reftau_SBM\nendaligned","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"with the second parameter of the parameterization, the time scale tau_SBM. Note that because this is a first-guess relaxation, these tendencies are not actually the resulting tendencies from this scheme. Those will be calculated in Corrected relaxation.","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"Note that above the level of zero buoyancy no relaxation takes place delta T = delta q = 0, or, equivalently T = T_ref, q = q_ref there. Vertically integration from surface p_0 to level of zero buoyancy in pressure coordinates p_LZB yields","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"beginaligned\nP_q = - int_p_0^p_LZB delta q fracdpg \nP_T = int_p_0^p_LZB fracc_pL_v delta T fracdpg\nendaligned","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"P_q is the precipitation in units of kg m^2 s due to drying (as a consequence of the humidity tendency) and P_T is the precipitation in the same units due to warming (as resulting from temperature tendencies). Note that they are the vertically difference between current profiles and the references profiles, so if P_q 0 this would mean that a convective adjustment to q_ref would release humidity from the column through condensation, but P_q can also be negative. Consequently similar for P_T.","category":"page"},{"location":"convection/#Convective-criteria","page":"Convection","title":"Convective criteria","text":"","category":"section"},{"location":"convection/","page":"Convection","title":"Convection","text":"We now distinguish three cases","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"Deep convection when P_T 0 and P_q 0\nShallow convection when P_T 0 and P_q = 0\nNo convection for P_T = 0.","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"Note that to evaluate these cases it is not necessary to divide by tau_SBM in the first-guess relaxation, neither are the 1g and tfracc_pg L_v necessary to multiply during the vertical integration as all are positive constants. While this changes the units of P_T P_q they can be reused in the following.","category":"page"},{"location":"convection/#Deep-convection","page":"Convection","title":"Deep convection","text":"","category":"section"},{"location":"convection/","page":"Convection","title":"Convection","text":"Following Frierson, 2007 [Frierson2007] in order to conserve enthalpy we correct the reference profile for temperature T_ref to T_ref 2 so that P_T = P_q.","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"T_ref 2 = T_ref + frac1Delta p c_p int_p_0^p_LZB c_p (T - T_ref) + L_v (q - q_ref) dp","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"Delta p is the pressure difference p_LZB - p_0. The terms inside the integral are rearranged compared to Frierson, 2007 to show that the vertical integral in First-guess relaxation really only has to be computed once.","category":"page"},{"location":"convection/#Shallow-convection","page":"Convection","title":"Shallow convection","text":"","category":"section"},{"location":"convection/","page":"Convection","title":"Convection","text":"In the following we describe the \"qref\" scheme from Frierson, 2007 which corrects reference profiles for both temperature and humidity to guarantee that P_q = 0, i.e. no precipitation during convection. In that sense, shallow convection is non-precipitating. Although shallow convection is supposed to be shallow we do not change the height of the convection and keep using the p_LZB determined during the calculation of the Reference profiles.","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"beginaligned\nDelta q = int_p_0^p_LZB q - q_ref dp \nQ_ref = int_p_0^p_LZB -q_ref dp \nf_q = 1 - fracDelta qQ_ref \nq_ref 2 = f_q q_ref \nDelta T = frac1Delta p int_p_0^p_LZB -(T - T_ref) dp \nT_ref2 = T_ref - Delta T\nendaligned","category":"page"},{"location":"convection/#Corrected-relaxation","page":"Convection","title":"Corrected relaxation","text":"","category":"section"},{"location":"convection/","page":"Convection","title":"Convection","text":"After the reference profiles have been corrected in Deep convection and Shallow convection we actually calculate tendencies from","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"beginaligned\ndelta q = - fracq - q_ref 2tau_SBM \ndelta T = - fracT - T_ref 2tau_SBM\nendaligned","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"with tau_SBM = 2h as default.","category":"page"},{"location":"convection/#Convective-precipitation","page":"Convection","title":"Convective precipitation","text":"","category":"section"},{"location":"convection/","page":"Convection","title":"Convection","text":"The convective precipitation P results then from the vertical integration of the delta q tendencies, similar to Large-scale precipitation.","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"P = -int fracDelta tg rho delta q dp","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"In the shallow convection case P=0 due to the correction even though in the first guess relaxation P0 was possible, but for deep convection P0 by definition.","category":"page"},{"location":"convection/#Dry-convection","page":"Convection","title":"Dry convection","text":"","category":"section"},{"location":"convection/","page":"Convection","title":"Convection","text":"In the primitive equation model with humidity the Betts-Miller convection scheme as described above is defined. Without humidity, a dry version reduces to the Shallow convection case. The two different shallow convection schemes in Frierson 2007[Frierson2007], the \"shallower\" shallow convection scheme and the \"qref\" (as implemented here in Shallow convection) in that case also reduce to the same formulation. The dry Betts-Miller convection scheme is the default in the primitive equation model without humidity.","category":"page"},{"location":"convection/#References","page":"Convection","title":"References","text":"","category":"section"},{"location":"convection/","page":"Convection","title":"Convection","text":"[Betts1986]: Betts, A. K., 1986: A new convective adjustment scheme. Part I: Observational and theoretical basis. Quart. J. Roy. Meteor. Soc.,112, 677-691. DOI: 10.1002/qj.49711247307","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"[BettsMiller1986]: Betts, A. K. and M. J. Miller, 1986: A new convective adjustment scheme. Part II: Single column tests using GATE wave, BOMEX, ATEX and Arctic air-mass data sets. Quart. J. Roy. Meteor. Soc.,112, 693-709. DOI: 10.1002/qj.49711247308","category":"page"},{"location":"convection/","page":"Convection","title":"Convection","text":"[Frierson2007]: Frierson, D. M. W., 2007: The Dynamics of Idealized Convection Schemes and Their Effect on the Zonally Averaged Tropical Circulation. J. Atmos. Sci., 64, 1959-1976. DOI:10.1175/JAS3935.1","category":"page"},{"location":"custom_netcdf_output/#Customizing-netCDF-output","page":"NetCDF output variables","title":"Customizing netCDF output","text":"","category":"section"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"SpeedyWeather's NetCDF output is modularised for the output variables, meaning you can add relatively easy new variables to be outputted alongside the default variables in the netCDF file. We explain here how to define a new output variable largely following the logic of Extending SpeedyWeather.","category":"page"},{"location":"custom_netcdf_output/#New-output-variable","page":"NetCDF output variables","title":"New output variable","text":"","category":"section"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"Say we want to output the Vertical velocity. In Sigma coordinates on every time step, one has to integrate the divergence vertically to know where the flow is not divergence-free, meaning that the horizontally converging or diverging motion is balanced by a vertical velocity. This leads to the variable partial sigma partial t, which is the equivalent of Vertical velocity in the Sigma coordinates. This variable is calculated and stored at every time step in ","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"simulation.diagnostic_variables.dynamics.σ_tend","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"So how do we access it and add it the netCDF output?","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"First we define VerticalVelocityOutput as a new struct subtype of SpeedyWeather.AbstractOutputVariable we add the required fields name::String, unit::String, long_name::String and dims_xyzt::NTuple{4, Bool} (we skip the optional fields for missing_value or compression).","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"using SpeedyWeather\n\n@kwdef struct VerticalVelocityOutput <: SpeedyWeather.AbstractOutputVariable\n name::String = \"w\"\n unit::String = \"s^-1\"\n long_name::String = \"vertical velocity dσ/dt\"\n dims_xyzt::NTuple{4, Bool} = (true, true, true, true)\nend","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"By default (using the @kwdef macro) we set the dimensions in dims_xyzt to 4D because the vertical velocity is a 3D variable that we want to output on every time step. So while dims_xyzt is a required field for every output variable you should not actually change it as it is an inherent property of the output variable.","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"You can now add this variable to the NetCDFOutput as already described in Output variables","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"spectral_grid = SpectralGrid()\noutput = NetCDFOutput(spectral_grid)\nadd!(output, VerticalVelocityOutput())","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"Note that here we skip the SpeedyWeather. prefix which would point to the SpeedyWeather scope but we have defined VerticalVelocityOutput in the global scope.","category":"page"},{"location":"custom_netcdf_output/#Extend-the-output!-function","page":"NetCDF output variables","title":"Extend the output! function","text":"","category":"section"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"While we have defined a new output variable we have not actually defined how to output it. Because in the end we will need to write that variable into the netcdf file in NetCDFOutput, which we describe now. We have to extend extend SpeedyWeather's output! function with the following function signature","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"function SpeedyWeather.output!(\n output::NetCDFOutput,\n variable::VerticalVelocityOutput,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel,\n)\n # INTERPOLATION\n w = output.grid3D # scratch grid to interpolate into\n (; σ_tend) = diagn.dynamics # point to data in diagnostic variables\n RingGrids.interpolate!(w, σ_tend , output.interpolator)\n\n # WRITE TO NETCDF\n i = output.output_counter # output time step to write\n output.netcdf_file[variable.name][:, :, :, i] = w\n return nothing\nend","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"The first argument has to be ::NetCDFOutput as this is the argument we write into (i.e. mutate). The second argument has to be ::VerticalVelocityOutput so that Julia's multiple dispatch calls this output! method for our new variable. Then the prognostic, diagnostic variables and the model follows which allows us generally to read any data and use it to write into the netCDF file.","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"In most cases you will need to interpolate any gridded variables inside the model (which can be on a reduced grd) onto the output grid (which has to be a full grid, see Output grid). For that the NetCDFOutput has two scratch arrays grid3D and grid2D which are of type and size as defined by the output_Grid and nlat_half arguments when creating the NetCDFOutput. So the three lines for interpolation are essentially those in which your definition of a new output variable is linked with where to find that variable in diagnostic_variables. You can, in principle, also do any kind of computation here, for example adding two variables, normalising data and so on. In the end it has to be on the output_Grid hence you probably do not want to skip the interpolation step but you are generally allowed to do much more here before or after the interpolation.","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"The last two lines are then just about actually writing to netcdf. For any variable that is written on every output time step you can use the output counter i to point to the correct index i in the netcdf file as shown here. For 2D variables (horizontal+time) the indexing would be [:, :, i]. 2D variables without time you only want to write once (because they do not change) the indexing would change to [:, :] and you then probably want to add a line at the top like output.output_counter > 1 || return nothing to escape immediately after the first output time step. But you could also check for a specific condition (e.g. a new temperature record in a given location) and only then write to netcdf. Just some ideas how to customize this even further.","category":"page"},{"location":"custom_netcdf_output/#Reading-the-new-variable","page":"NetCDF output variables","title":"Reading the new variable","text":"","category":"section"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"Now let's try this in a primitive dry model","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"model = PrimitiveDryModel(spectral_grid; output)\nmodel.output.variables[:w]","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"By passing on output to the model constructor the output variables now contain w and we see it here as we have defined it earlier.","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"simulation = initialize!(model)\nrun!(simulation, period=Day(5), output=true)\n\n# read netcdf data\nusing NCDatasets\npath = joinpath(model.output.run_path, model.output.filename)\nds = NCDataset(path)\nds[\"w\"]","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"Fantastic, it's all there. We wrap this back into a FullGaussianGrid but ignore the mask (there are no masked values) in the netCDF file which causes a Union{Missing, Float32} element type by reading out the raw data with .var. And visualise the vertical velocity in sigma coordinates (remember this is actually partial sigma partial t) of the last time step (index end) stored on layer k=4 (counted from the top)","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"w = FullGaussianGrid(ds[\"w\"].var[:, :, :, :], input_as=Matrix)\n\nusing CairoMakie\nheatmap(w[:, 4, end], title=\"vertical velocity dσ/dt at k=4\")\nsave(\"sigma_tend.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"(Image: Sigma tendency)","category":"page"},{"location":"custom_netcdf_output/","page":"NetCDF output variables","title":"NetCDF output variables","text":"This is now the vertical velocity between layer k=4 and k=5. You can check that the vertical velocity on layer k=8 is actually zero (because that is the boundary condition at the surface) and so would be the velocity between k=0 and k=1 at the top of the atmosphere, which however is not explicitly stored. The vertical velocity is strongest on the wind and leeward side of mountains which is reassuring and all the analysis we want to do here for now.","category":"page"},{"location":"gradients/#Gradient-operators","page":"Gradient operators","title":"Gradient operators","text":"","category":"section"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","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. We call them divergence, curl, ∇, ∇², ∇⁻² (as well as their in-place versions with !) within the limits of unicode characters and Julia syntax. These functions are defined for inputs being spectral coefficients (i.e. LowerTriangularMatrix) or gridded fields (i.e. <:AbstractGrid) and also allow as an additional argument a spectral transform object (see SpectralTransform) which avoids recalculating it under the hood.","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"info: SpeedyTransforms assumes a unit sphere\nThe gradient 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 divergence, curl, ∇, ∇², ∇⁻² omit the radius scaling unless you provide the optional keyword radius (or you can do ./= radius manually). Also note that meridional derivates in spectral space expect a cos^-1(theta) scaling. Details are always outlined in the respective docstrings, ?∇ for example.","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"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":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"hatnabla A = left(fracpartial Apartial lambda cos(theta)fracpartial Apartial theta right) =\nRcos(theta)nabla A","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","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":"gradients/","page":"Gradient operators","title":"Gradient operators","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":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"And the Laplace operators omit a R^2 (radius R) scaling, i.e.","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"hatnabla^-2A = frac1R^2nabla^-2A quad hatnabla^2A = R^2nabla^2A","category":"page"},{"location":"gradients/#Gradient","page":"Gradient operators","title":"Gradient ∇","text":"","category":"section"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"We illustrate the usage of the gradient function ∇. Let us create some fake data G on the grid first ","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"using SpeedyWeather, CairoMakie\n\n# create some data with wave numbers 0,1,2,3,4\ntrunc = 64 # 1-based maximum degree of spherical harmonics\nL = randn(LowerTriangularMatrix{ComplexF32}, trunc, trunc)\nspectral_truncation!(L, 5) # remove higher wave numbers\nG = transform(L)\nheatmap(G, title=\"Some fake data G\") # requires `using CairoMakie`\nsave(\"gradient_data.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"(Image: Gradient data)","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"Now we can take the gradient as follows","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"dGdx, dGdy = ∇(G)\nnothing # hide","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"this transforms internally back to spectral space takes the gradients in zonal and meridional direction, transforms to grid-point space again und unscales the coslat-scaling on the fly but assumes a radius of 1 as the keyword argument radius was not provided. Use ∇(G, radius=6.371e6) for a gradient on Earth in units of \"data unit\" divided by meters.","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"heatmap(dGdx, title=\"dG/dx on the unit sphere\")\nsave(\"dGdx.png\", ans) # hide\nheatmap(dGdy, title=\"dG/dy on the unit sphere\")\nsave(\"dGdy.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"(Image: dGdx) (Image: dGdy)","category":"page"},{"location":"gradients/#Geostrophy","page":"Gradient operators","title":"Geostrophy","text":"","category":"section"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"Now, we want to use the following example to illustrate a more complex use of the gradient operators: We have u v and want to calculate eta in the shallow water system from it following geostrophy. Analytically we have","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"-fv = -gpartial_lambda eta quad fu = -gpartial_theta eta","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"which becomes, if you take the divergence of these two equations","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"zeta = fracgfnabla^2 eta","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"Meaning that if we start with u v 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":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"Let us start by generating some data","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"spectral_grid = SpectralGrid(trunc=31, nlayers=1)\nforcing = SpeedyWeather.JetStreamForcing(spectral_grid)\ndrag = QuadraticDrag(spectral_grid)\nmodel = ShallowWaterModel(spectral_grid; forcing, drag)\nsimulation = initialize!(model);\nrun!(simulation, period=Day(30))\nnothing # hide","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","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":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"u = simulation.diagnostic_variables.grid.u_grid[:, 1] # [:, 1] for 1st layer\nv = simulation.diagnostic_variables.grid.v_grid[:, 1]\nvor = curl(u, v, radius = spectral_grid.radius)\nnothing # hide","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","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":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"RingGrids.scale_coslat⁻¹!(u)\nRingGrids.scale_coslat⁻¹!(v)\n\nS = SpectralTransform(u, one_more_degree=true)\nus = transform(u, S)\nvs = transform(v, S)\n\nvor = curl(us, vs, radius = spectral_grid.radius)","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"(Copies of) the velocity fields are unscaled by the cosine of latitude (see above), then transformed into spectral space, and the curl has the keyword argument radius to divide internally by the radius (if not provided it assumes a unit sphere). We always unscale vector fields by the cosine of latitude if they are provided to curl or divergence in spectral as you can only do this scaling effectively in grid-point space. The methods accepting arguments as grids generally do this for you. If in doubt, check the docstrings, ?∇ for example.","category":"page"},{"location":"gradients/#One-more-degree-for-spectral-fields","page":"Gradient operators","title":"One more degree for spectral fields","text":"","category":"section"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","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":"gradients/","page":"Gradient operators","title":"Gradient operators","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":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"You may also generally assume that a SpectralTransform struct precomputed for some truncation, say l_max = m_max = T could also be used for smaller lower triangular matrices. While this is mathematically true, this does not work here in practice because LowerTriangularMatrices are implemented as a vector. So always use a SpectralTransform struct that fits matches your resolution exactly (otherwise an error will be thrown).","category":"page"},{"location":"gradients/#Example:-Geostrophy-(continued)","page":"Gradient operators","title":"Example: Geostrophy (continued)","text":"","category":"section"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","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":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"vor_grid = transform(vor, Grid=spectral_grid.Grid)\nf = coriolis(vor_grid) # create Coriolis parameter f on same grid with default rotation\ng = model.planet.gravity\nfζ_g = @. vor_grid * f / g # in-place and element-wise\nnothing # hide","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"Now we need to apply the inverse Laplace operator to fzetag which we do as follows","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"fζ_g_spectral = transform(fζ_g, one_more_degree=true)\n\nR = spectral_grid.radius\nη = SpeedyTransforms.∇⁻²(fζ_g_spectral) * R^2\nη_grid = transform(η, Grid=spectral_grid.Grid)\nnothing # hide","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"Note the manual scaling with the radius R^2 here. We now compare the results","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"using CairoMakie\nheatmap(η_grid, title=\"Geostrophic interface displacement η [m]\")\nsave(\"eta_geostrophic.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"(Image: Geostrophic eta)","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"Which is the interface displacement assuming geostrophy. The actual interface displacement contains also ageostrophy","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"η_grid2 = simulation.diagnostic_variables.grid.pres_grid\nheatmap(η_grid2, title=\"Interface displacement η [m] with ageostrophy\")\nsave(\"eta_ageostrophic.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"(Image: Ageostrophic eta)","category":"page"},{"location":"gradients/","page":"Gradient operators","title":"Gradient operators","text":"Strikingly similar! The remaining differences are the ageostrophic motions but also note that the mean can be 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 can be higher or lower depending on the mass/volume in the shallow water system, see Mass conservation.","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 = (u v) 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_u F_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 u v or zeta mathcalD 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 x y: 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 U V\nUnscale the cos(theta) factor to obtain u v\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 u v 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 (A B)_lm in spectral space and add to forcing of zeta_lm\nCompute the divergence of (A B)_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 uh vh 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 zeta mathcalD 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 u v zeta mathcalD eta 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 0 1 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_mathcalD N_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":"ocean/#Ocean","page":"Ocean","title":"Ocean","text":"","category":"section"},{"location":"ocean/","page":"Ocean","title":"Ocean","text":"The ocean in SpeedyWeather.jl is defined with two horizontal fields in the prognostic variables which has a field ocean, i.e. simulation.prognostic_variables.ocean.","category":"page"},{"location":"ocean/","page":"Ocean","title":"Ocean","text":"ocean.sea_surface_temperature with units of Kelvin [K].\nocean.sea_ice_concentration with units of area fraction [1].","category":"page"},{"location":"ocean/","page":"Ocean","title":"Ocean","text":"Both are two-dimensional grids using the same grid type and resolution as the dynamical core. So both sea surface temperature and sea ice concentration are globally defined but their mask is defined with The land-sea mask. However, one should still set grid cells where the sea surface temperature is not defined to NaN in which case any fluxes are zero. This is important when a fractional land-sea mask does not align with the sea surface temperatures to not produce unphysical fluxes. The sea ice concentration is simply set to zero everywhere where there is no sea ice.","category":"page"},{"location":"ocean/","page":"Ocean","title":"Ocean","text":"Note that neither sea surface temperature, land-sea mask or orography have to agree. It is possible to have an ocean on top of a mountain. For an ocean grid-cell that is (partially) masked by the land-sea mask, its value will be (fractionally) ignored in the calculation of surface fluxes (potentially leading to a zero flux depending on land surface temperatures). For an ocean grid cell that is NaN but not masked by the land-sea mask, its value is always ignored.","category":"page"},{"location":"ocean/#Custom-ocean-model","page":"Ocean","title":"Custom ocean model","text":"","category":"section"},{"location":"ocean/","page":"Ocean","title":"Ocean","text":"Now the ocean model is expected to change ocean.sea_surface_temperature and/or ocean.sea_ice_concentration on a given time step. A new ocean model has to be defined as","category":"page"},{"location":"ocean/","page":"Ocean","title":"Ocean","text":"struct CustomOceanModel <: AbstractOcean\n # fields, coefficients, whatever is constant, \nend","category":"page"},{"location":"ocean/","page":"Ocean","title":"Ocean","text":"and can have parameters like CustomOceanModel{T} and any fields. CustomOceanModel then needs to extend the following functions","category":"page"},{"location":"ocean/","page":"Ocean","title":"Ocean","text":"function initialize!(\n ocean_model::CustomOceanModel,\n model::PrimitiveEquation)\n # your code here to initialize the ocean model itself\n # you can use other fields from model, e.g. model.geometry\nend\n\nfunction initialize!( \n ocean::PrognosticVariablesOcean,\n time::DateTime,\n ocean_model::CustomOceanModel,\n model::PrimitiveEquation)\n \n # your code here to initialize the prognostic variables for the ocean\n # namely, ocean.sea_surface_temperature, ocean.sea_ice_concentration, e.g.\n # ocean.sea_surface_temperature .= 300 # 300K everywhere\n\n # ocean also has its own time, set initial time\n ocean.time = time\nend","category":"page"},{"location":"ocean/","page":"Ocean","title":"Ocean","text":"Note that the first is only to initialize the CustomOceanModel not the prognostic variables. For example SeasonalOceanClimatology <: AbstractOcean loads in climatological sea surface temperatures for every time month in the first initialize! but only writes them (given time) into the prognostic variables in the second initialize!. They are internally therefore also called in that order. Note that the function signatures should not be changed except to define a new method for CustomOceanModel or whichever name you chose.","category":"page"},{"location":"ocean/","page":"Ocean","title":"Ocean","text":"Then you have to extend the ocean_timestep! function which has a signature like","category":"page"},{"location":"ocean/","page":"Ocean","title":"Ocean","text":"function ocean_timestep!(\n ocean::PrognosticVariablesOcean,\n time::DateTime,\n ocean_model::CustomOceanModel,\n)\n # your code here to change the ocean.sea_surface_temperature and/or\n # ocean.sea_ice_concentration on any timestep\n # you should also synchronize the clocks when executed like\n # ocean.time = time\nend","category":"page"},{"location":"ocean/","page":"Ocean","title":"Ocean","text":"which is called on every time step before the land and before the parameterization and therefore also before the dynamics. You can schedule the execution with Schedules or you can use the ocean.time time to determine when last the ocean time step was executed and whether it should be executed now, e.g. (time - ocean.time) < ocean_model.Δt && return nothing would not execute unless the period of the ocean_model.Δt time step has passed. Note that the ocean.sea_surface_temperature or .sea_ice_concentration are unchanged if the ocean time step is not executed, meaning that the sea surface temperatures for example can lag behind the dynamical core for some days essentially assuming constant temperatures throughout that period. Any ocean model with constant temperatures and sea ice should just return nothing.","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 = (0 2pi) and colatitudes theta = (-pi2 pi2), 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 = (0 pi) (0 at the north pole) but the dynamical core uses latitudes theta = (-pi2 pi2) (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(phi theta) (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(phi theta) = sum_l=0^infty sum_m=-l^l a_lm Y_lm(phi theta)","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"We obtain an approximation with a finite set of a_l m 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_l m 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_l m^*. 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_l m=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(phi theta) = sum_l=0^l_max sum_m=0^l (2-delta_m0) a_lm Y_lm(phi theta)","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_l m(phi pi-theta) = (-1)^l+mY_lm(phi phi)","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(lambda theta) into the spectral space of the spherical harmonics by","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"a_l m = int_0^2pi int_0^pi f(phi theta) Y_l m(phi theta) 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 (-fracpi2 fracpi2). A discretization with N grid points at location (phi_i theta_i), indexed by i can be written as [Willmert2020]","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"hata_l m = sum_i f(phi_i theta_i) Y_l m(phi_i theta_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 -1 1 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_l m = sum_j left sum_i f(phi_i theta_j) e^-imphi_i right lambda_l m(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 l m 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 = (u v) 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 u v are then obtained from (u v) = 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(phi theta) (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_l mdtheta = -lepsilon_l+1 mP_l+1 m + (l+1)epsilon_l mP_l-1 m","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_l m = 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(lambda theta) = sum_l mPsi_l mP_l m(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_l mPsi_l me^imlambdacosthetapartial_theta P_l m","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_l m then yields","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"(cos(theta)u)_l m = -frac1R(-(l-1)epsilon_l mPsi_l-1 m + (l+2)epsilon_l+1 mPsi_l+1 m)","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"To obtain the coefficient of each spherical harmonic l m of the meridional gradient of a spectral field, two coefficients at l-1 m and l+1 m 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+1 m Psi_l-1 m. 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 u v 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 u v 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 (u v) = 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_l m = frac12piint_-tfracpi2^tfracpi2int_0^2pi zeta(lambda theta)\nP_l m(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_l m 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_l m (u costheta) dtheta = -int costheta partial_theta P_l m\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+1 mP_l+1 m + (l+1)epsilon_l m P_l-1 m 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 l m harmonic will project ontoP_l m. 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_l m = imv^*_l m + (l+1)epsilon_l mu^*_l-1 m - lepsilon_l+1 mu^*_l+1 m \nRD_l m = imu^*_l m - (l+1)epsilon_l mv^*_l-1 m + lepsilon_l+1 mv^*_l+1 m \nendaligned","category":"page"},{"location":"spectral_transform/","page":"Spherical Harmonic Transform","title":"Spherical Harmonic Transform","text":"And the divergence D is similar, but (u v) to (-v u). We have moved the scaling with the radius R directly into zeta D 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_l m = frac-l(l+1)R^2Psi_l m","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_0 0 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_l m = fracR^2-l(l+1)zeta_l m quad foralll m 0 \nPsi_0 0 = 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 U V directly from zeta D 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_l m = -fraciml(l+1)(RD)_l m + fracepsilon_l+1 ml+1(Rzeta)_l+1 m -\nfracepsilon_l ml(Rzeta)_l-1 m \nV_l m = -fraciml(l+1)(Rzeta)_l m - fracepsilon_l+1 ml+1(RD)_l+1 m +\nfracepsilon_l ml(RD)_l-1 m \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 zeta D 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 i j k 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":"stochastic_physics/#Stochastic-physics","page":"Stochastic physics","title":"Stochastic physics","text":"","category":"section"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"Stochastic physics introduces stochasticity into the parameterizations of physical process. There is conceptually several classes of how this can be done","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"Stochastic perturbations of the tendencies\nStochastic parameter perturbations\nStochastic perturbations to the inputs of parameterizations","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"all of these use random numbers created from some random processes (white noise or with some autocorrelation in space or time or sampled from some other distribution) and are applied additive or multiplicative.","category":"page"},{"location":"stochastic_physics/#Stochastic-physics-implementations","page":"Stochastic physics","title":"Stochastic physics implementations","text":"","category":"section"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"Currently implemented is","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"using InteractiveUtils # hide\nusing SpeedyWeather\nsubtypes(SpeedyWeather.AbstractStochasticPhysics)","category":"page"},{"location":"stochastic_physics/#Stochastically-perturbed-parameterization-tendencies-(SPPT)","page":"Stochastic physics","title":"Stochastically perturbed parameterization tendencies (SPPT)","text":"","category":"section"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"SPPT is based on the idea that the dynamics D have a higher certainty but that the Parameterizations P are more uncertain and hence any stochasticity should scale with the size of the tendencies coming from the parameterizations. Conceptually an atmospheric state mathbfx is integrated in time t as","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"fracpartial mathbfxpartial t = D(mathbfx) + P(mathbfx t p)","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"with the parameterizations being a function of the atmospheric state (column only for the single column parameterizations in SpeedyWeather), time t and parameters p. Now SPPT changes this to","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"fracpartial mathbfxpartial t = D(mathbfx) + (1+r)P(mathbfx t p)","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"with r in -1 1 being a random value changing with time and space but not in the vertical (but you could define some tapering). The idea to constrain r in -1 1 is such that","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"the sign up the tendencies P is never reversed\nthe tendencies P are at most 2x larger after perturbation","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"So the stochasticity is multiplicative, in regions/times when the parameterizations are small, so is the perturbation, but with larger tendencies form parameterizations the perturbation is also stronger; satisfying the conceptual idea of uncertainty from the parameterizations scaling with the size of those.","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"You can use SPPT as follows. Start by defining a random_process, this controls how the random values r are created","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"using SpeedyWeather\n\nspectral_grid = SpectralGrid()\nrandom_process = SpectralAR1Process(spectral_grid)","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"and you could change the length scale via wavenumber the time_scale or set a seed for reproducibility. seed=0 (default) means that a random seed is taken from Julia's global random number generator. Now we define SPPT as","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"stochastic_physics = StochasticallyPerturbedParameterizationTendencies(spectral_grid)","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"tapering (by default an anonymous function hence it looks like var\"#269#273\" = #269 as the compiler assigns a \"name\") can be used to change vertically the amplitude of r, e.g tapering = σ -> σ < 0.8 ? 1 : 1 - (σ - 0.8)/0.2 (in Sigma coordinates) could be passed on a (keyword) arugment to reduce the SPPT perturbation towards the surface. A tapering tau(sigma) is applied like (1 + tau r), where r = r(lambda varphi t) is a function of horizontal coordinates longitude lambda, latitude varphi and time t only.","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"Now we pass these on to the model constructor and run a simulation","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"model = PrimitiveWetModel(spectral_grid; random_process, stochastic_physics)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(10))\n\n# surface humidity, kg/kg -> g/kg\nk = spectral_grid.nlayers # surface layer\nhumid = simulation.diagnostic_variables.grid.humid_grid[:, k]*1000\n\nusing CairoMakie\nheatmap(humid, title=\"Surface humidity [g/kg], with SPPT\", colormap=:oslo)\nsave(\"humid_sppt.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"(Image: Surface humidity with SPPT)","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"if we now run the same simulation again (but with a different seed for the random process, that's randomly drawn at initialize!)","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"simulation = initialize!(model)\nrun!(simulation, period=Day(10))\n\n# surface humidity, kg/kg -> g/kg\nhumid = simulation.diagnostic_variables.grid.humid_grid[:, k]*1000\n\nheatmap(humid, title=\"Surface humidity [g/kg], with SPPT, other seed\", colormap=:oslo)\nsave(\"humid_sppt2.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"stochastic_physics/","page":"Stochastic physics","title":"Stochastic physics","text":"(Image: Surface humidity with SPPT)","category":"page"},{"location":"callbacks/#Callbacks","page":"Callbacks","title":"Callbacks","text":"","category":"section"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"SpeedyWeather.jl implements a callback system to let users include a flexible piece of code into the time stepping. You can think about the main time loop calling back to check whether anything else should be done before continuing with the next time step. The callback system here is called after the time step only (plus one call at initialize! and one at finalize!), we currently do not implement other callsites.","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Callbacks are mainly introduced for diagnostic purposes, meaning that they do not influence the simulation, and access the prognostic variables and the model components in a read-only fashion. However, a callback is not strictly prevented from changing prognostic or diagnostic variables or the model. For example, you may define a callback that changes the orography during the simulation. In general, one has to keep the general order of executions during a time step in mind (valid for all models)","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"set tendencies to zero\ncompute parameterizations, forcing, or drag terms. Accumulate tendencies.\ncompute dynamics, accumulate tendencies.\ntime stepping\noutput\ncallbacks","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"This means that, at the current callsite, a callback can read the tendencies but writing into it would be overwritten by the zeroing of the tendencies in 1. anyway. At the moment, if a callback wants to implement an additional tendency then it currently should be implemented as a parameterization, forcing or drag term. ","category":"page"},{"location":"callbacks/#Defining-a-callback","page":"Callbacks","title":"Defining a callback","text":"","category":"section"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"You can (and are encouraged!) to write your own callbacks to diagnose SpeedyWeather simulations. Let us implement a StormChaser callback, recording the highest surface wind speed on every time step, that we want to use to illustrate how a callback needs to be defined.","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Every custom callback needs to be defined as a (mutable) struct, subtype of AbstractCallback, i.e. struct or mutable struct CustomCallback <: SpeedyWeather.AbstractCallback. In our case, this is","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"using SpeedyWeather\n\nBase.@kwdef mutable struct StormChaser{NF} <: SpeedyWeather.AbstractCallback\n timestep_counter::Int = 0\n maximum_surface_wind_speed::Vector{NF} = [0]\nend\n\n# Generator function\nStormChaser(SG::SpectralGrid) = StormChaser{SG.NF}()","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"We decide to have a field timestep_counter in the callback that allows us to track the number of times the callback was called to create a time series of our highest surface wind speeds. The actual maximum_surface_wind_speed is then a vector of a given type NF (= number format), which is where we'll write into. Both are initialised with zeros. We also add a generator function, similar as to many other components in SpeedyWeather that just pulls the number format from the SpectralGrid object.","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Now every callback needs to extend three methods","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"initialize!, called once before the main time loop starts\ncallback!, called after every time step\nfinalize!, called once after the last time step","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"And we'll go through them one by one.","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"function SpeedyWeather.initialize!(\n callback::StormChaser,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel,\n)\n # allocate recorder: number of time steps (incl initial conditions) in simulation \n callback.maximum_surface_wind_speed = zeros(progn.clock.n_timesteps + 1)\n \n # where surface (=lowermost model layer) u, v on the grid are stored\n u_grid = diagn.grid.u_grid[:, diagn.nlayers]\n v_grid = diagn.grid.u_grid[:, diagn.nlayers]\n\n # maximum wind speed of initial conditions\n callback.maximum_surface_wind_speed[1] = max_2norm(u_grid, v_grid)\n \n # (re)set counter to 1\n callback.timestep_counter = 1\nend","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"The initialize! function has to be extended for the new callback ::StormChaser as first argument, then followed by prognostic and diagnostic variables and model. For correct multiple dispatch it is important to restrict the first argument to the new StormChaser type (to not call another callback instead), but the other type declarations are for clarity only. initialize!(::AbstractCallback, args...) is called once just before the main time loop, meaning after the initial conditions are set and after all other components are initialized. We replace the vector inside our storm chaser with a vector of the correct length so that we have a \"recorder\" allocated, a vector that can store the maximum surface wind speed on every time step. We then also compute that maximum for the initial conditions and set the time step counter to 1. We define the max_2norm function as follows","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"\"\"\"Maximum of the 2-norm of elements across two arrays.\"\"\"\nfunction max_2norm(u::AbstractArray{T}, v::AbstractArray{T}) where T\n max_norm = zero(T) # = u² + v²\n for ij in eachindex(u, v)\n # find largest wind speed squared\n max_norm = max(max_norm, u[ij]^2 + v[ij]^2)\n end\n return sqrt(max_norm) # take sqrt only once\nend","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Note that this function is defined in the scope Main and not inside SpeedyWeather, this is absolutely possible due to Julia's scope of variables which will use max_2norm from Main scope if it doesn't exist in the global scope inside the SpeedyWeather module scope. Then we need to extend the callback! function as follows","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"function SpeedyWeather.callback!(\n callback::StormChaser,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel,\n)\n\n # increase counter\n callback.timestep_counter += 1 \n i = callback.timestep_counter\n\n # where surface (=lowermost model layer) u, v on the grid are stored\n u_grid = diagn.grid.u_grid[:, diagn.nlayers]\n v_grid = diagn.grid.u_grid[:, diagn.nlayers]\n\n # maximum wind speed at current time step\n callback.maximum_surface_wind_speed[i] = max_2norm(u_grid, v_grid)\nend","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"The function signature for callback! is the same as for initialize!. You may access anything from progn, diagn or model, although for a purely diagnostic callback this should be read-only. While you could change other model components like the land sea mask in model.land_sea_mask or orography etc. then you interfere with the simulation which is more advanced and will be discussed in Intrusive callbacks below.","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Lastly, we extend the finalize! function which is called once after the last time step. This could be used, for example, to save the maximum_surface_wind_speed vector to file or in case you want to find the highest wind speed across all time steps. But in many cases you may not need to do anything, in which case you just just let it return nothing.","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"SpeedyWeather.finalize!(::StormChaser, args...) = nothing","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"note: Always extend `initialize!`, `callback!` and `finalize!`\nFor a custom callback you need to extend all three, initialize!, callback! and finalize!, even if your callback doesn't need it. Just return nothing in that case. Otherwise a MethodError will occur. While we could have defined all callbacks by default to do nothing on each of these, this may give you the false impression that your callback is already defined correctly, although it's not.","category":"page"},{"location":"callbacks/#Adding-a-callback","page":"Callbacks","title":"Adding a callback","text":"","category":"section"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Every model has a field callbacks::Dict{Symbol, AbstractCallback} such that the callbacks keyword can be used to create a model with a dictionary of callbacks. Callbacks are identified with a Symbol key inside such a dictionary. We have a convenient CallbackDict generator function which can be used like Dict but the key-value pairs have to be of type Symbol-AbstractCallback. Let us illustrate this with the dummy callback NoCallback (which is a callback that returns nothing on initialize!, callback! and finalize!)","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"callbacks = CallbackDict() # empty dictionary\ncallbacks = CallbackDict(:my_callback => NoCallback()) # key => callback","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"If you don't provide a key a random key will be assigned","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"callbacks = CallbackDict(NoCallback())","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"and you can add (or delete) additional callbacks","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"add!(callbacks, NoCallback()) # this will also pick a random key\nadd!(callbacks, :my_callback => NoCallback()) # use key :my_callback\ndelete!(callbacks, :my_callback) # remove by key\ncallbacks","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"And you can chain them too","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"add!(callbacks, NoCallback(), NoCallback()) # random keys\nadd!(callbacks, :key1 => NoCallback(), :key2 => NoCallback()) # keys provided","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Meaning that callbacks can be added before and after model construction","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"spectral_grid = SpectralGrid()\ncallbacks = CallbackDict(:callback_added_before => NoCallback())\nmodel = PrimitiveWetModel(spectral_grid; callbacks)\nadd!(model.callbacks, :callback_added_afterwards => NoCallback())\nadd!(model, :callback_added_afterwards2 => NoCallback())","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Note how the first argument can be model.callbacks as outlined in the sections above because this is the callbacks dictionary, but also simply model, which will add the callback to model.callbacks. It's equivalent. Let us add two more meaningful callbacks","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"storm_chaser = StormChaser(spectral_grid)\nrecord_surface_temperature = GlobalSurfaceTemperatureCallback(spectral_grid)\nadd!(model.callbacks, :storm_chaser => storm_chaser)\nadd!(model.callbacks, :temperature => record_surface_temperature)","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"which means that now in the calls to callback! first the two dummy NoCallbacks are called and then our storm chaser callback and then the GlobalSurfaceTemperatureCallback which records the global mean surface temperature on every time step. From normal NetCDF output the information these callbacks analyse would not be available, only at the frequency of the model output, which for every time step would create way more data and considerably slow down the simulation. Let's run the simulation and check the callbacks","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"simulation = initialize!(model)\nrun!(simulation, period=Day(3))\nv = model.callbacks[:storm_chaser].maximum_surface_wind_speed\nmaximum(v) # highest surface wind speeds in simulation [m/s]","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Cool, our StormChaser callback with the key :storm_chaser has been recording maximum surface wind speeds in [m/s]. And the :temperature callback a time series of global mean surface temperatures in Kelvin on every time step while the model ran for 3 days.","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"model.callbacks[:temperature].temp","category":"page"},{"location":"callbacks/#intrusive_callbacks","page":"Callbacks","title":"Intrusive callbacks","text":"","category":"section"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"In the sections above, callbacks were introduced as a tool to define custom diagnostics or simulation output. This is the simpler and recommended way of using them but nothing stops you from defining a callback that is intrusive, meaning that it can alter the prognostic or diagnostic variables or the model.","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Changing any components of the model, e.g. boundary conditions like orography or the land-sea mask through a callback is possible although one should notice that this only comes into effect on the next time step given the execution order mentioned above. One could for example run a simulation for a certain period and then start moving continents around. Note that for physical consistency this should be reflected in the orography, land-sea mask, as well as in the available sea and land-surface temperatures, but one is free to do this only partially too. Another example would be to switch on/off certain model components over time. If these components are implemented as mutable struct then one could define a callback that weakens their respective strength parameter over time.","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"As an example of a callback that changes the model components see","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Millenium flood: Time-dependent land-sea mask","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Changing the diagnostic variables, however, will not have any effect. All of them are treated as work arrays, meaning that their state is completely overwritten on every time step. Changing the prognostic variables in spectral space directly is not advised though possible because this can easily lead to stability issues. It is generally easier to implement something like this as a parameterization, forcing or drag term (which can also be made time-dependent).","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Overall, callbacks give the user a wide range of possibilities to diagnose the simulation while running or to interfere with a simulation. We therefore encourage users to use callbacks as widely as possible, but if you run into any issues please open an issue in the repository and explain what you'd like to achieve and which errors you are facing. We are happy to help.","category":"page"},{"location":"callbacks/#Schedules","page":"Callbacks","title":"Schedules","text":"","category":"section"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"For convenience, SpeedyWeather.jl implements a Schedule which helps to schedule when callbacks are called. Because in many situations you don't want to call them on every time step but only periodically, say once a day, or only on specific dates and times, e.g. Jan 1 at noon. Several examples how to create schedules","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"using SpeedyWeather\n\n# execute on timestep at or after Jan 2 2000\nevent_schedule = Schedule(DateTime(2000,1,2)) \n\n# several events scheduled\nevents = (DateTime(2000,1,3), DateTime(2000,1,5,12))\nseveral_events_schedule = Schedule(events...)\n\n# provided as Vector{DateTime} with times= keyword\nalways_at_noon = [DateTime(2000,1,i,12) for i in 1:10]\nnoon_schedule = Schedule(times=always_at_noon)\n\n# or using every= for periodic execution, here once a day\nperiodic_schedule = Schedule(every=Day(1))","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"A Schedule has 5 fields, see Schedule. every is an option to create a periodic schedule to execute every time that indicated period has passed. steps and counter will let you know how many callback execution steps there are and count them up. times is a Vector{DateTime} containing scheduled events. schedule is the actual schedule inside a Schedule, implemented as BitVector indicating whether to execute on a given time step (true) or not (false).","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Let's show how to use a Schedule inside a callback","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"struct MyScheduledCallback <: SpeedyWeather.AbstractCallback\n schedule::Schedule\n # add other fields here that you need\nend\n\nfunction SpeedyWeather.initialize!(\n callback::MyScheduledCallback,\n progn::PrognosticVariables,\n args...\n)\n # when initializing a scheduled callback also initialize its schedule!\n initialize!(callback.schedule, progn.clock)\n\n # initialize other things in your callback here\nend\n\nfunction SpeedyWeather.callback!(\n callback::MyScheduledCallback,\n progn::PrognosticVariables,\n diagn::DiagnosticVariables,\n model::AbstractModel,\n)\n # scheduled callbacks start with this line to execute only when scheduled!\n # else escape immediately\n isscheduled(callback.schedule, progn.clock) || return nothing\n\n # Just print the North Pole surface temperature to screen\n (;time) = progn.clock\n temp_at_north_pole = diagn.grid.temp_grid[1,end]\n\n @info \"North pole has a temperature of $temp_at_north_pole on $time.\"\nend\n\n# nothing needs to be done after simulation is finished\nSpeedyWeather.finalize!(::MyScheduledCallback, args...) = nothing","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"So in summary","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"add a field schedule::Schedule to your callback\nadd the line initialize!(callback.schedule, progn.clock) when initializing your callback\nstart your callback! method with isscheduled(callback.schedule, progn.clock) || return nothing to execute only when scheduled","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"A Schedule is a field inside a callback as this allows you the set the callbacks desired schedule when creating it. In the example above we can create our callback that is supposed to print the North Pole's temperature like so","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"north_pole_temp_at_noon_jan9 = MyScheduledCallback(Schedule(DateTime(2000,1,9,12)))","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"The default for every is typemax(Int) indicating \"never\". This just means that there is no periodically reoccuring schedule, only schedule.times would include some times for events that are scheduled. Now let's create a primitive equation model with that callback","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"spectral_grid = SpectralGrid(trunc=31, nlayers=5)\nmodel = PrimitiveWetModel(spectral_grid)\nadd!(model.callbacks, north_pole_temp_at_noon_jan9)\n\n# start simulation 7 days earlier\nsimulation = initialize!(model, time = DateTime(2000,1,2,12))\nrun!(simulation, period=Day(10))\nnothing # hide","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"So the callback gives us the temperature at the North Pole exactly when scheduled. We could have also stored this temperature, or conditionally changed parameters inside the model. There are plenty of ways how to use the scheduling, either by event, or in contrast, we could also schedule for once a day. As illustrated in the following","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"north_pole_temp_daily = MyScheduledCallback(Schedule(every=Day(1)))\nadd!(model.callbacks, north_pole_temp_daily)\n\n# resume simulation, start time is now 2000-1-12 noon\nrun!(simulation, period=Day(5))\nnothing # hide","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Note that the previous callback is still part of the model, we haven't deleted it with delete!. But because it's scheduled for a specific time that's in the past now that we resume the simulation it's schedule is empty (which is thrown as a warning). However, our new callback, scheduled daily, is active and prints daily at noon, because the simulation start time was noon.","category":"page"},{"location":"callbacks/#Scheduling-logic","page":"Callbacks","title":"Scheduling logic","text":"","category":"section"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"An event Schedule (created with DateTime object(s)) for callbacks, executes on or after the specified times. For two consecutive time steps i, i+1, an event is scheduled at i+1 when it occurs in (ii+1. So a simulation with timestep i on Jan-1 at 1am, and i+1 at 2am, will execute a callback scheduled for 1am at i but scheduled for 1am and 1s (=01:00:01 on a 24H clock) at 2am. Because callbacks are always executed after a timestep this also means that a simulation starting at midnight with a callback scheduled for midnight will not execute this callback as it is outside of the (i i+1 range. You'd need to include this execution into the initialization. If several events inside the Schedule fall into the same time step (in the example above, 1am and 1s and 1am 30min) the execution will not happen twice. Think of a scheduled callback as a binary \"should the callback be executed now or not?\". Which is in fact how it's implemented, as a BitVector of the length of the number of time steps. If the bit at a given timestep is true, execute, otherwise not.","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"A periodic Schedule (created with every = Hour(2) or similar) will execute on the timestep after that period (here 2 hours) has passed. If a simulation starts at midnight with one hour time steps then execution would take place after the timestep from 1am to 2am because that's when the clock switches to 2am which is 2 hours after the start of the simulation. Note that therefore the initial timestep is not included, however, the last time step would be if the period is a multiple of the scheduling period. If the first timestep should be included (e.g. you want to do something with the initial conditions) then you'll need to include that into the initialization of the callback.","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Periodic schedules which do not match the simulation time step will be adjusted by rounding. Example, if you want a schedule which executes every hour but your simulation time step is 25min then it will be adjusted to execute every 2nd time step, meaning every 50min and not 1 hour. However, an info will be thrown if that is the case","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"odd_schedule = MyScheduledCallback(Schedule(every = Minute(70)))\nadd!(model.callbacks, odd_schedule)\n\n# resume simulation for 4 hours\nrun!(simulation, period=Hour(4))\nnothing # hide","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Now we get two empty schedules, one from callback that's supposed to execute on Jan 9 noon (this time has passed in our simulation) and one from the daily callback (we're not simulating for a day). You could just delete! those callbacks. You can see that while we wanted our odd_schedule to execute every 70min, it has to adjust it to every 60min to match the simulation time step of 30min.","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"After the model initialization you can always check the simulation time step from model.time_stepping ","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"model.time_stepping","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Or converted into minutes (the time step internally is at millisecond accuracy)","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"Minute(model.time_stepping.Δt_millisec)","category":"page"},{"location":"callbacks/","page":"Callbacks","title":"Callbacks","text":"which illustrates why the adjustment of our callback frequency was necessary.","category":"page"},{"location":"ringgrids/#RingGrids","page":"RingGrids","title":"RingGrids","text":"","category":"section"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"RingGrids defines and exports the following grids:","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"full grids: FullClenshawGrid, FullGaussianGrid, FullHEALPix, and FullOctaHEALPix\nreduced grids: OctahedralGaussianGrid, OctahedralClenshawGrid, OctaHEALPixGrid and HEALPixGrid","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"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":"RingGrids","title":"Creating data on a RingGrid","text":"","category":"section"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"Data in a Matrix which follows this ring order can be put on a FullGaussianGrid like so","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"using SpeedyWeather.RingGrids\nmap = randn(Float32, 8, 4)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"grid = FullGaussianGrid(map, input_as=Matrix)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"Note that input_as=Matrix is necessary as, RingGrids have a flattened horizontal dimension into a vector. To distinguish the 2nd horizontal dimension from a possible vertical dimension the keyword argument here is required.","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"grid.data","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"map == Matrix(FullGaussianGrid(map))","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"nlat_half = 4\ngrid = randn(OctahedralGaussianGrid{Float16}, nlat_half)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"Visualising RingGrid data","text":"","category":"section"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"nlat_half = 24\ngrid = randn(OctahedralGaussianGrid, nlat_half)\nRingGrids.plot(grid)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"(Note that to skip the RingGrids. in the last line you can do import SpeedyWeather.RingGrids: plot, import SpeedyWeather: plot or simply using SpeedyWeather. It's just that LowerTriangularMatrices also defines plot which otherwise causes naming conflicts.)","category":"page"},{"location":"ringgrids/#Indexing-RingGrids","page":"RingGrids","title":"Indexing RingGrids","text":"","category":"section"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"grid = randn(OctahedralClenshawGrid, 5)\nlatd = get_latd(grid)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"Now we could calculate Coriolis and add it on the grid as follows","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"for ij in eachgridpoint(grid)\n grid[ij]\nend","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"or use eachindex instead.","category":"page"},{"location":"ringgrids/#Interpolation-on-RingGrids","page":"RingGrids","title":"Interpolation on RingGrids","text":"","category":"section"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"grid = randn(OctahedralGaussianGrid{Float32}, 4)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"interpolate(FullGaussianGrid, grid)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"interpolate(FullGaussianGrid, 6, grid)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"One can also interpolate onto a given coordinate ˚N, ˚E like so","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"interpolate(30.0, 10.0, grid)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"interpolate([30.0, 40.0, 50.0], [10.0, 10.0, 10.0], grid)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"Performance for RingGrid interpolation","text":"","category":"section"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"output vector\ninput grid\ninterpolator","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"grid_in = rand(HEALPixGrid, 4)\ngrid_out = zeros(FullClenshawGrid, 6)\ninterp = RingGrids.interpolator(grid_out, grid_in)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"interpolate!(grid_out, grid_in, interp)\ngrid_out","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"grid_out = zeros(FullClenshawGrid{Float16}, 6);\ninterpolate!(grid_out, grid_in, interp)\ngrid_out","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"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":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"npoints = 10 # number of coordinates to interpolate onto\ninterp = AnvilInterpolator(Float32, HEALPixGrid, 24, npoints)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"now we can update the locator inside our interpolator as follows","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"RingGrids.update_locator!(interp, latds, londs)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"output_vec = zeros(10)\ngrid_input = rand(HEALPixGrid, 24)\nnothing # hide","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"we can use the interpolator as follows","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"interpolate!(output_vec, grid_input, interp)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"RingGrids","text":"interpolate(latds, londs, grid_input)","category":"page"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"Anvil interpolator","text":"","category":"section"},{"location":"ringgrids/","page":"RingGrids","title":"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":"RingGrids","title":"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":"RingGrids","title":"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":"RingGrids","title":"Function index","text":"","category":"section"},{"location":"ringgrids/","page":"RingGrids","title":"RingGrids","text":"Modules = [SpeedyWeather.RingGrids]","category":"page"},{"location":"ringgrids/#Core.Type-Tuple{AbstractArray}","page":"RingGrids","title":"Core.Type","text":"() Initialize an instance of the grid from an Array. For keyword argument input_as=Vector (default) the leading dimension is interpreted as a flat vector of all horizontal entries in one layer. For input_as==Matrx the first two leading dimensions are interpreted as longitute and latitude. This is only possible for full grids that are a subtype of AbstractFullGridArray.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractFullGrid","page":"RingGrids","title":"SpeedyWeather.RingGrids.AbstractFullGrid","text":"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 latitudes (also called Clenshaw from Clenshaw-Curtis quadrature), or others.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractFullGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.AbstractFullGridArray","text":"Subtype of AbstractGridArray for all N-dimensional arrays of ring grids that have the same number of longitude points on every ring. As such these (horizontal) grids are representable as a matrix, with denser grid points towards the poles.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractGrid","page":"RingGrids","title":"SpeedyWeather.RingGrids.AbstractGrid","text":"Abstract supertype for all ring grids, representing 2-dimensional data on the sphere unravelled into a Julia Vector. Subtype of AbstractGridArray with N=1 and ArrayType=Vector{T} of eltype T.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.AbstractGridArray","text":"Abstract supertype for all arrays of ring grids, representing N-dimensional data on the sphere in two dimensions (but unravelled into a vector in the first dimension, the actual \"ring grid\") plus additional N-1 dimensions for the vertical and/or time etc. Parameter T is the eltype of the underlying data, held as in the array type ArrayType (Julia's Array for CPU or others for GPU).\n\nRing grids have several consecuitive grid points share the same latitude (= a ring), grid points on a given ring are equidistant. Grid points are ordered 0 to 360˚E, starting around the north pole, ring by ring to the south pole. \n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractInterpolator","page":"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":"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.AbstractReducedGrid","page":"RingGrids","title":"SpeedyWeather.RingGrids.AbstractReducedGrid","text":"Horizontal abstract type for all AbstractReducedGridArray with N=1 (i.e. horizontal only) and ArrayType of Vector{T} with element type T.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractReducedGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.AbstractReducedGridArray","text":"Subtype of AbstractGridArray for arrays of rings grids that have a reduced number of longitude points towards the poles, i.e. they are not \"full\", see AbstractFullGridArray. Data on these grids cannot be represented as matrix and has to be unravelled into a vector, ordered 0 to 360˚E then north to south, ring by ring. Examples for reduced grids are the octahedral Gaussian or Clenshaw grids, or the HEALPix grid.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AbstractSphericalDistance","page":"RingGrids","title":"SpeedyWeather.RingGrids.AbstractSphericalDistance","text":"abstract type AbstractSphericalDistance end\n\nSuper type of formulas to calculate the spherical distance or great-circle distance. To define a NewFormula, define struct NewFormula <: AbstractSphericalDistance end and the actual calculation as a functor\n\nfunction NewFormula(lonlat1::Tuple, lonlat2::Tuple; radius=DEFAULT_RADIUS, kwargs...)\n\nassuming inputs in degrees and returning the distance in meters (or radians for radius=1). Then use the general interface spherical_distance(NewFormula, args...; kwargs...)\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.AnvilLocator","page":"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":"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.FullClenshawArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.FullClenshawArray","text":"A FullClenshawArray is an array of full grid, subtyping AbstractFullGridArray, that use equidistant latitudes for each ring (a regular lon-lat grid). These require the Clenshaw-Curtis quadrature in the spectral transform, hence the name. One ring is on the equator, total number of rings is odd, no rings on the north or south pole. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings. Fields are\n\ndata::AbstractArray\nnlat_half::Int64\nrings::Vector{UnitRange{Int64}}\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.FullClenshawGrid","page":"RingGrids","title":"SpeedyWeather.RingGrids.FullClenshawGrid","text":"A FullClenshawArray but constrained to N=1 dimensions (horizontal only) and data is a Vector{T}.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.FullGaussianArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.FullGaussianArray","text":"A FullGaussianArray is an array of full grids, subtyping AbstractFullGridArray, that use Gaussian latitudes for each ring. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings. Fields are\n\ndata::AbstractArray: Data array, west to east, ring by ring, north to south.\nnlat_half::Int64: Number of latitudes on one hemisphere\nrings::Vector{UnitRange{Int64}}: Precomputed ring indices, ranging from first to last grid point on every ring.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.FullGaussianGrid","page":"RingGrids","title":"SpeedyWeather.RingGrids.FullGaussianGrid","text":"A FullGaussianArray but constrained to N=1 dimensions (horizontal only) and data is a Vector{T}.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.FullHEALPixArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.FullHEALPixArray","text":"A FullHEALPixArray is an array of full grids, subtyping AbstractFullGridArray, that use HEALPix latitudes for each ring. This type primarily equists to interpolate data from the (reduced) HEALPixGrid onto a full grid for output.\n\nFirst dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings. Fields are\n\ndata::AbstractArray\nnlat_half::Int64\nrings::Vector{UnitRange{Int64}}\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.FullHEALPixGrid","page":"RingGrids","title":"SpeedyWeather.RingGrids.FullHEALPixGrid","text":"A FullHEALPixArray but constrained to N=1 dimensions (horizontal only) and data is a Vector{T}.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.FullOctaHEALPixArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.FullOctaHEALPixArray","text":"A FullOctaHEALPixArray is an array of full grids, subtyping AbstractFullGridArray that use OctaHEALPix latitudes for each ring. This type primarily equists to interpolate data from the (reduced) OctaHEALPixGrid onto a full grid for output.\n\nFirst dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings. Fields are\n\ndata::AbstractArray\nnlat_half::Int64\nrings::Vector{UnitRange{Int64}}\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.FullOctaHEALPixGrid","page":"RingGrids","title":"SpeedyWeather.RingGrids.FullOctaHEALPixGrid","text":"A FullOctaHEALPixArray but constrained to N=1 dimensions (horizontal only) and data is a Vector{T}.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.GridGeometry","page":"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{<:AbstractGrid}, Integer}","page":"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.HEALPixArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.HEALPixArray","text":"A HEALPixArray is an array of HEALPix grids, subtyping AbstractReducedGridArray. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) which has to be even (non-fatal error thrown otherwise) which is less strict than the original HEALPix formulation (only powers of two for nside = nlat_half/2). Ring indices are precomputed in rings.\n\nA HEALPix grid has 12 faces, each nsidexnside grid points, each covering the same area of the sphere. They start with 4 longitude points on the northern-most ring, increase by 4 points per ring in the \"polar cap\" (the top half of the 4 northern-most faces) but have a constant number of longitude points in the equatorial belt. The southern hemisphere is symmetric to the northern, mirrored around the Equator. HEALPix grids have a ring on the Equator. For more details see Górski et al. 2005, DOI:10.1086/427976. \n\nrings are the precomputed ring indices, for nlat_half = 4 it is rings = [1:4, 5:12, 13:20, 21:28, 29:36, 37:44, 45:48]. So the first ring has indices 1:4 in the unravelled first dimension, etc. For efficient looping see eachring and eachgrid. Fields are\n\ndata::AbstractArray\nnlat_half::Int64\nrings::Vector{UnitRange{Int64}}\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.HEALPixGrid","page":"RingGrids","title":"SpeedyWeather.RingGrids.HEALPixGrid","text":"An HEALPixArray but constrained to N=1 dimensions (horizontal only) and data is a Vector{T}.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.Haversine-Tuple{Tuple, Tuple}","page":"RingGrids","title":"SpeedyWeather.RingGrids.Haversine","text":"Haversine(lonlat1::Tuple, lonlat2::Tuple; radius) -> Any\n\n\nHaversine formula calculating the great-circle or spherical distance (in meters) on the sphere between two tuples of longitude-latitude points in degrees ˚E, ˚N. Use keyword argument radius to change the radius of the sphere (default 6371e3 meters, Earth's radius), use radius=1 to return the central angle in radians or radius=360/2π to return degrees.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.OctaHEALPixArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.OctaHEALPixArray","text":"An OctaHEALPixArray is an array of OctaHEALPix grids, subtyping AbstractReducedGridArray. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings.\n\nAn OctaHEALPix grid has 4 faces, each nlat_half x nlat_half in size, covering 90˚ in longitude, pole to pole. As part of the HEALPix family of grids, the grid points are equal area. They start with 4 longitude points on the northern-most ring, increase by 4 points per ring towards the Equator with one ring on the Equator before reducing the number of points again towards the south pole by 4 per ring. There is no equatorial belt for OctaHEALPix grids. The southern hemisphere is symmetric to the northern, mirrored around the Equator. OctaHEALPix grids have a ring on the Equator. For more details see Górski et al. 2005, DOI:10.1086/427976, the OctaHEALPix grid belongs to the family of HEALPix grids with Nθ = 1, Nφ = 4 but is not explicitly mentioned therein.\n\nrings are the precomputed ring indices, for nlat_half = 3 (in contrast to HEALPix this can be odd) it is rings = [1:4, 5:12, 13:24, 25:32, 33:36]. For efficient looping see eachring and eachgrid. Fields are\n\ndata::AbstractArray\nnlat_half::Int64\nrings::Vector{UnitRange{Int64}}\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.OctaHEALPixGrid","page":"RingGrids","title":"SpeedyWeather.RingGrids.OctaHEALPixGrid","text":"An OctaHEALPixArray but constrained to N=1 dimensions (horizontal only) and data is a Vector{T}.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.OctahedralClenshawArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.OctahedralClenshawArray","text":"An OctahedralClenshawArray is an array of octahedral grids, subtyping AbstractReducedGridArray, that use equidistant latitudes for each ring, the same as for FullClenshawArray. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings.\n\nThese grids are called octahedral (same as for the OctahedralGaussianArray which only uses different latitudes) because after starting with 20 points on the first ring around the north pole (default) they increase the number of longitude points for each ring by 4, such that they can be conceptually thought of as lying on the 4 faces of an octahedron on each hemisphere. Hence, these grids have 20, 24, 28, ... longitude points for ring 1, 2, 3, ... Clenshaw grids have a ring on the Equator which has 16 + 4nlat_half longitude points before reducing the number of longitude points per ring by 4 towards the southern-most ring j = nlat. rings are the precomputed ring indices, the the example above rings = [1:20, 21:44, 45:72, ...]. For efficient looping see eachring and eachgrid. Fields are\n\ndata::AbstractArray\nnlat_half::Int64\nrings::Vector{UnitRange{Int64}}\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.OctahedralClenshawGrid","page":"RingGrids","title":"SpeedyWeather.RingGrids.OctahedralClenshawGrid","text":"An OctahedralClenshawArray but constrained to N=1 dimensions (horizontal only) and data is a Vector{T}.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.OctahedralGaussianArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.OctahedralGaussianArray","text":"An OctahedralGaussianArray is an array of octahedral grids, subtyping AbstractReducedGridArray, that use Gaussian latitudes for each ring. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings.\n\nThese grids are called octahedral because after starting with 20 points on the first ring around the north pole (default) they increase the number of longitude points for each ring by 4, such that they can be conceptually thought of as lying on the 4 faces of an octahedron on each hemisphere. Hence, these grids have 20, 24, 28, ... longitude points for ring 1, 2, 3, ... There is no ring on the Equator and the two rings around it have 16 + 4nlat_half longitude points before reducing the number of longitude points per ring by 4 towards the southern-most ring j = nlat. rings are the precomputed ring indices, the the example above rings = [1:20, 21:44, 45:72, ...]. For efficient looping see eachring and eachgrid. Fields are\n\ndata::AbstractArray\nnlat_half::Int64\nrings::Vector{UnitRange{Int64}}\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.OctahedralGaussianGrid","page":"RingGrids","title":"SpeedyWeather.RingGrids.OctahedralGaussianGrid","text":"An OctahedralGaussianArray but constrained to N=1 dimensions (horizontal only) and data is a Vector{T}.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.OctaminimalGaussianArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.OctaminimalGaussianArray","text":"An OctaminimalGaussianArray is an array of octahedral grids, subtyping AbstractReducedGridArray, that use Gaussian latitudes for each ring. First dimension of the underlying N-dimensional data represents the horizontal dimension, in ring order (0 to 360˚E, then north to south), other dimensions are used for the vertical and/or time or other dimensions. The resolution parameter of the horizontal grid is nlat_half (number of latitude rings on one hemisphere, Equator included) and the ring indices are precomputed in rings.\n\nThese grids are called octahedral because after starting with 4 points on the first ring around the north pole (default) they increase the number of longitude points for each ring by 4, such that they can be conceptually thought of as lying on the 4 faces of an octahedron on each hemisphere. Hence, these grids have 4, 8, 12, ... longitude points for ring 1, 2, 3, ... which is in contrast to the OctahedralGaussianArray which starts with 20 points around the poles, hence \"minimal\". There is no ring on the Equator and the two rings around it have 4nlat_half longitude points before reducing the number of longitude points per ring by 4 towards the southern-most ring j = nlat. rings are the precomputed ring indices, in the example above rings = [1:4, 5:12, 13:24, ...]. For efficient looping see eachring and eachgrid. Fields are\n\ndata::AbstractArray\nnlat_half::Int64\nrings::Vector{UnitRange{Int64}}\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#SpeedyWeather.RingGrids.OctaminimalGaussianGrid","page":"RingGrids","title":"SpeedyWeather.RingGrids.OctaminimalGaussianGrid","text":"An OctaminimalGaussianArray but constrained to N=1 dimensions (horizontal only) and data is a Vector{T}.\n\n\n\n\n\n","category":"type"},{"location":"ringgrids/#Base.sizeof-Tuple{AbstractGridArray}","page":"RingGrids","title":"Base.sizeof","text":"sizeof(G::AbstractGridArray) -> Any\n\n\nSize of underlying data array plus precomputed ring indices.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.Matrix!-Tuple{AbstractMatrix, OctaHEALPixGrid}","page":"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":"RingGrids","title":"SpeedyWeather.RingGrids.Matrix!","text":"Matrix!(\n M::AbstractMatrix,\n G::OctahedralClenshawGrid;\n kwargs...\n) -> AbstractMatrix\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":"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":"RingGrids","title":"SpeedyWeather.RingGrids.Matrix!","text":"Matrix!(\n MGs::Tuple{AbstractArray{T, 2}, OctahedralClenshawGrid}...;\n quadrant_rotation,\n matrix_quadrant\n) -> Union{Tuple, AbstractMatrix}\n\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{T}, Tuple{AbstractGridArray{T, N, ArrayType} where {N, ArrayType<:AbstractArray{T, N}}, AbstractVector}} where T","page":"RingGrids","title":"SpeedyWeather.RingGrids._scale_lat!","text":"_scale_lat!(\n grid::AbstractGridArray{T, N, ArrayType} where {N, ArrayType<:AbstractArray{T, N}},\n v::AbstractVector\n) -> AbstractGridArray\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":"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{AbstractGrid{NF}, Vector{<:UnitRange{<:Integer}}}} where NF<:Integer","page":"RingGrids","title":"SpeedyWeather.RingGrids.average_on_poles","text":"average_on_poles(\n A::AbstractGridArray{NF<:Integer, 1, Array{NF<:Integer, 1}},\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.average_on_poles-Union{Tuple{NF}, Tuple{AbstractVector{NF}, Vector{<:UnitRange{<:Integer}}}} where NF<:AbstractFloat","page":"RingGrids","title":"SpeedyWeather.RingGrids.average_on_poles","text":"average_on_poles(\n A::AbstractArray{NF<:AbstractFloat, 1},\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.check_inputs-NTuple{4, Any}","page":"RingGrids","title":"SpeedyWeather.RingGrids.check_inputs","text":"check_inputs(data, nlat_half, rings, Grid) -> Any\n\n\nTrue for data, nlat_half and rings that all match in size to construct a grid of type Grid.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.clenshaw_curtis_weights-Tuple{Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.clenshaw_curtis_weights","text":"clenshaw_curtis_weights(nlat_half::Integer) -> Any\n\n\nThe Clenshaw-Curtis weights for a Clenshaw grid (full or octahedral) of size nlathalf. Clenshaw-Curtis weights are of length nlat, i.e. a vector for every latitude ring, pole to pole. `sum(clenshawcurtisweights(nlathalf))is always2` as int0^π sin(x) dx = 2 (colatitudes), or equivalently int-pi/2^pi/2 cos(x) dx (latitudes).\n\nIntegration (and therefore the spectral transform) is exact (only rounding errors) when using Clenshaw grids provided that nlat >= 2(T + 1), meaning that a grid resolution of at least 128x64 (nlon x nlat) is sufficient for an exact transform with a T=31 spectral truncation.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.each_index_in_ring!-Tuple{Vector{<:UnitRange{<:Integer}}, Type{<:OctahedralGaussianArray}, Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.each_index_in_ring!","text":"each_index_in_ring!(\n rings::Vector{<:UnitRange{<:Integer}},\n Grid::Type{<:OctahedralGaussianArray},\n nlat_half::Integer\n)\n\n\nprecompute a Vector{UnitRange{Int}} to index grid points on every ringj(elements of the vector) ofGridat resolutionnlat_half. Seeeachringandeachgrid` for efficient looping over grid points.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.each_index_in_ring!-Tuple{Vector{<:UnitRange{<:Integer}}, Type{<:OctaminimalGaussianArray}, Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.each_index_in_ring!","text":"each_index_in_ring!(\n rings::Vector{<:UnitRange{<:Integer}},\n Grid::Type{<:OctaminimalGaussianArray},\n nlat_half::Integer\n)\n\n\nprecompute a Vector{UnitRange{Int}} to index grid points on every ringj(elements of the vector) ofGridat resolutionnlat_half. Seeeachringandeachgrid` for efficient looping over grid points.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.each_index_in_ring-Tuple{Type{<:SpeedyWeather.RingGrids.AbstractFullGridArray}, Integer, Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.each_index_in_ring","text":"each_index_in_ring(\n Grid::Type{<:SpeedyWeather.RingGrids.AbstractFullGridArray},\n j::Integer,\n nlat_half::Integer\n) -> Any\n\n\nUnitRange for every grid point of grid Grid of resolution nlat_half on ring j (j=1 is closest ring around north pole, j=nlat around south pole).\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.each_index_in_ring-Union{Tuple{Grid}, Tuple{Grid, Integer}} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.each_index_in_ring","text":"each_index_in_ring(\n grid::AbstractGridArray,\n j::Integer\n) -> Any\n\n\nUnitRange to access data on grid grid on ring j.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.eachgrid-Tuple{AbstractGridArray}","page":"RingGrids","title":"SpeedyWeather.RingGrids.eachgrid","text":"eachgrid(grid::AbstractGridArray) -> Any\n\n\nCartesianIndices for the 2nd to last dimension of an AbstractGridArray, to be used like\n\nfor k in eachgrid(grid) for ring in eachring(grid) for ij in ring grid[ij, k]\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.eachgridpoint-Tuple{AbstractGridArray}","page":"RingGrids","title":"SpeedyWeather.RingGrids.eachgridpoint","text":"eachgridpoint(grid::AbstractGridArray) -> Base.OneTo\n\n\nUnitRange to access each horizontal grid point on grid grid. For a NxM (N horizontal grid points, M vertical layers) OneTo(N) is returned.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.eachgridpoint-Union{Tuple{Grid}, Tuple{Grid, Vararg{Grid}}} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.eachgridpoint","text":"eachgridpoint(\n grid1::AbstractGridArray,\n grids::Grid<:AbstractGridArray...\n) -> Base.OneTo\n\n\nLike eachgridpoint(::AbstractGridArray) but checks for equal size between input arguments first.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.eachring-Tuple{AbstractGridArray, Vararg{AbstractGridArray}}","page":"RingGrids","title":"SpeedyWeather.RingGrids.eachring","text":"eachring(\n grid1::AbstractGridArray,\n grids::AbstractGridArray...\n) -> Any\n\n\nSame as eachring(grid) but performs a bounds check to assess that all grids according to grids_match (non-parametric grid type, nlat_half and length).\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.eachring-Tuple{AbstractGridArray}","page":"RingGrids","title":"SpeedyWeather.RingGrids.eachring","text":"eachring(grid::AbstractGridArray) -> 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\nAccesses precomputed grid.rings.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.eachring-Tuple{Type{<:AbstractGridArray}, Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.eachring","text":"eachring(\n Grid::Type{<:AbstractGridArray},\n nlat_half::Integer\n) -> Any\n\n\nComputes the ring indices i0:i1 for start and end of every longitudinal point on a given ring j of Grid at resolution nlat_half. Used to loop over rings of a grid. These indices are also precomputed in every grid.rings.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.equal_area_weights-Tuple{Type{<:AbstractGridArray}, Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.equal_area_weights","text":"equal_area_weights(\n Grid::Type{<:AbstractGridArray},\n nlat_half::Integer\n) -> Any\n\n\nThe equal-area weights used for the HEALPix grids (original or OctaHEALPix) of size nlathalf. The weights are of length nlat, i.e. a vector for every latitude ring, pole to pole. `sum(equalareaweights(nlathalf))is always2` as int0^π sin(x) dx = 2 (colatitudes), or equivalently int-pi/2^pi/2 cos(x) dx (latitudes). Integration (and therefore the spectral transform) is not exact with these grids but errors reduce for higher resolution.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.extrema_in-Tuple{AbstractVector, Real, Real}","page":"RingGrids","title":"SpeedyWeather.RingGrids.extrema_in","text":"extrema_in(v::AbstractVector, a::Real, b::Real) -> Any\n\n\nFor every element vᵢ in v does a<=vi<=b hold?\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.full_array_type-Tuple{AbstractGridArray}","page":"RingGrids","title":"SpeedyWeather.RingGrids.full_array_type","text":"full_array_type(grid::AbstractGridArray) -> Any\n\n\nFull grid array type for grid. Always returns the N-dimensional *Array not the two-dimensional (N=1) *Grid. For reduced grids the corresponding full grid that share the same latitudes.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.full_grid_type-Tuple{AbstractGridArray}","page":"RingGrids","title":"SpeedyWeather.RingGrids.full_grid_type","text":"full_grid_type(grid::AbstractGridArray) -> Any\n\n\nFull (horizontal) grid type for grid. Always returns the two-dimensional (N=1) *Grid type. For reduced grids the corresponding full grid that share the same latitudes.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.gaussian_weights-Tuple{Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.gaussian_weights","text":"gaussian_weights(nlat_half::Integer) -> Any\n\n\nThe Gaussian weights for a Gaussian grid (full or octahedral) of size nlathalf. Gaussian weights are of length nlat, i.e. a vector for every latitude ring, pole to pole. `sum(gaussianweights(nlathalf))is always2` as int0^π sin(x) dx = 2 (colatitudes), or equivalently int_-pi/2^pi/2 cos(x) dx (latitudes).\n\nIntegration (and therefore the spectral transform) is exact (only rounding errors) when using Gaussian grids provided that nlat >= 3(T + 1)/2, meaning that a grid resolution of at least 96x48 (nlon x nlat) is sufficient for an exact transform with a T=31 spectral truncation.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_colat-Tuple{Grid} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_colat","text":"get_colat(grid::AbstractGridArray) -> Any\n\n\nColatitudes (radians) for meridional column (full grids only).\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_colatlons-Tuple{Grid} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_colatlons","text":"get_colatlons(grid::AbstractGridArray) -> Tuple{Any, Any}\n\n\nLatitudes (in radians, 0-π) and longitudes (0 - 2π) for every (horizontal) grid point in grid.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_lat-Tuple{Grid} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_lat","text":"get_lat(grid::AbstractGridArray) -> Any\n\n\nLatitude (radians) for each ring in grid, north to south.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_latd-Tuple{Grid} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_latd","text":"get_latd(grid::AbstractGridArray) -> Any\n\n\nLatitude (degrees) for each ring in grid, north to south.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_latdlonds-Tuple{Grid} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_latdlonds","text":"get_latdlonds(grid::AbstractGridArray) -> Tuple{Any, Any}\n\n\nLatitudes (in degrees, -90˚-90˚N) and longitudes (0-360˚E) for every (horizontal) grid point in grid. Ordered 0-360˚E then north to south.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_latlons-Tuple{Grid} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_latlons","text":"get_latlons(grid::AbstractGridArray) -> Tuple{Any, Any}\n\n\nLatitudes (in radians, 0-2π) and longitudes (-π/2 - π/2) for every (horizontal) grid point in grid.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_lon-Tuple{Grid} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_lon","text":"get_lon(grid::AbstractGridArray) -> Any\n\n\nLongitude (radians) for meridional column (full grids only).\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_lond-Tuple{Grid} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_lond","text":"get_lond(grid::AbstractGridArray) -> Any\n\n\nLongitude (degrees) for meridional column (full grids only).\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_nlat-Tuple{Grid} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_nlat","text":"get_nlat(grid::AbstractGridArray) -> Any\n\n\nGet number of latitude rings, pole to pole.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_nlat_half-Tuple{AbstractGridArray}","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_nlat_half","text":"get_nlat_half(grid::AbstractGridArray) -> Any\n\n\nResolution paraemeters nlat_half of a grid. Number of latitude rings on one hemisphere, Equator included.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_nlon_per_ring-Tuple{AbstractGridArray, Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_nlon_per_ring","text":"get_nlon_per_ring(\n grid::AbstractGridArray,\n j::Integer\n) -> Any\n\n\nNumber of longitude points per latitude ring j.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_nlon_per_ring-Tuple{Type{<:HEALPixArray}, Integer, Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_nlon_per_ring","text":"get_nlon_per_ring(\n Grid::Type{<:HEALPixArray},\n nlat_half::Integer,\n j::Integer\n) -> Any\n\n\nNumber of longitude points for ring j on Grid of resolution nlat_half.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_nlons-Tuple{Type{<:AbstractGridArray}, Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_nlons","text":"get_nlons(\n Grid::Type{<:AbstractGridArray},\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 keyword argument both_hemispheres=false only the northern hemisphere (incl Equator) is returned.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_npoints-Tuple{Grid} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_npoints","text":"get_npoints(grid::AbstractGridArray) -> Any\n\n\nTotal number of grid points in all dimensions of grid. Equivalent to length of the underlying data array.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_npoints2D-Tuple{Grid} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_npoints2D","text":"get_npoints2D(grid::AbstractGridArray) -> Any\n\n\nNumber of grid points in the horizontal dimension only, even if grid is N-dimensional.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_vertices-Tuple{Type{<:AbstractGridArray}, Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_vertices","text":"get_vertices(\n Grid::Type{<:AbstractGridArray},\n nlat_half::Integer\n) -> NTuple{4, Any}\n\n\nVertices are defined for every grid point on a ring grid through 4 points: east, south, west, north.\n\n- east: longitude mid-point with the next grid point east\n- south: longitude mid-point between the two closest grid points on one ring to the south\n- west: longitude mid-point with the next grid point west\n- north: longitude mid-point between the two closest grid points on one ring to the north\n\nExample\n\n o ----- n ------ o\n\no --- w --- c --- e --- o\n\n o ----- s ------ o\n\nwith cell center c (the grid point), e, s, w, n the vertices and o the surrounding grid points. Returns 2xnpoints arrays for east, south, west, north each containing the longitude and latitude of the vertices.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.get_vertices-Tuple{Type{<:SpeedyWeather.RingGrids.AbstractFullGridArray}, Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.get_vertices","text":"get_vertices(\n Grid::Type{<:SpeedyWeather.RingGrids.AbstractFullGridArray},\n nlat_half::Integer\n) -> NTuple{4, Any}\n\n\nVertices for full grids, other definition than for reduced grids to prevent a diamond shape of the cells. Use default rectangular instead.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.grid_cell_average!-Tuple{AbstractGrid, SpeedyWeather.RingGrids.AbstractFullGrid}","page":"RingGrids","title":"SpeedyWeather.RingGrids.grid_cell_average!","text":"grid_cell_average!(\n output::AbstractGrid,\n input::SpeedyWeather.RingGrids.AbstractFullGrid\n) -> 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{<:AbstractGrid}, Integer, SpeedyWeather.RingGrids.AbstractFullGrid}","page":"RingGrids","title":"SpeedyWeather.RingGrids.grid_cell_average","text":"grid_cell_average(\n Grid::Type{<: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.grids_match-Tuple{AbstractGridArray, AbstractGridArray}","page":"RingGrids","title":"SpeedyWeather.RingGrids.grids_match","text":"grids_match(\n A::AbstractGridArray,\n B::AbstractGridArray;\n horizontal_only,\n vertical_only\n) -> Any\n\n\nTrue if both A and B are of the same nonparametric grid type (e.g. OctahedralGaussianArray, regardless type parameter T or underyling array type ArrayType) and of same resolution (nlat_half) and total grid points (length). Sizes of (4,) and (4,1) would match for example, but (8,1) and (4,2) would not (nlat_half not identical).\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.grids_match-Tuple{AbstractGridArray, Vararg{AbstractGridArray}}","page":"RingGrids","title":"SpeedyWeather.RingGrids.grids_match","text":"grids_match(\n A::AbstractGridArray,\n B::AbstractGridArray...;\n kwargs...\n) -> Any\n\n\nTrue if all grids A, B, C, ... provided as arguments match according to grids_match wrt to A (and therefore all).\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.horizontal_grid_type-Tuple{AbstractGridArray}","page":"RingGrids","title":"SpeedyWeather.RingGrids.horizontal_grid_type","text":"horizontal_grid_type(grid::AbstractGridArray) -> Any\n\n\nThe two-dimensional (N=1) *Grid for grid, which can be an N-dimensional *GridArray.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.isdecreasing-Tuple{AbstractVector}","page":"RingGrids","title":"SpeedyWeather.RingGrids.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":"ringgrids/#SpeedyWeather.RingGrids.isincreasing-Tuple{AbstractVector}","page":"RingGrids","title":"SpeedyWeather.RingGrids.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":"ringgrids/#SpeedyWeather.RingGrids.matrix_size-Tuple{Grid} where Grid<:AbstractGridArray","page":"RingGrids","title":"SpeedyWeather.RingGrids.matrix_size","text":"matrix_size(grid::AbstractGridArray) -> Tuple{Int64, Int64}\n\n\nSize of the matrix of the horizontal grid if representable as such (not all grids).\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.nlat_odd-Tuple{AbstractGridArray}","page":"RingGrids","title":"SpeedyWeather.RingGrids.nlat_odd","text":"nlat_odd(grid::AbstractGridArray) -> Any\n\n\nTrue for a grid that has an odd number of latitude rings nlat (both hemispheres).\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.nonparametric_type-Tuple{AbstractGridArray}","page":"RingGrids","title":"SpeedyWeather.RingGrids.nonparametric_type","text":"nonparametric_type(grid::AbstractGridArray) -> Any\n\n\nFor any instance of AbstractGridArray type its n-dimensional type (*Grid{T, N, ...} returns *Array) but without any parameters {T, N, ArrayType}\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.npoints_added_per_ring-Tuple{Type{<:OctahedralGaussianArray}}","page":"RingGrids","title":"SpeedyWeather.RingGrids.npoints_added_per_ring","text":"npoints_added_per_ring(\n _::Type{<:OctahedralGaussianArray}\n) -> Int64\n\n\n[EVEN MORE EXPERIMENTAL] number of longitude points added (removed) for every ring towards the Equator (on the southern hemisphere towards the south pole).\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.npoints_added_per_ring-Tuple{Type{<:OctaminimalGaussianArray}}","page":"RingGrids","title":"SpeedyWeather.RingGrids.npoints_added_per_ring","text":"npoints_added_per_ring(\n _::Type{<:OctaminimalGaussianArray}\n) -> Int64\n\n\n[EVEN MORE EXPERIMENTAL] number of longitude points added (removed) for every ring towards the Equator (on the southern hemisphere towards the south pole).\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.npoints_pole-Tuple{Type{<:OctahedralGaussianArray}}","page":"RingGrids","title":"SpeedyWeather.RingGrids.npoints_pole","text":"npoints_pole(_::Type{<:OctahedralGaussianArray}) -> Int64\n\n\n[EXPERIMENTAL] additional number of longitude points on the first and last ring. Change to 0 to start with 4 points on the first ring.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.npoints_pole-Tuple{Type{<:OctaminimalGaussianArray}}","page":"RingGrids","title":"SpeedyWeather.RingGrids.npoints_pole","text":"npoints_pole(_::Type{<:OctaminimalGaussianArray}) -> Int64\n\n\n[EXPERIMENTAL] additional number of longitude points on the first and last ring. Change to 0 to start with 4 points on the first ring.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.nside_healpix-Tuple{Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.nside_healpix","text":"nside_healpix(nlat_half::Integer) -> Any\n\n\nThe original Nside resolution parameter of the HEALPix grids. The number of grid points on one side of each (square) face. While we use nlat_half across all ring grids, this function translates this to Nside. Even nlat_half only.\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.rotate_matrix_indices_180-Tuple{Integer, Integer, Integer}","page":"RingGrids","title":"SpeedyWeather.RingGrids.rotate_matrix_indices_180","text":"rotate_matrix_indices_180(\n i::Integer,\n j::Integer,\n s::Integer\n) -> Tuple{Any, Any}\n\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":"RingGrids","title":"SpeedyWeather.RingGrids.rotate_matrix_indices_270","text":"rotate_matrix_indices_270(\n i::Integer,\n j::Integer,\n s::Integer\n) -> Tuple{Integer, Any}\n\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":"RingGrids","title":"SpeedyWeather.RingGrids.rotate_matrix_indices_90","text":"rotate_matrix_indices_90(\n i::Integer,\n j::Integer,\n s::Integer\n) -> Tuple{Any, Integer}\n\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.spherical_distance-Tuple{Type{<:SpeedyWeather.RingGrids.AbstractSphericalDistance}, Vararg{Any}}","page":"RingGrids","title":"SpeedyWeather.RingGrids.spherical_distance","text":"spherical_distance(\n Formula::Type{<:SpeedyWeather.RingGrids.AbstractSphericalDistance},\n args...;\n kwargs...\n) -> Any\n\n\nSpherical distance, or great-circle distance, between two points lonlat1 and lonlat2 using the Formula (default Haversine).\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.whichring-Tuple{Integer, Vector{UnitRange{Int64}}}","page":"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 rings describing rind indices as obtained from eachring(::Grid)\n\n\n\n\n\n","category":"method"},{"location":"ringgrids/#SpeedyWeather.RingGrids.zonal_mean-Tuple{AbstractGridArray}","page":"RingGrids","title":"SpeedyWeather.RingGrids.zonal_mean","text":"zonal_mean(grid::AbstractGridArray) -> Any\n\n\nZonal mean of grid, i.e. along its latitude rings.\n\n\n\n\n\n","category":"method"},{"location":"structure/#Tree-structure","page":"Tree structure","title":"Tree structure","text":"","category":"section"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"At the top of SpeedyWeather's type tree sits the Simulation, containing variables and model, which in itself contains model components with their own fields and so on. (Note that we are talking about the structure of structs within structs not the type hierarchy as defined by subtyping abstract types.) This can quickly get complicated with a lot of nested structs. The following is to give users a better overview of how simulation, variables and model are structured within SpeedyWeather. Many types in SpeedyWeather have extended Julia's show function to give you an overview of its contents, e.g. a clock::Clock is printed as","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"using SpeedyWeather\nclock = Clock()","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"illustrating the fields within a clock, their types and (unless they are an array) also their values. For structs within structs however, this information, would not be printed by default. You could use Julia's autocomplete like clock. by hitting tab after the . to inspect the fields of an instance but that would require you to manually go down every branch of that tree. To better visualise this, we have defined a tree(S) function for any instance S defined in SpeedyWeather which will print every field, and also its containing fields if they are also defined within SpeedyWeather. The \"if defined in SpeedyWeather\" is important because otherwise the tree would also show you the contents of a complex number or other types defined in Julia Base itself that we aren't interested in here. But let's start at the top.","category":"page"},{"location":"structure/#Simulation","page":"Tree structure","title":"Simulation","text":"","category":"section"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"When creating a Simulation, its fields are","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"spectral_grid = SpectralGrid(nlayers = 1)\nmodel = BarotropicModel(spectral_grid)\nsimulation = initialize!(model)","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"the prognostic_variables, the diagnostic_variables and the model (that we just initialized). We could now do tree(simulation) but that gets very lengthy and so will split things into tree(simulation.prognostic_variables), tree(simulation.diagnostic_variables) and tree(simulation.model) for more digestible chunks. You can also provide the with_types=true keyword to get also the types of these fields printed, but we'll skip that here.","category":"page"},{"location":"structure/#Prognostic-variables","page":"Tree structure","title":"Prognostic variables","text":"","category":"section"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"The prognostic variables struct is parametric on the model type, model_type(model) (which strips away its parameters), but this is only to dispatch over it. The fields are for all models the same, just the barotropic model would not use temperature for example (but you could use nevertheless). ","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"tree(simulation.prognostic_variables)","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"The prognostic variable struct can be mutated (e.g. to set new initial conditions) with the SpeedyWeather.set! function. ","category":"page"},{"location":"structure/#Diagnostic-variables","page":"Tree structure","title":"Diagnostic variables","text":"","category":"section"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"Similar for the diagnostic variables, regardless the model type, they contain the same fields but for the 2D models many will not be used for example.","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"tree(simulation.diagnostic_variables)","category":"page"},{"location":"structure/#BarotropicModel","page":"Tree structure","title":"BarotropicModel","text":"","category":"section"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"The BarotropicModel is the simplest model we have, which will not have many of the model components that are needed to define the primitive equations for example. Note that forcing or drag aren't further branched which is because the default BarotropicModel has NoForcing and NoDrag which don't have any fields. If you create a model with non-default conponents they will show up here. tree dynamicallt inspects the current contents of a (mutable) struct and that tree may look different depending on what model you have constructed!","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"model = BarotropicModel(spectral_grid)\ntree(model)","category":"page"},{"location":"structure/#ShallowWaterModel","page":"Tree structure","title":"ShallowWaterModel","text":"","category":"section"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"The ShallowWaterModel is similar to the BarotropicModel, but it contains for example orography, that the BarotropicModel doesn't have.","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"model = ShallowWaterModel(spectral_grid)\ntree(model)","category":"page"},{"location":"structure/#PrimitiveDryModel","page":"Tree structure","title":"PrimitiveDryModel","text":"","category":"section"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"The PrimitiveDryModel is a big jump in complexity compared to the 2D models, but because it doesn't contain humidity, several model components like evaporation aren't needed.","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"spectral_grid = SpectralGrid()\nmodel = PrimitiveDryModel(spectral_grid)\ntree(model)","category":"page"},{"location":"structure/#PrimitiveWetModel","page":"Tree structure","title":"PrimitiveWetModel","text":"","category":"section"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"The PrimitiveWetModel is the most complex model we currently have, hence its field tree is the longest, defining many components for the physics parameterizations.","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"model = PrimitiveWetModel(spectral_grid)\ntree(model)","category":"page"},{"location":"structure/#Size-of-a-Simulation","page":"Tree structure","title":"Size of a Simulation","text":"","category":"section"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"The tree function also allows for the with_size::Bool keyword (default false), which will also print the size of the respective branches to give you an idea of how much memory a SpeedyWeather simulation uses.","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"tree(simulation, max_level=1, with_size=true)","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"And with max_level you can truncate the tree to go down at most that many levels. 1MB is a typical size for a one-level T31 resolution simulation. In comparison, a higher resolution PrimitiveWetModel would use","category":"page"},{"location":"structure/","page":"Tree structure","title":"Tree structure","text":"spectral_grid = SpectralGrid(trunc=127, nlayers=8)\nmodel = PrimitiveWetModel(spectral_grid)\nsimulation = initialize!(model)\ntree(simulation, max_level=1, with_size=true)","category":"page"},{"location":"examples_3D/#Examples-3D","page":"Examples 3D","title":"Examples 3D","text":"","category":"section"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"The following showcases several examples of SpeedyWeather.jl simulating the Primitive equations with and without humidity and with and without physical parameterizations.","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"See also Examples 2D for examples with the Barotropic vorticity equation and the shallow water model.","category":"page"},{"location":"examples_3D/#Jablonowski-Williamson-baroclinic-wave","page":"Examples 3D","title":"Jablonowski-Williamson baroclinic wave","text":"","category":"section"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=31, nlayers=8, Grid=FullGaussianGrid, dealiasing=3)\n\norography = ZonalRidge(spectral_grid)\ninitial_conditions = InitialConditions(\n vordiv = ZonalWind(),\n temp = JablonowskiTemperature(),\n pres = ZeroInitially())\n\nmodel = PrimitiveDryModel(spectral_grid; orography, initial_conditions, physics=false)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(9))\nnothing # hide","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","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 ZonalWind for vorticity and divergence (curl and divergence of u v), JablonowskiTemperature for temperature and ZeroInitially for pressure. 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":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"using CairoMakie\n\nvor = simulation.diagnostic_variables.grid.vor_grid[:, end]\nheatmap(vor, title=\"Surface relative vorticity\")\nsave(\"jablonowski.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"(Image: Jablonowski pyplot)","category":"page"},{"location":"examples_3D/#Held-Suarez-forcing","page":"Examples 3D","title":"Held-Suarez forcing","text":"","category":"section"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"using SpeedyWeather\nspectral_grid = SpectralGrid(trunc=31, nlayers=8)\n\n# construct model with only Held-Suarez forcing, no other physics\nmodel = PrimitiveDryModel(\n spectral_grid,\n\n # Held-Suarez forcing and drag\n temperature_relaxation = HeldSuarez(spectral_grid),\n boundary_layer_drag = LinearDrag(spectral_grid),\n\n # switch off other physics\n convection = NoConvection(),\n shortwave_radiation = NoShortwave(),\n longwave_radiation = NoLongwave(),\n vertical_diffusion = NoVerticalDiffusion(),\n\n # switch off surface fluxes (makes ocean/land/land-sea mask redundant)\n surface_wind = NoSurfaceWind(),\n surface_heat_flux = NoSurfaceHeatFlux(),\n\n # use Earth's orography\n orography = EarthOrography(spectral_grid)\n)\n\nsimulation = initialize!(model)\nrun!(simulation, period=Day(20))\nnothing # hide","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"The code above defines the Held-Suarez forcing [HS94] in terms of temperature relaxation and a linear drag term that is applied near the planetary boundary but switches off all other physics in the primitive equation model without humidity. Switching off the surface wind would also automatically turn off the surface evaporation (not relevant in the primitive dry model) and sensible heat flux as that one is proportional to the surface wind (which is zero with NoSurfaceWind). But to also avoid the calculation being run at all we use NoSurfaceHeatFlux() for the model constructor. Many of the NoSomething model components do not require the spectral grid to be passed on, but as a convention we allow every model component to have it for construction even if not required.","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"Visualising surface temperature with","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"using CairoMakie\n\ntemp = simulation.diagnostic_variables.grid.temp_grid[:, end]\nheatmap(temp, title=\"Surface temperature [K]\", colormap=:thermal)\n\nsave(\"heldsuarez.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"(Image: Held-Suarez)","category":"page"},{"location":"examples_3D/#Aquaplanet","page":"Examples 3D","title":"Aquaplanet","text":"","category":"section"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"using SpeedyWeather\n\n# components\nspectral_grid = SpectralGrid(trunc=31, nlayers=8)\nocean = AquaPlanet(spectral_grid, temp_equator=302, temp_poles=273)\nland_sea_mask = AquaPlanetMask(spectral_grid)\norography = NoOrography(spectral_grid)\n\n# create model, initialize, run\nmodel = PrimitiveWetModel(spectral_grid; ocean, land_sea_mask, orography)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(20))\nnothing # hide","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"Here we have defined an aquaplanet simulation by","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"creating an ocean::AquaPlanet. This will use constant sea surface temperatures that only vary with latitude.\ncreating a land_sea_mask::AquaPlanetMask this will use a land-sea mask with false=ocean everywhere.\ncreating an orography::NoOrography which will have no orography and zero surface geopotential.","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"All passed on to the model constructor for a PrimitiveWetModel, we have now a model with humidity and physics parameterization as they are defined by default (typing model will give you an overview of its components). We could have change the model.land and model.vegetation components too, but given the land-sea masks masks those contributions to the surface fluxes anyway, this is not necessary. Note that neither sea surface temperature, land-sea mask or orography have to agree. It is possible to have an ocean on top of a mountain. For an ocean grid-cell that is (partially) masked by the land-sea mask, its value will be (fractionally) ignored in the calculation of surface fluxes (potentially leading to a zero flux depending on land surface temperatures).","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"Now with the following we visualize the surface humidity after the 50 days of simulation. We use 50 days as without mountains it takes longer for the initial conditions to become unstable. The surface humidity shows small-scale patches in the tropics, which is a result of the convection scheme, causing updrafts and downdrafts in both humidity and temperature.","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"using CairoMakie\n\nhumid = simulation.diagnostic_variables.grid.humid_grid[:, end]\nheatmap(humid, title=\"Surface specific humidity [kg/kg]\", colormap=:oslo)\n\nsave(\"aquaplanet.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"(Image: Aquaplanet)","category":"page"},{"location":"examples_3D/#Aquaplanet-without-(deep)-convection","page":"Examples 3D","title":"Aquaplanet without (deep) convection","text":"","category":"section"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"Now we want to compare the previous simulation to a simulation without deep convection, called DryBettsMiller, because it is the Betts-Miller convection but with humidity set to zero in which case the convection is always non-precipitating shallow (because the missing latent heat release from condensation makes it shallower) convection. In fact, this convection is the default when using the PrimitiveDryModel. Instead of redefining the Aquaplanet setup again, we simply reuse these components spectral_grid, ocean, land_sea_mask and orography (because spectral_grid hasn't changed this is possible).","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"# Execute the code from Aquaplanet above first!\nconvection = DryBettsMiller(spectral_grid, time_scale=Hour(4))\n\n# reuse other model components from before\nmodel = PrimitiveWetModel(spectral_grid; ocean, land_sea_mask, orography, convection)\n\nsimulation = initialize!(model)\nrun!(simulation, period=Day(20))\n\nhumid = simulation.diagnostic_variables.grid.humid_grid[:, end]\nheatmap(humid, title=\"No deep convection: Surface specific humidity [kg/kg]\", colormap=:oslo)\nsave(\"aquaplanet_nodeepconvection.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"But we also want to compare this to a setup where convection is completely disabled, i.e. convection = NoConvection() (many of the No model components don't require the spectral_grid to be passed on, but some do!)","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"# Execute the code from Aquaplanet above first!\nconvection = NoConvection(spectral_grid)\n\n# reuse other model components from before\nmodel = PrimitiveWetModel(spectral_grid; ocean, land_sea_mask, orography, convection)\n\nsimulation = initialize!(model)\nrun!(simulation, period=Day(20))\n\nhumid = simulation.diagnostic_variables.grid.humid_grid[:, end]\nheatmap(humid, title=\"No convection: Surface specific humidity [kg/kg]\", colormap=:oslo)\nsave(\"aquaplanet_noconvection.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"And the comparison looks like","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"(Image: Aquaplanet, no deep convection) (Image: Aquaplanet, no convection)","category":"page"},{"location":"examples_3D/#Large-scale-vs-convective-precipitation","page":"Examples 3D","title":"Large-scale vs convective precipitation","text":"","category":"section"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"using SpeedyWeather\n\n# components\nspectral_grid = SpectralGrid(trunc=31, nlayers=8)\nlarge_scale_condensation = ImplicitCondensation(spectral_grid)\nconvection = SimplifiedBettsMiller(spectral_grid)\n\n# create model, initialize, run\nmodel = PrimitiveWetModel(spectral_grid; large_scale_condensation, convection)\nsimulation = initialize!(model)\nrun!(simulation, period=Day(10))\nnothing # hide","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"We run the default PrimitiveWetModel with ImplicitCondensation as large-scale condensation (see Implicit large-scale condensation) and the SimplifiedBettsMiller for convection (see Simplified Betts-Miller). These schemes have some additional parameters, we leave them as default for now, but you could do ImplicitCondensation(spectral_grid, relative_humidity_threshold = 0.8) to let it rain at 80% instead of 100% relative humidity. We now want to analyse the precipitation that comes from these parameterizations","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"using CairoMakie\n\n(; precip_large_scale, precip_convection) = simulation.diagnostic_variables.physics\nm2mm = 1000 # convert from [m] to [mm]\nheatmap(m2mm*precip_large_scale, title=\"Large-scale precipiation [mm]: Accumulated over 10 days\", colormap=:dense)\nsave(\"large-scale_precipitation_acc.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"(Image: Large-scale precipitation)","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"Precipitation (both large-scale and convective) are written into the simulation.diagnostic_variables.physics which, however, accumulate all precipitation during simulation. In the NetCDF output, precipitation rate (in mm/hr) is calculated from accumulated precipitation as a post-processing step. More interactively, you can also reset these accumulators and integrate for another 6 hours to get the precipitation only in that period.","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"# reset accumulators and simulate 6 hours\nsimulation.diagnostic_variables.physics.precip_large_scale .= 0\nsimulation.diagnostic_variables.physics.precip_convection .= 0\nrun!(simulation, period=Hour(6))\n\n# visualise, precip_* arrays are flat copies, no need to read them out again!\nm2mm_hr = (1000*Hour(1)/Hour(6)) # convert from [m] to [mm/hr]\nheatmap(m2mm_hr*precip_large_scale, title=\"Large-scale precipiation [mm/hr]\", colormap=:dense)\nsave(\"large-scale_precipitation.png\", ans) # hide\nheatmap(m2mm_hr*precip_convection, title=\"Convective precipiation [mm/hr]\", colormap=:dense)\nsave(\"convective_precipitation.png\", ans) # hide\nnothing # hide","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"(Image: Large-scale precipitation) (Image: Convective precipitation)","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"As the precipitation fields are accumulated meters over the integration period we divide by 6 hours to get a precipitation rate ms but then multiply with 1 hour and 1000 to get the typical precipitation unit of mmhr.","category":"page"},{"location":"examples_3D/#References","page":"Examples 3D","title":"References","text":"","category":"section"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","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. DOI:10.1256/qj.06.12","category":"page"},{"location":"examples_3D/","page":"Examples 3D","title":"Examples 3D","text":"[HS94]: Held, I. M. & Suarez, M. J. A Proposal for the Intercomparison of the Dynamical Cores of Atmospheric General Circulation Models. Bulletin of the American Meteorological Society 75, 1825-1830 (1994). DOI:10.1175/1520-0477(1994)075<1825:APFTIO>2.0.CO;2","category":"page"},{"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: Convection, clouds, precipitation, radiation, 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":"#Vision","page":"Home","title":"Vision","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Why another model? You may ask. We believe that most currently available are stiff, difficult to use and extend, and therefore slow down research whereas a modern code in a modern language wouldn't have to. We decided to use Julia because it combines the best of Fortran and Python: Within a single language we can interactively run SpeedyWeather but also extend it, inspect its components, evaluate individual terms of the equations, and analyse and visualise output on the fly.","category":"page"},{"location":"","page":"Home","title":"Home","text":"We do not aim to make SpeedyWeather an atmospheric model similar to the production-ready models used in weather forecasting, at least not at the cost of our current level of interactivity and ease of use or extensibility. If someone wants to implement a cloud parameterization that is very complicated and expensive to run then they are more than encouraged to do so, but it will probably live in its own repository and we are happy to provide a general interface to do so. But SpeedyWeather's defaults should be balanced: Physically accurate yet general; as independently as possible from other components and parameter choices; not too complicated to implement and understand; and computationally cheap. Finding a good balance is difficult but we try our best. ","category":"page"},{"location":"#Developers-and-contributing","page":"Home","title":"Developers and contributing","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":"(Apologies if you've recently started contributing but this isn't reflected here yet, create a pull request!) Any contributions are always welcome!","category":"page"},{"location":"","page":"Home","title":"Home","text":"Open-source lives from large teams of (even occasional) contributors. If you are interested to fix something, implement something, or just use it and provide feedback you are always welcome. We are more than happy to guide you, especially when you don't know where to start. We can point you to the respective code, highlight how everything is connected and tell you about dos and don'ts. Just express your interest to contribute and we'll be happy to have you.","category":"page"},{"location":"#Citing","page":"Home","title":"Citing","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"If you use SpeedyWeather.jl in research, teaching, or other activities, we would be grateful if you could mention SpeedyWeather.jl and cite our paper in JOSS:","category":"page"},{"location":"","page":"Home","title":"Home","text":"Klöwer et al., (2024). SpeedyWeather.jl: Reinventing atmospheric general circulation models towards interactivity and extensibility. Journal of Open Source Software, 9(98), 6323, doi:10.21105/joss.06323.","category":"page"},{"location":"","page":"Home","title":"Home","text":"The bibtex entry for the paper is:","category":"page"},{"location":"","page":"Home","title":"Home","text":"@article{SpeedyWeatherJOSS,\n doi = {10.21105/joss.06323},\n url = {https://doi.org/10.21105/joss.06323},\n year = {2024},\n publisher = {The Open Journal},\n volume = {9},\n number = {98},\n pages = {6323},\n author = {Milan Klöwer and Maximilian Gelbrecht and Daisuke Hotta and Justin Willmert and Simone Silvestri and Gregory L. Wagner and Alistair White and Sam Hatfield and Tom Kimpson and Navid C. Constantinou and Chris Hill},\n title = {{SpeedyWeather.jl: Reinventing atmospheric general circulation models towards interactivity and extensibility}},\n journal = {Journal of Open Source Software}\n}","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. From 2022-2024 this project was also funded by the National Science Foundation NSF. Since 2024, the main funding is from Schmidt Sciences LLC through MK's Eric & Wendy Schmidt AI in Science Fellowship.","category":"page"},{"location":"extensions/#Extending-SpeedyWeather","page":"Extensions","title":"Extending SpeedyWeather","text":"","category":"section"},{"location":"extensions/","page":"Extensions","title":"Extensions","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":"extensions/","page":"Extensions","title":"Extensions","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":"extensions/#logic","page":"Extensions","title":"SpeedyWeather's modular logic","text":"","category":"section"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"Almost every component in SpeedyWeather is implemented in three steps:","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"Define a new type,\ndefine its initialization,\nextend a function that defines what it does.","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","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. For the latter, see Parameterizations 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":"extensions/","page":"Extensions","title":"Extensions","text":"To define a new forcing type, at the most basic level you would do","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"using SpeedyWeather\n\nstruct MyForcing{NF} <: SpeedyWeather.AbstractForcing\n # define some parameters and work arrays here\n a::NF\n v::Vector{NF}\nend","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","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 (see Abstract model components) 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 @kwdef and can also be parametric with respect to the number format NF 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 (ideally not work arrays, see discussion in Use ColumnVariables work arrays). 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 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 and drag.","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"To define the new type's initialization, at the most basic level you need to extend the initialize! function for this new type. A dummy example:","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"function initialize!(forcing::MyForcing, model::AbstractModel)\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":"extensions/","page":"Extensions","title":"Extensions","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":"extensions/","page":"Extensions","title":"Extensions","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":"extensions/","page":"Extensions","title":"Extensions","text":"function forcing!(\n diagn::DiagnosticVariables,\n progn::PrognosticVariables,\n forcing::MyForcing,\n model::AbstractModel,\n lf::Integer,\n)\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 = forcing.a\n diagn.tendencies.v_tend_grid = forcing.a\n diagn.tendencies.vor_tend = forcing.a\nend","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"DiagnosticVariables 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 can make use of anything else in model, but often we unpack the model in a function barrier (which can help with type inference and therefore performance). 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":"extensions/#Scope-of-variables","page":"Extensions","title":"Scope of variables","text":"","category":"section"},{"location":"extensions/","page":"Extensions","title":"Extensions","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":"extensions/","page":"Extensions","title":"Extensions","text":"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":"extensions/","page":"Extensions","title":"Extensions","text":"function SpeedyWeather.initialize!(forcing::MyForcing, model::SpeedyWeather.AbstractModel)\n # how to initialize it\nend","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"And similar for SpeedyWeather.forcing!.","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","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: AbstractModel to bring a given variable into global scope which removes the necessity to write SpeedyWeather.AbstractModel.","category":"page"},{"location":"extensions/#Abstract-model-components","page":"Extensions","title":"Abstract model components","text":"","category":"section"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"You may wonder which abstract model components there are, you can always check this with","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"using InteractiveUtils # hide\nsubtypes(SpeedyWeather.AbstractModelComponent)","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"we illustrated the modular logic here using AbstractForcing and AbstractDrag is very similar. However, other model components also largely follow SpeedyWeather's modular logic as for example outlined in Defining a callback or Defining a new orography type. If you do not find much documentation about a new custom type where you would like extend SpeedyWeather's functionality it is probably because we have not experimented much with this either. But that does not mean it is not possible. Just reach out by creating an issue in this case.","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"Similarly, AbstractParameterization has several subtypes that define conceptual classes of parameterizations, namely","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"subtypes(SpeedyWeather.AbstractParameterization)","category":"page"},{"location":"extensions/","page":"Extensions","title":"Extensions","text":"but these are discussed in more detail in Parameterizations. For a more concrete example of how to define a new forcing for the 2D models, see Custom forcing and drag.","category":"page"}] } diff --git a/previews/PR645/shallowwater/index.html b/previews/PR645/shallowwater/index.html index c24a9181b..cb0ab9745 100644 --- a/previews/PR645/shallowwater/index.html +++ b/previews/PR645/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/PR645/spectral_transform/index.html b/previews/PR645/spectral_transform/index.html index 6d3a6fd7b..0c43906a3 100644 --- a/previews/PR645/spectral_transform/index.html +++ b/previews/PR645/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/PR645/speedytransforms/index.html b/previews/PR645/speedytransforms/index.html index 4ea9636d9..5e8a2c77d 100644 --- a/previews/PR645/speedytransforms/index.html +++ b/previews/PR645/speedytransforms/index.html @@ -227,39 +227,39 @@ 1.7854887 0.9347485 0.53324044 - 0.28122818

Functions and type index

SpeedyWeather.SpeedyTransforms.AbstractLegendreShortcutType

Legendre shortcut is the truncation of the loop over the order m of the spherical harmonics in the Legendre transform. For reduced grids with as few as 4 longitudes around the poles (HEALPix grids) or 20 (octahedral grids) the higher wavenumbers in large orders m do not project (significantly) onto such few longitudes. For performance reasons the loop over m can therefore but truncated early. A Legendre shortcut <: AbstractLegendreShortcut is implemented as a functor that returns the 0-based maximum order m to retain per latitude ring, i.e. to be used for m in 0:mmax_truncation.

New shortcuts can be added by defining struct LegendreShortcutNew <: AbstractLegendreShortcut end and the functor method LegendreShortcutNew(nlon::Integer, lat::Real) = ..., with nlon the number of longitude points on that ring, and latd its latitude in degrees (-90˚ to 90˚N). Many implementations may not use the latitude latd but it is included for compatibility. If unused set to default value to 0. Also define short_name(::Type{<:LegendreShortcutNew}) = "new".

Implementions are LegendreShortcutLinear, LegendreShortcutQuadratic, LegendreShortcutCubic, LegendreShortcutLinQuadCosLat² and LegendreShortcutLinCubCoslat.

source
SpeedyWeather.SpeedyTransforms.AssociatedLegendrePolArrayType
AssociatedLegendrePolArray{T, N, M, V} <: AbstractArray{T,N}

Type that wraps around a LowerTriangularArray{T,M,V} but is a subtype of AbstractArray{T,M+1}. This enables easier use with AssociatedLegendrePolynomials.jl which otherwise couldn't use the "matrix-style" (l, m) indexing of LowerTriangularArray. This type however doesn't support any other operations than indexing and is purerly intended for internal purposes.

  • data::LowerTriangularArray{T, M, V} where {T, M, V}
source
SpeedyWeather.SpeedyTransforms.LegendreShortcutCubicType
LegendreShortcutCubic(nlon::Integer) -> Any
+ 0.28122818

Functions and type index

SpeedyWeather.SpeedyTransforms.AbstractLegendreShortcutType

Legendre shortcut is the truncation of the loop over the order m of the spherical harmonics in the Legendre transform. For reduced grids with as few as 4 longitudes around the poles (HEALPix grids) or 20 (octahedral grids) the higher wavenumbers in large orders m do not project (significantly) onto such few longitudes. For performance reasons the loop over m can therefore but truncated early. A Legendre shortcut <: AbstractLegendreShortcut is implemented as a functor that returns the 0-based maximum order m to retain per latitude ring, i.e. to be used for m in 0:mmax_truncation.

New shortcuts can be added by defining struct LegendreShortcutNew <: AbstractLegendreShortcut end and the functor method LegendreShortcutNew(nlon::Integer, lat::Real) = ..., with nlon the number of longitude points on that ring, and latd its latitude in degrees (-90˚ to 90˚N). Many implementations may not use the latitude latd but it is included for compatibility. If unused set to default value to 0. Also define short_name(::Type{<:LegendreShortcutNew}) = "new".

Implementions are LegendreShortcutLinear, LegendreShortcutQuadratic, LegendreShortcutCubic, LegendreShortcutLinQuadCosLat² and LegendreShortcutLinCubCoslat.

source
SpeedyWeather.SpeedyTransforms.AssociatedLegendrePolArrayType
AssociatedLegendrePolArray{T, N, M, V} <: AbstractArray{T,N}

Type that wraps around a LowerTriangularArray{T,M,V} but is a subtype of AbstractArray{T,M+1}. This enables easier use with AssociatedLegendrePolynomials.jl which otherwise couldn't use the "matrix-style" (l, m) indexing of LowerTriangularArray. This type however doesn't support any other operations than indexing and is purerly intended for internal purposes.

  • data::LowerTriangularArray{T, M, V} where {T, M, V}
source
SpeedyWeather.SpeedyTransforms.SpectralTransformType

SpectralTransform struct that contains all parameters and precomputed arrays to perform a spectral transform. Fields are

  • Grid::Type{<:AbstractGridArray}

  • nlat_half::Int64

  • nlayers::Int64

  • lmax::Int64

  • mmax::Int64

  • nfreq_max::Int64

  • LegendreShortcut::Type{<:SpeedyWeather.SpeedyTransforms.AbstractLegendreShortcut}

  • mmax_truncation::Vector{Int64}

  • nlon_max::Int64

  • nlons::Vector{Int64}

  • nlat::Int64

  • coslat::Any

  • coslat⁻¹::Any

  • lon_offsets::Any

  • norm_sphere::Any

  • rfft_plans::Vector{AbstractFFTs.Plan}

  • brfft_plans::Vector{AbstractFFTs.Plan}

  • rfft_plans_1D::Vector{AbstractFFTs.Plan}

  • brfft_plans_1D::Vector{AbstractFFTs.Plan}

  • legendre_polynomials::Any

  • scratch_memory_north::Any

  • scratch_memory_south::Any

  • scratch_memory_grid::Any

  • scratch_memory_spec::Any

  • scratch_memory_column_north::Any

  • scratch_memory_column_south::Any

  • jm_index_size::Int64

  • kjm_indices::Any

  • solid_angles::Any

  • grad_y1::Any

  • grad_y2::Any

  • grad_y_vordiv1::Any

  • grad_y_vordiv2::Any

  • vordiv_to_uv_x::Any

  • vordiv_to_uv1::Any

  • vordiv_to_uv2::Any

  • eigenvalues::Any

  • eigenvalues⁻¹::Any

source
SpeedyWeather.SpeedyTransforms.SpectralTransformType

SpectralTransform struct that contains all parameters and precomputed arrays to perform a spectral transform. Fields are

  • Grid::Type{<:AbstractGridArray}

  • nlat_half::Int64

  • nlayers::Int64

  • lmax::Int64

  • mmax::Int64

  • nfreq_max::Int64

  • LegendreShortcut::Type{<:SpeedyWeather.SpeedyTransforms.AbstractLegendreShortcut}

  • mmax_truncation::Vector{Int64}

  • nlon_max::Int64

  • nlons::Vector{Int64}

  • nlat::Int64

  • coslat::Any

  • coslat⁻¹::Any

  • lon_offsets::Any

  • norm_sphere::Any

  • rfft_plans::Vector{AbstractFFTs.Plan}

  • brfft_plans::Vector{AbstractFFTs.Plan}

  • rfft_plans_1D::Vector{AbstractFFTs.Plan}

  • brfft_plans_1D::Vector{AbstractFFTs.Plan}

  • legendre_polynomials::Any

  • scratch_memory_north::Any

  • scratch_memory_south::Any

  • scratch_memory_grid::Any

  • scratch_memory_spec::Any

  • scratch_memory_column_north::Any

  • scratch_memory_column_south::Any

  • jm_index_size::Int64

  • kjm_indices::Any

  • solid_angles::Any

  • grad_y1::Any

  • grad_y2::Any

  • grad_y_vordiv1::Any

  • grad_y_vordiv2::Any

  • vordiv_to_uv_x::Any

  • vordiv_to_uv1::Any

  • vordiv_to_uv2::Any

  • eigenvalues::Any

  • eigenvalues⁻¹::Any

source
SpeedyWeather.SpeedyTransforms.SpectralTransformMethod
SpectralTransform(
     grids::AbstractGridArray{NF, N, ArrayType};
     trunc,
     dealiasing,
     one_more_degree,
     kwargs...
 ) -> SpectralTransform{NF, _A, _B, _C, _D, _E, LowerTriangularArray{NF1, 1, _A1}, LowerTriangularArray{NF2, 2, _A2}} where {NF, _A, _B, _C, _D, _E, NF1, _A1, NF2, _A2}
-

Generator function for a SpectralTransform struct based on the size and grid type of grids. Use keyword arugments trunc, dealiasing (ignored if trunc is used) or one_more_degree to define the spectral truncation.

source
SpeedyWeather.SpeedyTransforms.SpectralTransformMethod
SpectralTransform(
+

Generator function for a SpectralTransform struct based on the size and grid type of grids. Use keyword arugments trunc, dealiasing (ignored if trunc is used) or one_more_degree to define the spectral truncation.

source
SpeedyWeather.SpeedyTransforms.SpectralTransformMethod
SpectralTransform(
     grids::AbstractGridArray{NF1, N, ArrayType1},
     specs::LowerTriangularArray{NF2, N, ArrayType2};
     kwargs...
 ) -> SpectralTransform{NF, _A, _B, _C, _D, _E, LowerTriangularArray{NF1, 1, _A1}, LowerTriangularArray{NF2, 2, _A2}} where {NF, _A, _B, _C, _D, _E, NF1, _A1, NF2, _A2}
-

Generator function for a SpectralTransform struct to transform between grids and specs.

source
SpeedyWeather.SpeedyTransforms.SpectralTransformMethod
SpectralTransform(
     specs::LowerTriangularArray{NF, N, ArrayType};
     nlat_half,
     dealiasing,
     kwargs...
 ) -> SpectralTransform{NF, _A, _B, _C, _D, _E, LowerTriangularArray{NF1, 1, _A1}, LowerTriangularArray{NF2, 2, _A2}} where {NF, _A, _B, _C, _D, _E, NF1, _A1, NF2, _A2}
-

Generator function for a SpectralTransform struct based on the size of the spectral coefficients specs. Use keyword arguments nlat_half, Grid or deliasing (if nlat_half not provided) to define the grid.

source
SpeedyWeather.SpeedyTransforms.SpectralTransformMethod
SpectralTransform(
+

Generator function for a SpectralTransform struct based on the size of the spectral coefficients specs. Use keyword arguments nlat_half, Grid or deliasing (if nlat_half not provided) to define the grid.

source
SpeedyWeather.SpeedyTransforms.SpectralTransformMethod
SpectralTransform(
     ::Type{NF},
     lmax::Integer,
     mmax::Integer,
@@ -269,16 +269,16 @@
     nlayers,
     LegendreShortcut
 ) -> SpectralTransform{T, Array, Vector{T1}, Array{Complex{T2}, 1}, Array{Complex{T3}, 2}, Array{Complex{T4}, 3}, LowerTriangularArray{T5, 1, Vector{T6}}, LowerTriangularArray{T7, 2, Matrix{T8}}} where {T, T1, T2, T3, T4, T5, T6, T7, T8}
-

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 and quadrature weights.

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 and quadrature weights.

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::LowerTriangularArray,
     V::LowerTriangularArray,
     vor::LowerTriangularArray,
     S::SpectralTransform;
     radius
 ) -> Tuple{LowerTriangularArray, LowerTriangularArray}
-

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. Acts on the unit sphere, i.e. it omits any radius scaling as all inplace gradient operators, unless the radius keyword argument is provided.

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. Acts on the unit sphere, i.e. it omits any radius scaling as all inplace gradient operators, unless the radius keyword argument is provided.

source
SpeedyWeather.SpeedyTransforms.UV_from_vordiv!Method
UV_from_vordiv!(
     U::LowerTriangularArray,
     V::LowerTriangularArray,
     vor::LowerTriangularArray,
@@ -286,7 +286,7 @@
     S::SpectralTransform;
     radius
 ) -> Tuple{LowerTriangularArray, LowerTriangularArray}
-

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. Acts on the unit sphere, i.e. it omits any radius scaling as all inplace gradient operators.

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. Acts on the unit sphere, i.e. it omits any radius scaling as all inplace gradient operators.

source
SpeedyWeather.SpeedyTransforms._divergence!Method
_divergence!(
     kernel,
     div::LowerTriangularArray,
     u::LowerTriangularArray,
@@ -294,44 +294,44 @@
     S::SpectralTransform;
     radius
 ) -> LowerTriangularArray
-

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. Acts on the unit sphere, i.e. it omits 1/radius scaling as all gradient operators, unless the radius keyword argument is provided.

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

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. Acts on the unit sphere, i.e. it omits 1/radius scaling as all gradient operators, unless the radius keyword argument is provided.

source
SpeedyWeather.SpeedyTransforms._fourier_batched!Method
_fourier_batched!(
     f_north::AbstractArray{<:Complex, 3},
     f_south::AbstractArray{<:Complex, 3},
     grids::AbstractGridArray,
     S::SpectralTransform
 )
-

(Forward) Fast Fourier transform (grid to spectral) in zonal direction of grids, stored in scratch memories f_north, f_south to be passed on to the Legendre transform. Batched version that requires the number of vertical layers to be the same as precomputed in S. Not to be called directly, use transform! instead.

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

(Forward) Fast Fourier transform (grid to spectral) in zonal direction of grids, stored in scratch memories f_north, f_south to be passed on to the Legendre transform. Batched version that requires the number of vertical layers to be the same as precomputed in S. Not to be called directly, use transform! instead.

source
SpeedyWeather.SpeedyTransforms._fourier_batched!Method
_fourier_batched!(
     grids::AbstractGridArray,
     g_north::AbstractArray{<:Complex, 3},
     g_south::AbstractArray{<:Complex, 3},
     S::SpectralTransform
 )
-

Inverse fast Fourier transform (spectral to grid) of Legendre-transformed inputs g_north and g_south to be stored in grids. Not to be called directly, use transform! instead.

source
SpeedyWeather.SpeedyTransforms._fourier_serial!Method
_fourier_serial!(
     f_north::AbstractArray{<:Complex, 3},
     f_south::AbstractArray{<:Complex, 3},
     grids::AbstractGridArray,
     S::SpectralTransform
 )
-

(Forward) Fast Fourier transform (grid to spectral) in zonal direction of grids, stored in scratch memories f_north, f_south to be passed on to the Legendre transform. Serial version that does not require the number of vertical layers to be the same as precomputed in S. Not to be called directly, use transform! instead.

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

(Forward) Fast Fourier transform (grid to spectral) in zonal direction of grids, stored in scratch memories f_north, f_south to be passed on to the Legendre transform. Serial version that does not require the number of vertical layers to be the same as precomputed in S. Not to be called directly, use transform! instead.

source
SpeedyWeather.SpeedyTransforms._fourier_serial!Method
_fourier_serial!(
     grids::AbstractGridArray,
     g_north::AbstractArray{<:Complex, 3},
     g_south::AbstractArray{<:Complex, 3},
     S::SpectralTransform
 )
-

(Inverse) Fast Fourier transform (spectral to grid) of Legendre-transformed inputs g_north and g_south to be stored in grids. Serial version that does not require the number of vertical layers to be the same as precomputed in S. Not to be called directly, use transform! instead.

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

(Inverse) Fast Fourier transform (spectral to grid) of Legendre-transformed inputs g_north and g_south to be stored in grids. Serial version that does not require the number of vertical layers to be the same as precomputed in S. Not to be called directly, use transform! instead.

source
SpeedyWeather.SpeedyTransforms._legendre!Method
_legendre!(
     g_north::AbstractArray{<:Complex, 3},
     g_south::AbstractArray{<:Complex, 3},
     specs::LowerTriangularArray,
     S::SpectralTransform;
     unscale_coslat
 )
-

Inverse Legendre transform, batched in the vertical. Not to be used directly, but called from transform!.

source
SpeedyWeather.SpeedyTransforms._legendre!Method
_legendre!(
     specs::LowerTriangularArray,
     f_north::AbstractArray{<:Complex, 3},
     f_south::AbstractArray{<:Complex, 3},
     S::SpectralTransform
 )
-

(Forward) Legendre transform, batched in the vertical. Not to be used directly, but called from transform!.

source
SpeedyWeather.SpeedyTransforms.curl!Method
curl!(
     curl::LowerTriangularArray,
     u::LowerTriangularArray,
     v::LowerTriangularArray,
@@ -340,7 +340,7 @@
     add,
     kwargs...
 ) -> LowerTriangularArray
-

Curl of a vector u, v written into curl, curl = ∇×(u, v). u, v are expected to have a 1/coslat-scaling included, otherwise curl is scaled. Acts on the unit sphere, i.e. it omits 1/radius scaling as all gradient operators unless the radius keyword argument is provided. 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, otherwise curl is scaled. Acts on the unit sphere, i.e. it omits 1/radius scaling as all gradient operators unless the radius keyword argument is provided. 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::LowerTriangularArray,
     v::LowerTriangularArray;
     kwargs...
@@ -350,12 +350,12 @@
 u = transform(u_grid)
 v = transform(v_grid)
 vor = curl(u, v, radius=6.371e6)
-vor_grid = transform(div)
source
SpeedyWeather.SpeedyTransforms.curlMethod
curl(
     u::AbstractGridArray,
     v::AbstractGridArray;
     kwargs...
 ) -> Any
-

Curl (∇×) of two vector components u, v on a grid. Applies 1/coslat scaling, transforms to spectral space and returns the spectral curl. Acts on the unit sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided.

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. Acts on the unit sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided.

source
SpeedyWeather.SpeedyTransforms.divergence!Method
divergence!(
     div::LowerTriangularArray,
     u::LowerTriangularArray,
     v::LowerTriangularArray,
@@ -364,7 +364,7 @@
     add,
     kwargs...
 ) -> LowerTriangularArray
-

Divergence of a vector u, v written into div, div = ∇⋅(u, v). u, v are expected to have a 1/coslat-scaling included, otherwise div is scaled. Acts on the unit sphere, i.e. it omits 1/radius scaling as all gradient operators, unless the radius keyword argument is provided. 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, otherwise div is scaled. Acts on the unit sphere, i.e. it omits 1/radius scaling as all gradient operators, unless the radius keyword argument is provided. 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::LowerTriangularArray,
     v::LowerTriangularArray;
     kwargs...
@@ -374,27 +374,27 @@
 u = transform(u_grid, one_more_degree=true)
 v = transform(v_grid, one_more_degree=true)
 div = divergence(u, v, radius = 6.371e6)
-div_grid = transform(div)
source
SpeedyWeather.SpeedyTransforms.divergenceMethod
divergence(
     u::AbstractGridArray,
     v::AbstractGridArray;
     kwargs...
 ) -> Any
-

Divergence (∇⋅) of two vector components u, v on a grid. Applies 1/coslat scaling, transforms to spectral space and returns the spectral divergence. Acts on the unit sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided.

source
SpeedyWeather.SpeedyTransforms.get_recursion_factorsMethod
get_recursion_factors(
+

Divergence (∇⋅) of two vector components u, v on a grid. Applies 1/coslat scaling, transforms to spectral space and returns the spectral divergence. Acts on the unit sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided.

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.ismatchingMethod
ismatching(
+

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.ismatchingMethod
ismatching(
     S::SpectralTransform,
     grid::AbstractGridArray
 ) -> Any
-

Spectral transform S and grid match if the resolution nlat_half and the type of the grid match and the number of vertical layers is equal or larger in the transform (constraints due to allocated scratch memory size).

source
SpeedyWeather.SpeedyTransforms.ismatchingMethod
ismatching(
+

Spectral transform S and grid match if the resolution nlat_half and the type of the grid match and the number of vertical layers is equal or larger in the transform (constraints due to allocated scratch memory size).

source
SpeedyWeather.SpeedyTransforms.ismatchingMethod
ismatching(
     S::SpectralTransform,
     L::LowerTriangularArray
 ) -> Any
-

Spectral transform S and lower triangular matrix L match if the spectral dimensions (lmax, mmax) match and the number of vertical layers is equal or larger in the transform (constraints due to allocated scratch memory size).

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

Spectral transform S and lower triangular matrix L match if the spectral dimensions (lmax, mmax) match and the number of vertical layers is equal or larger in the transform (constraints due to allocated scratch memory size).

source
SpeedyWeather.SpeedyTransforms.plan_FFTs!Method
plan_FFTs!(
     rfft_plans::Vector{AbstractFFTs.Plan},
     brfft_plans::Vector{AbstractFFTs.Plan},
     rfft_plans_1D::Vector{AbstractFFTs.Plan},
@@ -404,96 +404,96 @@
     rings::AbstractArray,
     nlons::Vector{<:Int64}
 ) -> NTuple{4, Vector{AbstractFFTs.Plan}}
-

Util function to generate FFT plans based on the array type of the fake Grid data provided. Uses views, which is less allocate-y than indexing but breaks when using CuArrays (see CUDA extension for alternative implementation for CuArrays).

source
SpeedyWeather.SpeedyTransforms.power_spectrumMethod
power_spectrum(spec::LowerTriangularArray; normalize) -> Any
-

Compute the power spectrum of the spherical harmonic coefficients spec (lower triangular matrix/array) of type Complex{NF}. For any additional dimensions in spec, the power spectrum is computed along the first/spherical harmonic dimension.

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_interpolationMethod
spectral_interpolation(
+

Util function to generate FFT plans based on the array type of the fake Grid data provided. Uses views, which is less allocate-y than indexing but breaks when using CuArrays (see CUDA extension for alternative implementation for CuArrays).

source
SpeedyWeather.SpeedyTransforms.power_spectrumMethod
power_spectrum(spec::LowerTriangularArray; normalize) -> Any
+

Compute the power spectrum of the spherical harmonic coefficients spec (lower triangular matrix/array) of type Complex{NF}. For any additional dimensions in spec, the power spectrum is computed along the first/spherical harmonic dimension.

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_interpolationMethod
spectral_interpolation(
     _::Type{NF},
     alms::LowerTriangularArray{T, N, ArrayType},
     ltrunc::Integer,
     mtrunc::Integer
 ) -> Any
-

Returns a LowerTriangularArray that is interpolated from alms to the size (ltrunc+1) x (mtrunc+1), both inputs are 0-based, by padding zeros for higher wavenumbers. If ltrunc or mtrunc are smaller than the corresponding size ofalms than spectral_truncation is automatically called instead, returning a smaller LowerTriangularArray.

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

Returns a LowerTriangularArray that is interpolated from alms to the size (ltrunc+1) x (mtrunc+1), both inputs are 0-based, by padding zeros for higher wavenumbers. If ltrunc or mtrunc are smaller than the corresponding size ofalms than spectral_truncation is automatically called instead, returning a smaller LowerTriangularArray.

source
SpeedyWeather.SpeedyTransforms.spectral_smoothing!Method
spectral_smoothing!(
     L::LowerTriangularArray,
     c::Real;
     power,
     truncation
 )
-

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
spectral_smoothing(
+

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
spectral_smoothing(
     A::LowerTriangularArray,
     c::Real;
     power
 ) -> Any
-

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!(
+

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::LowerTriangularArray,
     ltrunc::Integer,
     mtrunc::Integer
 ) -> LowerTriangularArray
-

Triangular truncation to degree ltrunc and order mtrunc (both 0-based). 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.

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

Triangular truncation to degree ltrunc and order mtrunc (both 0-based). 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.

source
SpeedyWeather.SpeedyTransforms.spectral_truncationMethod
spectral_truncation(
     _::Type{NF},
     alms::LowerTriangularArray{T, N, ArrayType},
     ltrunc::Integer,
     mtrunc::Integer
 ) -> Any
-

Returns a LowerTriangularArray that is truncated from alms to the size (ltrunc+1) x (mtrunc+1), both inputs are 0-based. If ltrunc or mtrunc is larger than the corresponding size ofalms than spectral_interpolation is automatically called instead, returning a LowerTriangularArray padded zero coefficients for higher wavenumbers.

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

Returns a LowerTriangularArray that is truncated from alms to the size (ltrunc+1) x (mtrunc+1), both inputs are 0-based. If ltrunc or mtrunc is larger than the corresponding size ofalms than spectral_interpolation is automatically called instead, returning a LowerTriangularArray padded zero coefficients for higher wavenumbers.

source
SpeedyWeather.SpeedyTransforms.transform!Method
transform!(
     grids::AbstractGridArray,
     specs::LowerTriangularArray,
     S::SpectralTransform;
     unscale_coslat
 ) -> AbstractGridArray
-

Spectral transform (spectral to grid space) from n-dimensional array specs of spherical harmonic coefficients to an n-dimensional array grids of ring grids. Uses FFT in the zonal direction, and a Legendre Transform in the meridional direction exploiting symmetries. The spectral transform is number format-flexible but grids and the spectral transform S have to have the same number format. Uses the precalculated arrays, FFT plans and other constants in the SpectralTransform struct S. The spectral transform is grid-flexible as long as the typeof(grids)<:AbstractGridArray and S.Grid matches.

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

Spectral transform (spectral to grid space) from n-dimensional array specs of spherical harmonic coefficients to an n-dimensional array grids of ring grids. Uses FFT in the zonal direction, and a Legendre Transform in the meridional direction exploiting symmetries. The spectral transform is number format-flexible but grids and the spectral transform S have to have the same number format. Uses the precalculated arrays, FFT plans and other constants in the SpectralTransform struct S. The spectral transform is grid-flexible as long as the typeof(grids)<:AbstractGridArray and S.Grid matches.

source
SpeedyWeather.SpeedyTransforms.transform!Method
transform!(
     specs::LowerTriangularArray,
     grids::AbstractGridArray,
     S::SpectralTransform
 ) -> LowerTriangularArray
-

Spectral transform (grid to spectral space) from n-dimensional array of grids to an n-dimensional array specs of spherical harmonic coefficients. Uses FFT in the zonal direction, and a Legendre Transform in the meridional direction exploiting symmetries. The spectral transform is number format-flexible but grids and the spectral transform S have to have the same number format. Uses the precalculated arrays, FFT plans and other constants in the SpectralTransform struct S. The spectral transform is grid-flexible as long as the typeof(grids)<:AbstractGridArray and S.Grid matches.

source
SpeedyWeather.SpeedyTransforms.transformMethod
transform(grids::AbstractGridArray; kwargs...) -> Any
-

Spectral transform (grid to spectral space) from grids to a newly allocated LowerTriangularArray. Based on the size of grids and the keyword dealiasing the spectral resolution trunc is retrieved. SpectralTransform struct S is allocated to execute transform(grids, S).

source
SpeedyWeather.SpeedyTransforms.transformMethod
transform(
+

Spectral transform (grid to spectral space) from n-dimensional array of grids to an n-dimensional array specs of spherical harmonic coefficients. Uses FFT in the zonal direction, and a Legendre Transform in the meridional direction exploiting symmetries. The spectral transform is number format-flexible but grids and the spectral transform S have to have the same number format. Uses the precalculated arrays, FFT plans and other constants in the SpectralTransform struct S. The spectral transform is grid-flexible as long as the typeof(grids)<:AbstractGridArray and S.Grid matches.

source
SpeedyWeather.SpeedyTransforms.transformMethod
transform(grids::AbstractGridArray; kwargs...) -> Any
+

Spectral transform (grid to spectral space) from grids to a newly allocated LowerTriangularArray. Based on the size of grids and the keyword dealiasing the spectral resolution trunc is retrieved. SpectralTransform struct S is allocated to execute transform(grids, S).

source
SpeedyWeather.SpeedyTransforms.transformMethod
transform(
     specs::LowerTriangularArray;
     unscale_coslat,
     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 transform(alms, S).

source
SpeedyWeather.SpeedyTransforms.transformMethod
transform(
+

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 transform(alms, S).

source
SpeedyWeather.SpeedyTransforms.transformMethod
transform(
     grids::AbstractGridArray,
     S::SpectralTransform{NF}
 ) -> Any
-

Spherical harmonic transform from grids to a newly allocated specs::LowerTriangularArray using the precomputed spectral transform S.

source
SpeedyWeather.SpeedyTransforms.transformMethod
transform(
     specs::LowerTriangularArray,
     S::SpectralTransform{NF};
     kwargs...
 ) -> Any
-

Spherical harmonic transform from specs to a newly allocated grids::AbstractGridArray using the precomputed spectral transform S.

source
SpeedyWeather.SpeedyTransforms.∇!Method
∇!(
     dpdx::LowerTriangularArray,
     dpdy::LowerTriangularArray,
     p::LowerTriangularArray,
     S::SpectralTransform;
     radius
 ) -> Tuple{LowerTriangularArray, LowerTriangularArray}
-

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 1/radius scaling unless radius keyword argument is provided.

source
SpeedyWeather.SpeedyTransforms.∇Method
∇(
+

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 1/radius scaling unless radius keyword argument is provided.

source
SpeedyWeather.SpeedyTransforms.∇Method
∇(
     grid::AbstractGridArray,
     S::SpectralTransform;
     kwargs...
 ) -> Tuple{Any, Any}
-

The zonal and meridional gradient of grid. Transform to spectral space, takes the gradient and unscales the 1/coslat scaling in the gradient. Acts on the unit-sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided. Makes use of an existing spectral transform S.

source
SpeedyWeather.SpeedyTransforms.∇Method
∇(grid::AbstractGridArray; kwargs...) -> Tuple{Any, Any}
-

The zonal and meridional gradient of grid. Transform to spectral space, takes the gradient and unscales the 1/coslat scaling in the gradient. Acts on the unit-sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided.

source
SpeedyWeather.SpeedyTransforms.∇Method
∇(
+

The zonal and meridional gradient of grid. Transform to spectral space, takes the gradient and unscales the 1/coslat scaling in the gradient. Acts on the unit-sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided. Makes use of an existing spectral transform S.

source
SpeedyWeather.SpeedyTransforms.∇Method
∇(grid::AbstractGridArray; kwargs...) -> Tuple{Any, Any}
+

The zonal and meridional gradient of grid. Transform to spectral space, takes the gradient and unscales the 1/coslat scaling in the gradient. Acts on the unit-sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided.

source
SpeedyWeather.SpeedyTransforms.∇Method
∇(
     p::LowerTriangularArray,
     S::SpectralTransform;
     kwargs...
 ) -> Tuple{Any, Any}
-

The zonal and meridional gradient of p using an existing SpectralTransform S. Acts on the unit sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided.

source
SpeedyWeather.SpeedyTransforms.∇Method
∇(p::LowerTriangularArray; kwargs...) -> Tuple{Any, Any}
-

The zonal and meridional gradient of p. Precomputes a SpectralTransform S. Acts on the unit-sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided.

source
SpeedyWeather.SpeedyTransforms.∇²!Method
∇²!(
+

The zonal and meridional gradient of p using an existing SpectralTransform S. Acts on the unit sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided.

source
SpeedyWeather.SpeedyTransforms.∇Method
∇(p::LowerTriangularArray; kwargs...) -> Tuple{Any, Any}
+

The zonal and meridional gradient of p. Precomputes a SpectralTransform S. Acts on the unit-sphere, i.e. it omits 1/radius scaling unless radius keyword argument is provided.

source
SpeedyWeather.SpeedyTransforms.∇²!Method
∇²!(
     ∇²alms::LowerTriangularArray,
     alms::LowerTriangularArray,
     S::SpectralTransform;
@@ -502,13 +502,13 @@
     inverse,
     radius
 ) -> LowerTriangularArray
-

Laplace operator ∇² applied to the spectral coefficients alms in spherical coordinates. The eigenvalues which are precomputed in S. ∇²! is the in-place version which directly stores the output in the first argument ∇²alms. Acts on the unit sphere, i.e. it omits any radius scaling as all inplace gradient operators, unless the radius keyword argument is provided.

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
∇²(
+

Laplace operator ∇² applied to the spectral coefficients alms in spherical coordinates. The eigenvalues which are precomputed in S. ∇²! is the in-place version which directly stores the output in the first argument ∇²alms. Acts on the unit sphere, i.e. it omits any radius scaling as all inplace gradient operators, unless the radius keyword argument is provided.

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::LowerTriangularArray,
     S::SpectralTransform;
     kwargs...
 ) -> Any
-

Laplace operator ∇² applied to input alms, using precomputed eigenvalues from S. Acts on the unit sphere, i.e. it omits 1/radius^2 scaling unless radius keyword argument is provided.

source
SpeedyWeather.SpeedyTransforms.∇²Method
∇²(alms::LowerTriangularArray; kwargs...) -> Any
-

Returns the Laplace operator ∇² applied to input alms. Acts on the unit sphere, i.e. it omits 1/radius^2 scaling unless radius keyword argument is provided.

source
SpeedyWeather.SpeedyTransforms.∇⁻²!Method
∇⁻²!(
+

Laplace operator ∇² applied to input alms, using precomputed eigenvalues from S. Acts on the unit sphere, i.e. it omits 1/radius^2 scaling unless radius keyword argument is provided.

source
SpeedyWeather.SpeedyTransforms.∇²Method
∇²(alms::LowerTriangularArray; kwargs...) -> Any
+

Returns the Laplace operator ∇² applied to input alms. Acts on the unit sphere, i.e. it omits 1/radius^2 scaling unless radius keyword argument is provided.

source
SpeedyWeather.SpeedyTransforms.∇⁻²!Method
∇⁻²!(
     ∇⁻²alms::LowerTriangularArray,
     alms::LowerTriangularArray,
     S::SpectralTransform;
@@ -516,10 +516,10 @@
     flipsign,
     kwargs...
 ) -> LowerTriangularArray
-

Calls ∇²!(∇⁻²alms, alms, S; add, flipsign, inverse=true).

source
SpeedyWeather.SpeedyTransforms.∇⁻²Method
∇⁻²(
     ∇²alms::LowerTriangularArray,
     S::SpectralTransform;
     kwargs...
 ) -> Any
-

InverseLaplace operator ∇⁻² applied to input alms, using precomputed eigenvalues from S. Acts on the unit sphere, i.e. it omits radius^2 scaling unless radius keyword argument is provided.

source
SpeedyWeather.SpeedyTransforms.∇⁻²Method
∇⁻²(∇²alms::LowerTriangularArray; kwargs...) -> Any
-

Returns the inverse Laplace operator ∇⁻² applied to input alms. Acts on the unit sphere, i.e. it omits radius^2 scaling unless radius keyword argument is provided.

source
+

InverseLaplace operator ∇⁻² applied to input alms, using precomputed eigenvalues from S. Acts on the unit sphere, i.e. it omits radius^2 scaling unless radius keyword argument is provided.

source
SpeedyWeather.SpeedyTransforms.∇⁻²Method
∇⁻²(∇²alms::LowerTriangularArray; kwargs...) -> Any
+

Returns the inverse Laplace operator ∇⁻² applied to input alms. Acts on the unit sphere, i.e. it omits radius^2 scaling unless radius keyword argument is provided.

source
diff --git a/previews/PR645/stochastic_physics/index.html b/previews/PR645/stochastic_physics/index.html index a86e534df..1dc581d91 100644 --- a/previews/PR645/stochastic_physics/index.html +++ b/previews/PR645/stochastic_physics/index.html @@ -32,4 +32,4 @@ # surface humidity, kg/kg -> g/kg humid = simulation.diagnostic_variables.grid.humid_grid[:, k]*1000 -heatmap(humid, title="Surface humidity [g/kg], with SPPT, other seed", colormap=:oslo)

Surface humidity with SPPT

+heatmap(humid, title="Surface humidity [g/kg], with SPPT, other seed", colormap=:oslo)

Surface humidity with SPPT

diff --git a/previews/PR645/structure/index.html b/previews/PR645/structure/index.html index f1975001f..a6cf9e16e 100644 --- a/previews/PR645/structure/index.html +++ b/previews/PR645/structure/index.html @@ -1243,4 +1243,4 @@ tree(simulation, max_level=1, with_size=true)
Simulation{PrimitiveWetModel} (66.00 MB)
 ├┐prognostic_variables (5.48 MB)
 ├┐diagnostic_variables (36.42 MB)
-└┐model (24.10 MB)
+└┐model (24.10 MB) diff --git a/previews/PR645/surface_fluxes/index.html b/previews/PR645/surface_fluxes/index.html index 0c33dafc6..c77dc228c 100644 --- a/previews/PR645/surface_fluxes/index.html +++ b/previews/PR645/surface_fluxes/index.html @@ -19,4 +19,4 @@ F_u^\uparrow &= - \rho_s C V_0 u_s \\ F_v^\uparrow &= - \rho_s C V_0 v_s \end{aligned}\]

with $\rho_s = \frac{p_s}{R_d T_N}$ the surface air density calculated from surface pressure $p_s$ and lowermost layer temperature $T_N$. Better would be to extrapolate $T_N$ to $T_s$ a surface air temperature assuming adiabatic descent but that is currently not implemented.

Surface heat fluxes

The surface heat flux is proportional to the difference of the surface air temperature $T_s$ and the land or ocean skin temperature $T_{skin}$. We currently just approximate as $T_N$ the lowermost layer temperature although an adiabatic descent from pressure $p_N$ to surface pressure $p_s$ would be more accurate. We also use land and sea surface temperature to approximate $T_{skin}$ although future improvements should account for faster radiative effects on $T_{skin}$ compared to sea and land surface temperatures determined by a higher heat capacity of the relevant land surface layer or the mixed layer in the ocean. We then compute

\[F_T^\uparrow = \rho_s C V_0 c_p (T_{skin} - T_s)\]

Surface evaporation

The surface moisture flux, i.e. evaporation of soil moisture over land and evaporation of sea water over the ocean is proportional to the difference of the surface specific humidity $q_s$ and the saturation specific humidity given $T_{skin}$ and surface pressure $p_s$. This assumes that a very thin layer of air just above the ocean is saturated but over land this assumption is less well justified as it should be a function of the soil moisture and how much of that is available to evaporate given vegetation. We again make the simplification that $q_s = q_N$, i.e. specific humidity of the surface is the same as in the lowermost atmospheric layer above.

The surface evaporative flux is then (always positive)

\[F_q^\uparrow = \rho_s C V_0 \max(0, \alpha_{sw} q^* - q_s)\]

with $q^*$ the saturation specific humidity calculated from the skin temperature $T_{skin}$ and surface pressure $p_s$. The available of soil water over land is represented by (over the ocean $\alpha_{sw} = 1$)

\[\alpha_{sw} = \frac{D_{top} W_{top} + f_{veg} D_{root} \max(0, W_{root} - W_{wil})}{ - D_{top}W_{cap} + D_{root}(W_{cap} - W_{wil})}\]

following the Fortran SPEEDY documentation[SPEEDY] which follows Viterbo and Beljiars 1995 [Viterbo95]. The variables (or spatially prescribed arrays) are water content in the top soil layer $W_{top}$ and the root layer below $W_{root}$ using the vegetation fraction $f_{veg} = veg_{high} + 0.8 veg_{low}$ composed of a (dimensionless) high and low vegetation cover per grid cell $veg_{high}, veg_{low}$. The constants are depth of top soil layer $D_{top} = 7~cm$, depth of root layer $D_{root} = 21~cm$, soil wetness at field capacity (volume fraction) $W_{cap} = 0.3$, and soil wetness at wilting point (volume fraction) $W_{wil} = 0.17$.

Land-sea mask

SpeedyWeather uses a fractional land-sea mask, i.e. for every grid-point

  • 1 indicates land
  • 0 indicates ocean
  • a value in between indicates a grid-cell partially covered by ocean and land

The land-sea mask determines solely how to weight the surface fluxes coming from land or from the ocean. For the sensible heat fluxes this uses land and sea surface temperatures and weights the respective fluxes proportional to the fractional mask. Similar for evaporation. You can therefore define an ocean on top of a mountain, or a land without heat fluxes when the land-surface temperature is not defined, i.e. NaN. Let $F_L, F_S$ be the fluxes coming from land and sea, respectively. Then the land-sea mask $a \in [0,1]$ weights them as follows for the total flux $F$

\[F = aF_L + (1-a)F_S\]

but $F=F_L$ if the sea flux is NaN (because the ocean temperature is not defined) and $F=F_S$ if the land flux is NaN (because the land temperature or soil moisture is not defined, for sensible heat fluxes or evaporation), and $F=0$ if both fluxes are NaN.

Setting the land-sea mask to ocean therefore will disable any fluxes that may come from land, and vice versa. However, with an ocean-everywhere land-sea mask you must also define sea surface temperatures everywhere, otherwise the fluxes in those regions will be zero.

For more details see The land-sea mask implementation section.

References

+ D_{top}W_{cap} + D_{root}(W_{cap} - W_{wil})}\]

following the Fortran SPEEDY documentation[SPEEDY] which follows Viterbo and Beljiars 1995 [Viterbo95]. The variables (or spatially prescribed arrays) are water content in the top soil layer $W_{top}$ and the root layer below $W_{root}$ using the vegetation fraction $f_{veg} = veg_{high} + 0.8 veg_{low}$ composed of a (dimensionless) high and low vegetation cover per grid cell $veg_{high}, veg_{low}$. The constants are depth of top soil layer $D_{top} = 7~cm$, depth of root layer $D_{root} = 21~cm$, soil wetness at field capacity (volume fraction) $W_{cap} = 0.3$, and soil wetness at wilting point (volume fraction) $W_{wil} = 0.17$.

Land-sea mask

SpeedyWeather uses a fractional land-sea mask, i.e. for every grid-point

  • 1 indicates land
  • 0 indicates ocean
  • a value in between indicates a grid-cell partially covered by ocean and land

The land-sea mask determines solely how to weight the surface fluxes coming from land or from the ocean. For the sensible heat fluxes this uses land and sea surface temperatures and weights the respective fluxes proportional to the fractional mask. Similar for evaporation. You can therefore define an ocean on top of a mountain, or a land without heat fluxes when the land-surface temperature is not defined, i.e. NaN. Let $F_L, F_S$ be the fluxes coming from land and sea, respectively. Then the land-sea mask $a \in [0,1]$ weights them as follows for the total flux $F$

\[F = aF_L + (1-a)F_S\]

but $F=F_L$ if the sea flux is NaN (because the ocean temperature is not defined) and $F=F_S$ if the land flux is NaN (because the land temperature or soil moisture is not defined, for sensible heat fluxes or evaporation), and $F=0$ if both fluxes are NaN.

Setting the land-sea mask to ocean therefore will disable any fluxes that may come from land, and vice versa. However, with an ocean-everywhere land-sea mask you must also define sea surface temperatures everywhere, otherwise the fluxes in those regions will be zero.

For more details see The land-sea mask implementation section.

References

diff --git a/previews/PR645/vertical_diffusion/index.html b/previews/PR645/vertical_diffusion/index.html index deeee0764..96946255d 100644 --- a/previews/PR645/vertical_diffusion/index.html +++ b/previews/PR645/vertical_diffusion/index.html @@ -21,4 +21,4 @@ \kappa u_N \sqrt{C}z \left( 1 + \frac{Ri}{Ri_c}\frac{\log(z/z_0)}{1 - Ri/Ri_c}\right)^{-1} \quad &\text{for} \quad Ri_N > 0 \\ \end{cases}\]

$C$ is the surface drag coefficient as computed in Bulk Richardson-based drag coefficient. The subscript $N$ denotes the lowermost model layer $k=N$.

As for the Bulk Richardson-based drag coefficient we also simplify this calculation here by approximating $\log(z/z_0) \approx \log(Z/z_0)$ with the height Z of the lowermost layer given resolution and a reference surface temperature, for more details see description in that section.

Vertical diffusion tendencies

The vertical diffusion is then computed as a tendency for $u, v, q$ and temperature $T$ via the dry static energy $SE = c_p T + gz$, i.e.

\[\frac{\partial T}{\partial t} = \frac{1}{c_p}\frac{\partial}{\partial \sigma} K -\frac{\partial SE}{\partial \sigma} \]

where we just fold the heat capacity $c_p$ into the diffusion coefficient $K \to K/c_p$. The other variables are diffused straight-forwardly as $\partial_t u = \partial_\sigma K \partial_\sigma u$, etc.

References

  • Frierson2006Frierson, D. M. W., I. M. Held, and P. Zurita-Gotor, 2006: A Gray-Radiation Aquaplanet Moist GCM. Part I: Static Stability and Eddy Scale. J. Atmos. Sci., 63, 2548-2566. DOI: 10.1175/JAS3753.1.
+\frac{\partial SE}{\partial \sigma} \]

where we just fold the heat capacity $c_p$ into the diffusion coefficient $K \to K/c_p$. The other variables are diffused straight-forwardly as $\partial_t u = \partial_\sigma K \partial_\sigma u$, etc.

References

  • Frierson2006Frierson, D. M. W., I. M. Held, and P. Zurita-Gotor, 2006: A Gray-Radiation Aquaplanet Moist GCM. Part I: Static Stability and Eddy Scale. J. Atmos. Sci., 63, 2548-2566. DOI: 10.1175/JAS3753.1.