tag_download_wind(tag)
5 Trajectory with wind
In this second chapter of the advanced tutorial, we will see how to model the trajectory of the Great Reed Warbler using wind data.
Wind can significantly influence a bird’s movement, explaining up to 50% of the displacement! Accounting for wind allows to estimate the airspeed of each transition rather than groundspeed. As such, the movement model can be defined as the probability of a bird’s airspeed, which is much more constrained and precise. This approach is presented in detail in section 2.2.4 of Nussbaumer et al. (2023).
5.1 Download wind data
Wind data is available at high resolution (1hr, 0.25°, 37 pressure level) on ERA5 hourly data on pressure levels (Copernicus Climate Change Service 2018). This data is easily accessible through the ecmwfr
package.
The first step is to create a ECMWF account on www.ecmwf.int. Once you have an account, you can log in on cds.climate.copernicus.eu and retrieve your personal access token which should be saved as environment variables in your .Renviron
using usethis::edit_r_environ()
.
cds_token = "{Personal Access Token}"
As the flights tend to be of short duration, we suggest downloading a file for each flight. This can be done automatically with tag_download_wind()
, which uses wf_request_batch()
to make all the requests in parallel.
You can monitor the requests at [https://cds.climate.copernicus.eu/requests]. The files will be downloaded in data/wind/
In case you have a lot of tracks for which you need to download wind data and don’t want to block your console, you might consider using an RStudio background job, which can be easily called with the job package:
job::job({
tag_download_wind(tag)
})
5.2 Create graph
Similar to the example of the Swainson’s Warbler in the basic tutorial, we first need to create the trellis graph:
graph <- graph_create(tag, quiet = TRUE)
5.3 Add wind to graph
We then compute the average windspeed experienced by the bird for each edge of the graph. This process can be quite long as we need to interpolate the position of the bird along its flight on a 4D grid (latitude-longitude-pressure level-time).
We then compute the airspeed based on this windspeed and the known groundspeed. All of these are stored as complex values with the real part representing the E-W component and the imaginary part corresponding to the N-S component.
graph <- graph_add_wind(graph, pressure = tag$pressure, quiet = TRUE)
5.4 Define movement model
While you can still define the movement model with a parametric function (i.e., gamma or logit), we find it more intuitive to use the mechanical power curve. The power curve expresses the energy required for a bird to fly at a certain airspeed based on aerodynamic theory. See more details in section 2.2.5 of Nussbaumer et al. (2023).
First, we search for morphological information on the Great Reed Warbler using the AVONET database (Tobias et al. 2022).
bird <- bird_create("Acrocephalus arundinaceus")
Using the bird created, we can set the movement model by converting the airspeed to power, and power to a probability. This second step is still a parametric equation, which can be manually defined with power2prob
.
graph <- graph_set_movement(graph,
method = "power",
bird = bird,
power2prob = \(power) (1 / power)^3,
low_speed_fix = 15
)
plot_graph_movement(graph)
5.5 Products
We can then compute the same three products as for the Swainson’s Warbler:
path_most_likely <- graph_most_likely(graph, quiet = TRUE)
marginal <- graph_marginal(graph, quiet = TRUE)
path_simulation <- graph_simulation(graph, nj = 10, quiet = TRUE)
plot(marginal, path = path_most_likely)
plot_path(path_simulation, plot_leaflet = FALSE)
5.5.1 Extract flight information
The path
variable contains all the information at the scale of the stationary period. However, to get flight information, you need to extract variable of the edge of the graph. path2edge()
is the function for that!
edge_simulation <- path2edge(path_simulation, graph)
edge_most_likely <- path2edge(path_most_likely, graph)
knitr::kable(head(edge_most_likely, 3), digits = 1)
stap_s | stap_t | j | s | t | lat_s | lat_t | lon_s | lon_t | start | end | duration | n | distance | bearing | gs | ws | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 2 | 1 | 26405 | 57010 | 48.9 | 47.6 | 17.0 | 16.4 | 2017-08-04 19:47:30 | 2017-08-04 23:17:30 | 3.5 | 1 | 150.4 | -160.3 | -14.2-34.8i | 13.8-13.6i |
12 | 2 | 3 | 1 | 57010 | 86621 | 47.6 | 44.9 | 16.4 | 14.4 | 2017-08-05 19:27:30 | 2017-08-06 02:52:30 | 7.4 | 1 | 342.4 | -152.5 | -20.2-38.7i | -5.5-4.6i |
22 | 3 | 4 | 1 | 86621 | 118434 | 44.9 | 41.6 | 14.4 | 15.1 | 2017-08-06 19:12:30 | 2017-08-07 03:17:30 | 8.1 | 1 | 366.2 | 170.2 | 7.3-42.3i | -7.8-20.6i |
edge_simulation$name <- glue::glue("{edge_simulation$stap_s}->{edge_simulation$stap_t}")
plot1 <- ggplot(edge_simulation, aes(reorder(name, stap_s), abs(gs))) +
theme_bw() +
ylab("gs") +
geom_boxplot()
plot2 <- ggplot(edge_simulation, aes(reorder(name, stap_s), abs(ws))) +
theme_bw() +
ylab("ws") +
geom_boxplot()
plot3 <- ggplot(edge_simulation, aes(reorder(name, stap_s), abs(gs - ws))) +
theme_bw() +
ylab("as") +
geom_boxplot()
plot4 <- ggplot(edge_simulation, aes(reorder(name, stap_s), duration)) +
theme_bw() +
ylab("duration") +
geom_point()
plotly::subplot(
plotly::ggplotly(plot1),
plotly::ggplotly(plot2),
plotly::ggplotly(plot3),
plotly::ggplotly(plot4),
nrows = 4, titleY = TRUE
)
It’s a good idea to check the distribution of ground speed (gs), winspeed (ws) and airspeed (as) and check for any outliers which might come from error in the labeling. Here you can see the high groundspeed (>100km/h between stap 24
and 25
) which is nicely explained by wind, as the corresponding airspeed is perfectly normal (~50km/h)
We can (and should) also check that our movement model is coherent with the distribution of flight speed assumed in the movement model:
plot_graph_movement(graph) +
geom_histogram(
data = data.frame(as = abs(edge_simulation$gs - edge_simulation$ws)),
aes(x = as, y = after_stat(count) / sum(after_stat(count))),
color = "black", fill = NA, bins = 100
)
If you find anomalous flight speed, it might be worth checking if this/these flight(s) have been correctly labelled.
5.6 Save
graph
can become extremely big for such models and it might not be recommended to save it. Check its size with format(object.size(graph), units = "MB")
.
save(
tag,
graph,
path_most_likely,
path_simulation,
marginal,
edge_simulation,
edge_most_likely,
file = "./data/interim/18LX.RData"
)