---
title: "Analyze a grid of Td and Ed values"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Analyze a grid of Td and Ed values}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.width = 12, fig.height = 8
)
## modern_r <- getRversion() >= "4.1.0"
pth <- withr::local_tempdir(pattern = "snvecR")
withr::local_options(list(snvecR.cachedir = pth))
```
```{r setup}
library(tibble) # nice dataframes
library(dplyr) # mutate/select/filter/glimpse
library(purrr) # pmap
library(tidyr) # unnest
library(ggplot2) # nice plots
library(snvecR) # this package
```
NOTE: If you get complaints that `|>` is unrecognized, please update your R
version to something later than "4.1.0". If you don not want to/cannot do that,
you can also `install.packages("magrittr")` and replace each instance of `|>`
with `%>%`.
# Introduction
The function `snvec()` uses some of the parameters of a full astronomical
solution (AS) such as ZB18a from Zeebe and Lourens (2019) in combination with
values for tidal dissipation (Td) and dynamical ellipticity
(Ed) to calculate precession and obliquity (or tilt).
In this vignette we show how we would go about using `snvec()` for a range of
input values.
# Create a grid of Td and Ed
We create a grid of input values for Td and Ed. The
values in the grid are based on Zeebe and Lourens (2022) table 2.
```{r make-grid}
biggrid <- as_tibble(
expand.grid(Td = c(0, 0.5, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2),
Ed = c(1.000, 0.998, 1.005, 1.012)))
# that's 32 rows
biggrid
```
We now add columns for important parameter values that do not vary between
experiments, so that it is very clear from the output what the inputs were.
```{r update-grid}
biggrid <- biggrid |>
# for now only for 1000 years at very high tolerance so it's fast
mutate(atol = 1e-4, tend = -1e3)
# this would be the real deal, the full 100--0 Myr results at medium
# tolerance.
## mutate(tol = 1e-7, tend = -1e5)
```
# Calculate a new list-column with the results
We're going to use `purrr::pmap()` here, which allows you to apply a function
given a list of input values. We use it here to create a list-column with the
results.
This is some advanced R stuff, so feel free to read up on it if you want in
Wickham et al., (2023). In particular, see [the chapter on
iteration](https://r4ds.had.co.nz/iteration.html#mapping-over-multiple-arguments)
for the basics, and [many models](https://r4ds.had.co.nz/many-models.html) for
the full approach.
If we would apply the `snvec()` function here directly for the full 100 Myr, it
would quickly make R run out of memory, because it would be storing all the
timesteps for those 32 experiments. Instead, we write a wrapper function that
only stores the latest N timesteps.
```{r snvec-tail}
snvec_tail <- function(..., n = 100) {
# do the fit with the parameters in ...
snvec(...) |>
# save only the last n values, that's where the differences are greatest
tail(n = n)
}
```
Compute obliquity and precession for each parameter combination, and save it in
the new list-column `sol`.
```{r massive-compute}
biggrid <- biggrid |>
# apply our new function!
mutate(sol = pmap(list(td = Td, ed = Ed, tend = tend, atol = atol),
.f = snvec_tail,
# additional parameters to snvec_tail can go after!
quiet = TRUE, output = "nice", n = 100,
# I would strongly recommend against increasing the
# resolution too much, but for speed/illustration we
# prefer to do it here
tres = -5,
# interactively this makes a nice progress bar
.progress = "snvec on a grid")) #|>
# normally we would save the results to file, because these take quite a
# long time to calculate and we don't want to accidentally delete them.
## write_rds("out/2023-04-05_biggrid.rds")
```
This would be how I would read in my old results (about 9MB on-disk for the
final 1000 timesteps in the full 100 Myr simulations).
```{r read-old, eval=FALSE}
biggrid <- readr::read_rds("out/2023-04-05_biggrid.rds")
```
# Inspect results
Let's look at the structure of the output:
```{r check}
glimpse(biggrid)
```
We can see the list column `sol` in this result! But we'd like to access the
raw output, so we use `unnest()`.
```{r unnest}
expanded <- biggrid |>
unnest(sol)
expanded
```
Let's make a figure of these final values.
```{r plot}
expanded |>
ggplot(aes(x = time, y = cp,
colour = factor(Td),
linetype = factor(Ed))) +
labs(x = "Time (kyr)",
y = "Climatic precession",
colour = "Tidal dissipation",
linetype = "Dynamical ellipticity") +
# make panels of plots
facet_grid(rows = vars(Td)) +
geom_line() +
# add eccentricity
geom_line(aes(y = ee),
linetype = "solid",
colour = "black",
data = get_solution() |>
filter(time > -1000) |>
filter(time < -500))
```
Now the analysis can begin!
# Alternatively: save the results for each row to file instead
If we care about the full outputs of each of the simulations, the above
approach will likely make you run out of memory (crash R). One way to deal with
this is to write each simulation to a file. We create a filename from the
variables and then use `purrr::pwalk()` in stead of `pmap()`.
This could look like this:
```{r add-filenames}
biggrid <- biggrid |>
# get rid of sol column
select(-sol) |>
# add a filename that's easy to break into relevant parameters later
# I write to tempdir here, but you might want to write to something like out/
mutate(file = glue::glue("{tempdir()}/2023-04-13_biggrid_{Td}_{Ed}_{atol}_{tend}.rds"))
biggrid
```
We write a new wrapper function that includes a file argument to save the
outputs.
```{r snvec-save}
snvec_save <- function(..., file) {
snvec(...) |>
readr::write_rds(file)
cli::cli_inform(
"Wrote file {.file {file}}."
)
}
```
Calculate each time series' obliquity and precession and save to file.
```{r run-pwalk}
biggrid |>
# in this case we make sure that column names are identical to argument names
# so that the list (in this case tibble/data.frame) is matched to the correct
# arguments
rename(td = Td, ed = Ed, atol = atol) |>
purrr::pwalk(.f = snvec_save,
# additional parameters can go after!
quiet = TRUE, output = "nice", tres = -5,
# show progress bar
.progress = "snvec to file")
```
This would lead to the creation of one file per row, which you can read in
individually or for a certain subset using `readr::read_rds()`.
Below we do it for all the files and make a plot!
```{r read-files}
biggrid |> # limit to a few experiments
## slice(c(1, 15, 32)) |>
# in this case that's not necessary because we limited it to a very
# low-resolution (tres) and short time period (tend)
# read them in to list-column
mutate(fullsol = map(file, readr::read_rds)) |>
# unfold the list column
unnest(fullsol) |>
# plot the obliquity
ggplot(aes(x = time, y = epl,
colour = factor(Td),
linetype = factor(Ed))) +
labs(x = "Time (kyr)", y = "Obliquity",
colour = "Tidal dissipation",
linetype = "Dynamical ellipticity") +
## facet_grid(rows = vars(Td)) +
geom_line()
```
# References
Zeebe, R. E., & Lourens, L. J. (2019). Solar System chaos and the
Paleocene–Eocene boundary age constrained by geology and astronomy.
_Science_, 365(6456), 926–929.
[doi:10.1126/science.aax0612](https://doi.org/10.1126/science.aax0612).
Zeebe, R. E., & Lourens, L. J. (2022). A deep-time dating tool for
paleo-applications utilizing obliquity and precession cycles: The role of
dynamical ellipticity and tidal dissipation. _Paleoceanography and
Paleoclimatology_, e2021PA004349.
[doi:10.1029/2021PA004349](https://doi.org/10.1029/2021PA004349).
Zeebe, R. E. (2022). Reduced Variations in Earth’s and Mars’ Orbital
Inclination and Earth’s Obliquity from 58 to 48 Myr ago due to Solar System
Chaos. _The Astronomical Journal_, 164(3), 107.
[doi:10.3847/1538-3881/ac80f8](https://doi.org/10.3847/1538-3881/ac80f8).
Wikipedia page on Orbital Elements:
Wickham, H., Çetinkaya-Rundel, M., & Grolemund, G. (2023). R for Data Science:
Import, Tidy, Transform, Visualize, and Model Data (2nd edition). O’Reilly
Media.