Calculating Global Coherence as a function of Glacial/Interglacial cycles#
This notebook lays out the details of how we use Pyleoclim to calculate global coherence between insolation and oxygen isotopes from a subset of records as a function of glacial/interglacial cycles.
The notebook is structured as follows:
Define a function that will be used to calculate correlations between records that contain large hiatuses
Define insolation curves using climlab
Define boundaries of interglacial/glacial periods use those defined in LR04
Break the Sanbao record into glacial/interglacial components and create two series object, one for each.
Analyze global coherence between insolation and the glacial/interglacial Sanbao series objects.
Repeat steps 4 and 5 for the oxygen isotope data from Buckeye Creek
Repeat steps 4 and 5 for oxygen isotope data from a marine sediment core in the Bay of Bengal
# Importing the necessary libraries
import pickle
import pyleoclim as pyleo
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from climlab.solar.orbital import OrbitalTable
from climlab.solar.insolation import daily_insolation
from pylipd.lipd import LiPD
# Importing the data
with open("../../data/geo_ms_composite_dict.pkl", "rb") as handle:
geo_ms_composite_dict = pickle.load(handle)
with open("../../data/cmap_grouped.pkl", "rb") as handle:
cmap = pickle.load(handle)
with open("../../data/correlated_latitude.pkl", "rb") as handle:
correlated_latitude = pickle.load(handle)
Creating integrated summer insolation curve at 30N (picked arbitrarily):
# Creating insolation object
# array with specified kyears (can be plain numpy or xarray.DataArray)
years = np.arange(-1000, 1)
# subset of orbital parameters for specified time
orb = OrbitalTable.interp(kyear=years)
# Day numbers from June 1st to August 31st
jja_days = np.arange(152, 243)
lat = 30
days = jja_days
inso = daily_insolation(lat=lat, day=days, orb=orb).mean(dim="day")
inso_series = pyleo.Series(
time=0 - years[::-1],
value=inso[::-1],
time_name="Age",
time_unit="Kyr BP",
value_name=f"JJA Insolation {lat} N",
value_unit="W/m^2",
verbose=False,
)
Building MIS boundaries:
# Building the MIS boundaries
MIS_df = pd.read_table(
"https://lorraine-lisiecki.com/LR04_MISboundaries.txt",
skiprows=1,
header=0,
delim_whitespace=True,
nrows=25,
index_col="Boundary",
)
interglacial_to_glacial = [
"1/2",
"5/6",
"7/8",
"9/10",
"11/12",
"13/14",
"15/16",
"17/18",
"19/20",
]
glacial_to_interglacial = [
"4/5",
"6/7",
"8/9",
"10/11",
"12/13",
"14/15",
"16/17",
"18/19",
]
glacial_timing = [
(
MIS_df.loc[interglacial_to_glacial[idx]]["Age(ka)"],
MIS_df.loc[glacial_to_interglacial[idx]]["Age(ka)"],
)
for idx in range(len(glacial_to_interglacial))
]
interglacial_timing = [
(glacial_timing[idx - 1][1], glacial_timing[idx][0])
for idx in range(1, len(glacial_to_interglacial))
]
interglacial_timing.insert(0, (0, glacial_timing[0][0]))
Sanbao Analysis#
Creating glacial/interglacial series objects using MIS boundaries:
# Creating interglacial series objects
series = geo_ms_composite_dict["Sanbao.China.2016"].convert_time_unit(
"kyrs BP"
) # .interp()
value = []
time = []
for interval in interglacial_timing:
series_interval = series.slice(interval)
if len(series_interval.time) > 1:
value.extend(series_interval.value)
time.extend(series_interval.time)
interglacial_series = series.copy()
interglacial_series.time = time
interglacial_series.value = value
# Creating glacial series objects
value = []
time = []
for interval in glacial_timing:
series_interval = series.slice(interval)
if len(series_interval.time) > 1:
value.extend(series_interval.value)
time.extend(series_interval.time)
glacial_series = series.copy()
glacial_series.time = time
glacial_series.value = value
Calculating global coherence and plotting:
# Calculating coherence and plotting the data
fig = plt.figure(figsize=(16, 4))
gs = fig.add_gridspec(nrows=1, ncols=3, wspace=0.5)
ax = fig.add_subplot(gs[0, 0:2])
ts = geo_ms_composite_dict["Sanbao.China.2016"]
ts.value_name = r"$\delta^{18}O$"
ts.value_unit = "‰"
ts_plot = ts.interp().filter(cutoff_scale=6)
max_age = max(ts.time)
min_age = min(ts.time)
glacial_intervals = []
interglacial_intervals = []
for glacial_end, glacial_start in glacial_timing:
if min_age < glacial_end < max_age or min_age < glacial_start < max_age:
glacial_intervals.append(
[max(min_age, glacial_end), min(max_age, glacial_start)]
)
for interglacial_end, interglacial_start in interglacial_timing:
if min_age < interglacial_end < max_age or min_age < interglacial_start < max_age:
interglacial_intervals.append(
[max(min_age, interglacial_end), min(max_age, interglacial_start)]
)
ts_plot.plot(ax=ax, color=cmap[ts.label], legend=False)
ax.invert_xaxis()
for glacial_interval in glacial_intervals:
ax.axvspan(glacial_interval[0], glacial_interval[1], color="b", alpha=0.1)
for interglacial_interval in interglacial_intervals:
ax.axvspan(interglacial_interval[0], interglacial_interval[1], color="r", alpha=0.1)
ax_twin = ax.twinx()
ax_twin.grid(False)
inso_series.slice((min(ts.time), max(ts.time))).plot(ax=ax_twin, color="grey")
ax2 = fig.add_subplot(gs[0, 2])
glacial_coh_real = glacial_series.global_coherence(inso_series)
glacial_coh_real.plot(
ax=ax2,
spec1_plot_kwargs={"color": cmap[ts.label], "linestyle": "dotted"},
spec2_plot_kwargs={"color": "grey", "linestyle": "dotted"},
coh_line_color="blue",
fill_color="blue",
legend=False,
)
interglacial_coh_real = interglacial_series.global_coherence(inso_series)
interglacial_coh_real.plot(
ax=ax2,
spec1_plot_kwargs={"color": cmap[ts.label], "linestyle": "dashed"},
spec2_plot_kwargs={"color": "grey", "linestyle": "dashed"},
coh_line_color="red",
fill_color="red",
legend=False,
)
<Axes: xlabel='Period [kyr]', ylabel='PSD'>

Buckeye Creek#
# Loading smoothed plotting dict, necessary here
with open("../../data/smooth_plotting_series_dict.pkl", "rb") as handle:
smoothed_plotting_series = pickle.load(handle)
Creating glacial/interglacial series for Buckeye Creek oxygen isotope records:
# Creating the interglacial series
series = geo_ms_composite_dict["BuckeyeCreek.WestVirginia.2019"].convert_time_unit(
"kyrs BP"
) # .interp()
value = []
time = []
for interval in interglacial_timing:
series_interval = series.slice(interval)
if len(series_interval.time) > 1:
value.extend(series_interval.value)
time.extend(series_interval.time)
interglacial_series = series.copy()
interglacial_series.time = time
interglacial_series.value = value
# Creating the glacial series
value = []
time = []
for interval in glacial_timing:
series_interval = series.slice(interval)
if len(series_interval.time) > 1:
value.extend(series_interval.value)
time.extend(series_interval.time)
glacial_series = series.copy()
glacial_series.time = time
glacial_series.value = value
Calculating global coherence and plotting:
# Calculating coherence and plotting the data
fig = plt.figure(figsize=(16, 4))
gs = fig.add_gridspec(nrows=1, ncols=3, wspace=0.5)
ax = fig.add_subplot(gs[0, 0:2])
ts = geo_ms_composite_dict["BuckeyeCreek.WestVirginia.2019"].convert_time_unit(
"kyrs BP"
)
ts_plot = smoothed_plotting_series["BuckeyeCreek.WestVirginia.2019"].convert_time_unit(
"kyrs BP"
)
ts.value_name = r"$\delta^{18}O$"
ts.value_unit = "‰"
max_age = max(ts.time)
min_age = min(ts.time)
glacial_intervals = []
interglacial_intervals = []
for glacial_end, glacial_start in glacial_timing:
if min_age < glacial_end < max_age or min_age < glacial_start < max_age:
glacial_intervals.append(
[max(min_age, glacial_end), min(max_age, glacial_start)]
)
for interglacial_end, interglacial_start in interglacial_timing:
if min_age < interglacial_end < max_age or min_age < interglacial_start < max_age:
interglacial_intervals.append(
[max(min_age, interglacial_end), min(max_age, interglacial_start)]
)
ts_plot.plot(ax=ax, color=cmap[ts.label], legend=False)
ax.invert_xaxis()
ax.set_xlabel("Age [kyr BP]")
for glacial_interval in glacial_intervals:
ax.axvspan(glacial_interval[0], glacial_interval[1], color="b", alpha=0.1)
for interglacial_interval in interglacial_intervals:
ax.axvspan(interglacial_interval[0], interglacial_interval[1], color="r", alpha=0.1)
ax_twin = ax.twinx()
ax_twin.grid(False)
inso_series.slice((min(ts.time), max(ts.time))).plot(ax=ax_twin, color="grey")
ax2 = fig.add_subplot(gs[0, 2])
glacial_coh_real = glacial_series.global_coherence(inso_series)
glacial_coh_real.plot(
ax=ax2,
spec1_plot_kwargs={"color": cmap[ts.label], "linestyle": "dotted"},
spec2_plot_kwargs={"color": "grey", "linestyle": "dotted"},
coh_line_color="blue",
fill_color="blue",
legend=False,
)
interglacial_coh_real = interglacial_series.global_coherence(inso_series)
interglacial_coh_real.plot(
ax=ax2,
spec1_plot_kwargs={"color": cmap[ts.label], "linestyle": "dashed"},
spec2_plot_kwargs={"color": "grey", "linestyle": "dashed"},
coh_line_color="red",
fill_color="red",
legend=False,
)
<Axes: xlabel='Period [kyr]', ylabel='PSD'>

U1446#
Loading U1446 from LiPD, as we haven’t used this record before:
# Loading the U1446 marine sediment data
lipd_path = "../../data/marine_sediments/U1446.IndianOcean.2021.lpd"
L = LiPD()
if __name__ == "__main__":
L.load(lipd_path)
Loading 1 LiPD files
0%| | 0/1 [00:00<?, ?it/s]
100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 35.80it/s]
Loaded..
Creating a series object from the reconstructed oxygen isotope composition of seawater (we use the TEX86 reconstruction, see the original publication for details):
# Loading the U1446 marine sediment data
ms_dict = {}
for _, row in L.get_timeseries_essentials().iterrows():
time = row["time_values"]
time_name = row["time_variableName"]
time_unit = row["time_units"]
value = row["paleoData_values"]
value_name = row["paleoData_variableName"]
value_unit = row["paleoData_units"]
lat = row["geo_meanLat"]
lon = row["geo_meanLon"]
if value_name == "d18O SW TEX86":
series = pyleo.GeoSeries(
time=time,
value=value,
time_name=time_name,
time_unit=time_unit,
value_name=value_name,
value_unit=value_unit,
lat=lat,
lon=lon,
archiveType="Marine Sediment",
verbose=False,
).interp()
sw_series = series.slice([0, 800])
break
Creating glacial and interglacial series from the U1446 series:
# Creating the interglacial series
series = sw_series
value = []
time = []
for interval in interglacial_timing:
series_interval = series.slice(interval)
if len(series_interval.time) > 1:
value.extend(series_interval.value)
time.extend(series_interval.time)
interglacial_series = series.copy()
interglacial_series.time = time
interglacial_series.value = value
# Creating the glacial series
value = []
time = []
for interval in glacial_timing:
series_interval = series.slice(interval)
if len(series_interval.time) > 1:
value.extend(series_interval.value)
time.extend(series_interval.time)
glacial_series = series.copy()
glacial_series.time = time
glacial_series.value = value
Calculating global coherence and plotting:
# Calculating coherence and plotting the data
fig = plt.figure(figsize=(16, 4))
gs = fig.add_gridspec(nrows=1, ncols=3, wspace=0.5)
ax = fig.add_subplot(gs[0, 0:2])
ts = series
ts_plot = series.filter(cutoff_scale=5)
ts.value_name = r"$\delta^{18}O$"
ts.value_unit = "‰"
max_age = max(ts.time)
min_age = min(ts.time)
glacial_intervals = []
interglacial_intervals = []
for glacial_end, glacial_start in glacial_timing:
if min_age < glacial_end < max_age or min_age < glacial_start < max_age:
glacial_intervals.append(
[max(min_age, glacial_end), min(max_age, glacial_start)]
)
for interglacial_end, interglacial_start in interglacial_timing:
if min_age < interglacial_end < max_age or min_age < interglacial_start < max_age:
interglacial_intervals.append(
[max(min_age, interglacial_end), min(max_age, interglacial_start)]
)
ts_plot.plot(ax=ax, color="brown", legend=False)
ax.invert_xaxis()
for glacial_interval in glacial_intervals:
ax.axvspan(glacial_interval[0], glacial_interval[1], color="b", alpha=0.1)
for interglacial_interval in interglacial_intervals:
ax.axvspan(interglacial_interval[0], interglacial_interval[1], color="r", alpha=0.1)
ax_twin = ax.twinx()
ax_twin.grid(False)
inso_series.slice((min(ts.time), max(ts.time))).plot(ax=ax_twin, color="grey")
ax2 = fig.add_subplot(gs[0, 2])
glacial_coh_real = glacial_series.global_coherence(inso_series)
glacial_coh_real.plot(
ax=ax2,
spec1_plot_kwargs={"color": "brown", "linestyle": "dotted"},
spec2_plot_kwargs={"color": "grey", "linestyle": "dotted"},
coh_line_color="blue",
fill_color="blue",
legend=False,
)
interglacial_coh_real = interglacial_series.global_coherence(inso_series)
interglacial_coh_real.plot(
ax=ax2,
spec1_plot_kwargs={"color": "brown", "linestyle": "dashed"},
spec2_plot_kwargs={"color": "grey", "linestyle": "dashed"},
coh_line_color="red",
fill_color="red",
legend=False,
)
ax.text(
-0.2,
0.4,
"U1446",
transform=ax.transAxes,
ha="center",
fontsize=24,
fontweight="bold",
)
ax2.set_xlim(500, 0)
ax2.set_xticks(
[500, 200, 100, 50, 20, 10, 5, 2],
)
[<matplotlib.axis.XTick at 0x339729790>,
<matplotlib.axis.XTick at 0x33e0cc410>,
<matplotlib.axis.XTick at 0x3397e0590>,
<matplotlib.axis.XTick at 0x3397e1a50>,
<matplotlib.axis.XTick at 0x3397e3890>,
<matplotlib.axis.XTick at 0x3397e5e90>,
<matplotlib.axis.XTick at 0x339773dd0>,
<matplotlib.axis.XTick at 0x339a55810>]
