How to filter out extra-tropical variabilty to highlight the structure of the Semiannual Oscillation using just multiple linear regression.
Some time ago, with a Journal Club I’m part of (shout-out to Go stratospheric!), we read “Representation of the equatorial stratopause semiannual oscillation in global atmospheric reanalyses” (Kawatani et al. 2020), a paper about the Semiannual Oscillation (SAO). One figure (Figure 1) piqued our interest. The SAO is supposed to be a characteristic of the tropical stratosphere and be… you know… semiannual. But in Figure 1, the more sticking feature is extra-tropical and with an annual cycle.
knitr::include_graphics(file.path("img", "sao-jet-fig8.png"))
We quickly realised that there’s more going on there than the SAO. Is there a way to filter out the rest of the variability and only look at the SAO-related variabiltiy?
As always, first thing first. Download the data and replicate the figure. Kawatani et al. (2020) use data from many many reanalyses, but I’m only going to use data from ERA5.
library(magrittr)
library(data.table)
library(ggplot2)
library(metR)
theme_set(theme_minimal(base_size = 10) + theme(panel.grid = element_blank()))
# This WILL take long. Go make yourself a cut of tea or brew some mate.
era5_file <- here::here("_data", "era5-u.nc")
if (!file.exists(era5_file)) {
request <- list(
format = "netcdf",
product_type = "monthly_averaged_reanalysis",
variable = "u_component_of_wind",
pressure_level = c("1", "2", "3", "5", "7", "10", "20", "30"),
year = c("1979", "1980", "1981", "1982", "1983", "1984", "1985", "1986", "1987", "1988", "1989", "1990", "1991", "1992", "1993", "1994", "1995", "1996", "1997", "1998", "1999", "2000", "2001", "2002", "2003", "2004", "2005", "2006", "2007", "2008", "2009", "2010", "2011", "2012", "2013", "2014", "2015", "2016", "2017", "2018", "2019"),
month = c("01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"),
time = "00:00",
grid = c("2.5", "2.5"), # we don't need high resolution
dataset_short_name = "reanalysis-era5-pressure-levels-monthly-means",
target = basename(era5_file)
)
# Need to set up user with
# ecmwfr::wf_set_key()
ecmwfr::wf_request(request, path = dirname(era5_file))
}
era5 <- ReadNetCDF(era5_file)
era5 %>%
.[level == 1] %>%
.[, .(u = mean(u)), by = .(latitude, month(time), level)] %>%
ggplot(aes(month, latitude)) +
geom_contour_fill(aes(z = u, fill = ..level..),
breaks = AnchorBreaks(0, binwidth = 15, exclude = 0)) +
geom_contour_tanaka(aes(z = u),
breaks = AnchorBreaks(0, binwidth = 15, exclude = 0)) +
scale_y_latitude() +
scale_x_continuous(breaks = 1:12, labels = month.abb, expand = c(0, 0)) +
scale_fill_divergent_discretised("U") +
facet_wrap(~level, labeller = labeller(level = function(x) paste0(x, " hPa")))
Figure 2 replicated Figure 1 extending the latitude range to the rest of the globe. It becomes even clearer that the main variability is in the extratropics.
Now, the SAO not only is supposed to be in the tropics, but it’s also restricted to levels higher than ~3 hPa. Figure 3 shows what’s going on at levels bewteen 1 hPa and 30 hPa.
The variability in the extratopics is there in the lower levels. So I though, why don’t try to remove that lower-level signal? After looking at Figure 3 and trying around, I settled into representing the lower levels with 5 hPa and 20 hPa.
So, at each gridpoint I fit the model
\[ U_{1hPa} = \alpha U_{5hPa} + \beta U_{20hPa} + \epsilon_{1hPa} \]
Where \(U\) is the zonal mean zonal wind at each level and \(\epsilon_{1hPa}\) is the residual wind that I’m interested in. That is, the variability of zonal mean zonal wind at 1 hPa that is not (linearly) explained by the variability of the zonal mean zonal wind at the lower levels. For those paying attention, this is (I think) essentially removing the equivalent barotropic component in the wind.
The seasonal cycle of the residual zonal mean zonal wind in Figure 4 shows that the filtering really works! Now it really looks like the equatorial phenomenon that it’s supposed to be, and the semiannual cycle is plainly for all to see. 🥳.
filtered %>%
.[, .(u = mean(u_resid)), by = .(month(time), latitude)] %>%
ggplot(aes(month, latitude)) +
geom_contour_fill(aes(z = u, fill = ..level..),
breaks = AnchorBreaks(0, binwidth = 10, exclude = 0)) +
geom_contour_tanaka(aes(z = u),
breaks = AnchorBreaks(0, binwidth = 10, exclude = 0)) +
scale_y_latitude() +
scale_x_continuous(breaks = 1:12, labels = month.abb, expand = c(0, 0)) +
scale_fill_divergent_discretised("U")
And for completitude, Figure 5 shows timeseries of zonal mean zonal wind at the equator, and averaged polarward of 30°. In the equator, the residual zonal wind is virtually identical to the unfiltered zonal wind.
filtered %>%
.[, zone := fcase(latitude > 30, "north of 30°N",
latitude < -30, "south of 30°S",
latitude == 0, "equator",
default = NA)] %>%
na.omit() %>%
.[, .(u = mean(`1`),
u_resid = mean(u_resid)), by = .(time, zone)] %>%
melt(id.vars = c("time", "zone")) %>%
ggplot(aes(time, value)) +
geom_line(aes(color = variable)) +
scale_color_brewer(NULL, palette = "Dark2", labels = c(u = "U",
u_resid = "Residual U")) +
scale_y_continuous(NULL) +
facet_wrap(zone~., ncol = 1)
So the conclusion, I think, is that the meridional structure was a function of the polar jets, and that looking at the SAO by looking at zonal mean zonal wind in the equator is fine.
Click on this button to get the code that generated this document:
View all changes to this article since it was first published. If you see mistakes or want to suggest changes, please create an issue on the source repository.
For attribution, please cite this work as
Campitelli (2021, Jan. 15). The Scrapbook: The meridional structure of the Semiannual Oscillation. Retrieved from http://eliocamp.github.io/scrapbook/posts/2021-01-15-sao-and-jet/
BibTeX citation
@misc{sao-jet, author = {Campitelli, Elio}, title = {The Scrapbook: The meridional structure of the Semiannual Oscillation}, url = {http://eliocamp.github.io/scrapbook/posts/2021-01-15-sao-and-jet/}, year = {2021} }